diff --git a/.dockerignore b/.dockerignore index 763aeda1be..e142afd073 100644 --- a/.dockerignore +++ b/.dockerignore @@ -22,10 +22,21 @@ brotli/buildfiles/**/* nitro-testnode/**/* # Arbitrator ignores +arbitrator/tools/module_roots +arbitrator/tools/pricer # Rust outputs arbitrator/target/**/* arbitrator/target +arbitrator/stylus/tests/*/target/ +arbitrator/wasm-testsuite/target/ +arbitrator/wasm-libraries/target/ +arbitrator/tools/wasmer/target/ +arbitrator/tools/wasm-tools/ +arbitrator/tools/pricers/ +arbitrator/tools/module_roots/ +arbitrator/langs/rust/target/ +arbitrator/langs/bf/target/ # Compiled files **/*.o diff --git a/.github/workflows/arbitrator-ci.yml b/.github/workflows/arbitrator-ci.yml index 54a948e04a..b2713bdfde 100644 --- a/.github/workflows/arbitrator-ci.yml +++ b/.github/workflows/arbitrator-ci.yml @@ -3,6 +3,12 @@ run-name: Arbitrator CI triggered from @${{ github.actor }} of ${{ github.head_r on: workflow_dispatch: + inputs: + enable_tmate: + type: boolean + description: 'Enable tmate' + required: false + default: false merge_group: pull_request: paths: @@ -16,7 +22,7 @@ on: env: RUST_BACKTRACE: 1 - RUSTFLAGS: -Dwarnings +# RUSTFLAGS: -Dwarnings # TODO: re-enable after wasmer upgrade WABT_VERSION: 1.0.32 jobs: @@ -24,6 +30,12 @@ jobs: name: Run Arbitrator tests runs-on: ubuntu-8 steps: + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + if: ${{ github.event_name == 'workflow_dispatch' && inputs.enable_tmate }} + with: + detached: true + - name: Checkout uses: actions/checkout@v4 with: @@ -38,7 +50,7 @@ jobs: - name: Install go uses: actions/setup-go@v4 with: - go-version: 1.20.x + go-version: 1.21.x - name: Install custom go-ethereum run: | @@ -59,8 +71,16 @@ jobs: - name: Install rust stable uses: dtolnay/rust-toolchain@stable with: + toolchain: "1.76" components: 'llvm-tools-preview, rustfmt, clippy' + + - name: Install rust nightly + uses: dtolnay/rust-toolchain@nightly + id: install-rust-nightly + with: + toolchain: "nightly-2024-02-04" targets: 'wasm32-wasi, wasm32-unknown-unknown' + components: 'rust-src, rustfmt, clippy' - name: Cache Rust intermediate build products uses: actions/cache@v3 @@ -136,16 +156,28 @@ jobs: run: echo "$HOME/wabt-prefix/bin" >> "$GITHUB_PATH" - name: Make arbitrator libraries - run: make -j wasm-ci-build + run: make -j wasm-ci-build STYLUS_NIGHTLY_VER="+nightly-2024-02-04" - name: Clippy check run: cargo clippy --all --manifest-path arbitrator/Cargo.toml -- -D warnings - name: Run rust tests - run: cargo test --all --manifest-path arbitrator/Cargo.toml + uses: actions-rs/cargo@v1 + with: + command: test + args: -p arbutil -p prover -p jit -p stylus --release --manifest-path arbitrator/prover/Cargo.toml - name: Rustfmt - run: cargo fmt --all --manifest-path arbitrator/Cargo.toml -- --check + uses: actions-rs/cargo@v1 + with: + command: fmt + args: -p arbutil -p prover -p jit -p stylus --manifest-path arbitrator/Cargo.toml -- --check + + - name: Rustfmt - langs/rust + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all --manifest-path arbitrator/langs/rust/Cargo.toml -- --check - name: Make proofs from test cases run: make -j test-gen-proofs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f9591b222..4d97b5bfd5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - test-mode: [defaults, race, challenge] + test-mode: [defaults, race, challenge, stylus, long] steps: - name: Checkout @@ -46,7 +46,7 @@ jobs: - name: Install go uses: actions/setup-go@v4 with: - go-version: 1.20.x + go-version: 1.21.x - name: Install wasm-ld run: | @@ -61,6 +61,21 @@ jobs: - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 + - name: Install rust nightly + uses: actions-rs/toolchain@v1 + id: install-rust-nightly + with: + profile: minimal + toolchain: "nightly" + + - name: Install rust wasm targets + run: rustup target add wasm32-wasi wasm32-unknown-unknown + + - name: Install nightly wasm targets + run: | + rustup component add rust-src --toolchain nightly + rustup target add wasm32-unknown-unknown --toolchain nightly + - name: Cache Build Products uses: actions/cache@v3 with: @@ -130,13 +145,13 @@ jobs: if: matrix.test-mode == 'defaults' run: | packages=`go list ./...` - gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 -- -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -timeout 20m + stdbuf -oL gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 --no-color=false -- ./... -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -timeout 20m -parallel=8 > >(stdbuf -oL tee full.log | grep -vE "INFO|seal") - name: run tests with race detection if: matrix.test-mode == 'race' - run: | + run: | packages=`go list ./...` - gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 -- -race -timeout 30m + stdbuf -oL gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 --no-color=false -- ./... -race -timeout 30m -parallel=8 > >(stdbuf -oL tee full.log | grep -vE "INFO|seal") - name: run redis tests if: matrix.test-mode == 'defaults' @@ -144,9 +159,27 @@ jobs: - name: run challenge tests if: matrix.test-mode == 'challenge' - run: | + run: | packages=`go list ./...` - gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 -- ./... -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -tags=challengetest -run=TestChallenge + stdbuf -oL gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 --no-color=false -- ./... -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -parallel=8 -tags=challengetest -run=TestChallenge > >(stdbuf -oL tee full.log | grep -vE "INFO|seal") + + - name: run stylus tests + if: matrix.test-mode == 'stylus' + run: | + packages=`go list ./...` + stdbuf -oL gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 --no-color=false -- ./... -timeout 60m -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -parallel=8 -tags=stylustest -run="TestProgramArbitrator" > >(stdbuf -oL tee full.log | grep -vE "INFO|seal") + + - name: run long stylus tests + if: matrix.test-mode == 'long' + run: | + packages=`go list ./...` + stdbuf -oL gotestsum --format short-verbose --packages="$packages" --rerun-fails=1 --no-color=false -- ./... -timeout 60m -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -parallel=8 -tags=stylustest -run="TestProgramLong" > >(stdbuf -oL tee full.log | grep -vE "INFO|seal") + + - name: Archive detailed run log + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.test-mode }}-full.log + path: full.log - name: Upload coverage to Codecov uses: codecov/codecov-action@v2 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8b7ebd0e15..acaa97895d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -47,6 +47,9 @@ jobs: with: submodules: true + - name: Install dependencies + run: sudo apt update && sudo apt install -y wabt + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 @@ -70,7 +73,7 @@ jobs: - name: Install go uses: actions/setup-go@v4 with: - go-version: 1.20.x + go-version: 1.21.x - name: Install rust stable uses: dtolnay/rust-toolchain@stable diff --git a/.github/workflows/merge-checks.yml b/.github/workflows/merge-checks.yml new file mode 100644 index 0000000000..6f291bbb22 --- /dev/null +++ b/.github/workflows/merge-checks.yml @@ -0,0 +1,20 @@ +name: Merge Checks + +on: + pull_request: + branches: [ master ] + types: [synchronize, opened, reopened, labeled, unlabeled] + +jobs: + design-approved-check: + if: ${{ !contains(github.event.*.labels.*.name, 'design-approved') }} + name: Design Approved Check + runs-on: ubuntu-latest + steps: + - name: Check for design-approved label + run: | + echo "Pull request is missing the 'design-approved' label" + echo "This workflow fails so that the pull request cannot be merged" + exit 1 + + diff --git a/.gitignore b/.gitignore index 8a628e29c4..b94d61e746 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,6 @@ target/ yarn-error.log local/ system_tests/test-data/* +.configs/ system_tests/testdata/* arbos/testdata/* diff --git a/.gitmodules b/.gitmodules index e12156f5db..075c986df6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,6 +17,18 @@ [submodule "arbitrator/wasm-testsuite/testsuite"] path = arbitrator/wasm-testsuite/testsuite url = https://github.com/WebAssembly/testsuite.git +[submodule "arbitrator/tools/wasmer"] + path = arbitrator/tools/wasmer + url = https://github.com/OffchainLabs/wasmer.git [submodule "nitro-testnode"] path = nitro-testnode url = https://github.com/OffchainLabs/nitro-testnode.git +[submodule "arbitrator/langs/rust"] + path = arbitrator/langs/rust + url = https://github.com/OffchainLabs/stylus-sdk-rs.git +[submodule "arbitrator/langs/c"] + path = arbitrator/langs/c + url = https://github.com/OffchainLabs/stylus-sdk-c.git +[submodule "arbitrator/langs/bf"] + path = arbitrator/langs/bf + url = https://github.com/OffchainLabs/stylus-sdk-bf.git diff --git a/Dockerfile b/Dockerfile index 26bf0c3d7d..b80db426cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -41,21 +41,30 @@ 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:14.0-55.7~deb12u1 lld=1:14.0-55.7~deb12u1 - # pinned rust 1.70.0 -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.70.0 --target x86_64-unknown-linux-gnu wasm32-unknown-unknown wasm32-wasi +RUN apt-get update && \ + apt-get install -y clang=1:14.0-55.7~deb12u1 lld=1:14.0-55.7~deb12u1 wabt + # pinned rust 1.75.0 +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.75.0 --target x86_64-unknown-linux-gnu wasm32-unknown-unknown wasm32-wasi COPY ./Makefile ./ +COPY arbitrator/Cargo.* arbitrator/ COPY arbitrator/arbutil arbitrator/arbutil +COPY arbitrator/brotli arbitrator/brotli +COPY arbitrator/caller-env arbitrator/caller-env +COPY arbitrator/prover arbitrator/prover COPY arbitrator/wasm-libraries arbitrator/wasm-libraries +COPY arbitrator/tools/wasmer arbitrator/tools/wasmer +COPY brotli brotli +COPY scripts/build-brotli.sh scripts/ COPY --from=brotli-wasm-export / target/ +RUN apt-get update && apt-get install -y cmake 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 - +# pinned go version +RUN curl -L https://golang.org/dl/go1.21.10.linux-`dpkg --print-architecture`.tar.gz | tar -C /usr/local -xzf - COPY ./Makefile ./go.mod ./go.sum ./ COPY ./arbcompress ./arbcompress COPY ./arbos ./arbos @@ -82,17 +91,26 @@ COPY --from=contracts-builder workspace/contracts/node_modules/@offchainlabs/upg 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.70-slim-bookworm as prover-header-builder +FROM rust:1.75-slim-bookworm as prover-header-builder WORKDIR /workspace RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ - apt-get install -y make clang && \ + apt-get install -y make clang wabt && \ cargo install --force cbindgen -COPY arbitrator/Cargo.* arbitrator/cbindgen.toml arbitrator/ +COPY arbitrator/Cargo.* arbitrator/ COPY ./Makefile ./ COPY arbitrator/arbutil arbitrator/arbutil +COPY arbitrator/brotli arbitrator/brotli +COPY arbitrator/caller-env arbitrator/caller-env COPY arbitrator/prover arbitrator/prover +COPY arbitrator/wasm-libraries arbitrator/wasm-libraries COPY arbitrator/jit arbitrator/jit +COPY arbitrator/stylus arbitrator/stylus +COPY arbitrator/tools/wasmer arbitrator/tools/wasmer +COPY --from=brotli-wasm-export / target/ +COPY scripts/build-brotli.sh scripts/ +COPY brotli brotli +RUN apt-get update && apt-get install -y cmake RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-prover-header FROM scratch as prover-header-export @@ -102,25 +120,41 @@ FROM rust:1.75-slim-bookworm 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++-11-dev wabt clang llvm-dev libclang-common-14-dev libpolly-14-dev + apt-get install -y make wget gpg software-properties-common zlib1g-dev libstdc++-12-dev wabt +RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \ + add-apt-repository 'deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm-15 main' && \ + apt-get update && \ + apt-get install -y llvm-15-dev libclang-common-15-dev +COPY --from=brotli-library-export / target/ COPY arbitrator/Cargo.* arbitrator/ COPY arbitrator/arbutil arbitrator/arbutil +COPY arbitrator/brotli arbitrator/brotli +COPY arbitrator/caller-env arbitrator/caller-env 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 && \ +COPY arbitrator/stylus/Cargo.toml arbitrator/stylus/ +COPY arbitrator/tools/wasmer arbitrator/tools/wasmer +COPY arbitrator/wasm-libraries/user-host-trait/Cargo.toml arbitrator/wasm-libraries/user-host-trait/Cargo.toml +RUN bash -c 'mkdir arbitrator/{prover,jit,stylus}/src arbitrator/wasm-libraries/user-host-trait/src' +RUN echo "fn test() {}" > arbitrator/jit/src/lib.rs && \ echo "fn test() {}" > arbitrator/prover/src/lib.rs && \ + echo "fn test() {}" > arbitrator/stylus/src/lib.rs && \ + echo "fn test() {}" > arbitrator/wasm-libraries/user-host-trait/src/lib.rs && \ cargo build --manifest-path arbitrator/Cargo.toml --release --lib && \ - rm arbitrator/jit/src/lib.rs + rm arbitrator/prover/src/lib.rs arbitrator/jit/src/lib.rs arbitrator/stylus/src/lib.rs && \ + rm arbitrator/wasm-libraries/user-host-trait/src/lib.rs COPY ./Makefile ./ COPY arbitrator/prover arbitrator/prover +COPY arbitrator/wasm-libraries arbitrator/wasm-libraries COPY arbitrator/jit arbitrator/jit -COPY --from=brotli-library-export / target/ +COPY arbitrator/stylus arbitrator/stylus +COPY --from=brotli-wasm-export / target/ +COPY scripts/build-brotli.sh scripts/ +COPY brotli brotli 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 +RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-jit FROM scratch as prover-export COPY --from=prover-builder /workspace/target/ / @@ -134,7 +168,12 @@ 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/prover/ arbitrator/prover/ +COPY --from=wasm-libs-builder /workspace/arbitrator/tools/wasmer/ arbitrator/tools/wasmer/ COPY --from=wasm-libs-builder /workspace/arbitrator/wasm-libraries/ arbitrator/wasm-libraries/ +COPY --from=wasm-libs-builder /workspace/arbitrator/arbutil arbitrator/arbutil +COPY --from=wasm-libs-builder /workspace/arbitrator/brotli arbitrator/brotli +COPY --from=wasm-libs-builder /workspace/arbitrator/caller-env arbitrator/caller-env COPY --from=wasm-libs-builder /workspace/.make/ .make/ COPY ./Makefile ./ COPY ./arbitrator ./arbitrator @@ -158,15 +197,16 @@ COPY ./scripts/download-machine.sh . #RUN ./download-machine.sh consensus-v7 0x53dd4b9a3d807a8cbb4d58fbfc6a0857c3846d46956848cae0a1cc7eca2bb5a8 #RUN ./download-machine.sh consensus-v7.1 0x2b20e1490d1b06299b222f3239b0ae07e750d8f3b4dedd19f500a815c1548bbc #RUN ./download-machine.sh consensus-v9 0xd1842bfbe047322b3f3b3635b5fe62eb611557784d17ac1d2b1ce9c170af6544 -RUN ./download-machine.sh consensus-v10 0x6b94a7fc388fd8ef3def759297828dc311761e88d8179c7ee8d3887dc554f3c3 -RUN ./download-machine.sh consensus-v10.1 0xda4e3ad5e7feacb817c21c8d0220da7650fe9051ece68a3f0b1c5d38bbb27b21 -RUN ./download-machine.sh consensus-v10.2 0x0754e09320c381566cc0449904c377a52bd34a6b9404432e80afd573b67f7b17 -RUN ./download-machine.sh consensus-v10.3 0xf559b6d4fa869472dabce70fe1c15221bdda837533dfd891916836975b434dec -RUN ./download-machine.sh consensus-v11 0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a -RUN ./download-machine.sh consensus-v11.1 0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4 -RUN ./download-machine.sh consensus-v20 0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4 +#RUN ./download-machine.sh consensus-v10 0x6b94a7fc388fd8ef3def759297828dc311761e88d8179c7ee8d3887dc554f3c3 +#RUN ./download-machine.sh consensus-v10.1 0xda4e3ad5e7feacb817c21c8d0220da7650fe9051ece68a3f0b1c5d38bbb27b21 +#RUN ./download-machine.sh consensus-v10.2 0x0754e09320c381566cc0449904c377a52bd34a6b9404432e80afd573b67f7b17 +#RUN ./download-machine.sh consensus-v10.3 0xf559b6d4fa869472dabce70fe1c15221bdda837533dfd891916836975b434dec +#RUN ./download-machine.sh consensus-v11 0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a +#RUN ./download-machine.sh consensus-v11.1 0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4 +#RUN ./download-machine.sh consensus-v20 0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4 +RUN ./download-machine.sh consensus-v30-rc.2 0xb0de9cb89e4d944ae6023a3b62276e54804c242fd8c4c2d8e6cc4450f5fa8b1b -FROM golang:1.20-bookworm as node-builder +FROM golang:1.21.10-bookworm as node-builder WORKDIR /workspace ARG version="" ARG datetime="" @@ -230,11 +270,15 @@ USER user WORKDIR /home/user/ ENTRYPOINT [ "/usr/local/bin/nitro" ] +FROM offchainlabs/nitro-node:v2.3.4-rc.5-b4cc111 as nitro-legacy + 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/ +COPY --from=nitro-legacy /home/user/target/machines /home/user/nitro-legacy/machines +RUN rm -rf /workspace/target/legacy-machines/latest RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y \ @@ -244,10 +288,23 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ 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 +ENTRYPOINT [ "/usr/local/bin/nitro" , "--validation.wasm.allowed-wasm-module-roots", "/home/user/nitro-legacy/machines,/home/user/target/machines"] USER user -FROM nitro-node as nitro-node-dev +FROM nitro-node as nitro-node-validator +USER root +COPY --from=nitro-legacy /usr/local/bin/nitro-val /home/user/nitro-legacy/bin/nitro-val +COPY --from=nitro-legacy /usr/local/bin/jit /home/user/nitro-legacy/bin/jit +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -y xxd netcat-traditional && \ + 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/* +COPY scripts/split-val-entry.sh /usr/local/bin +ENTRYPOINT [ "/usr/local/bin/split-val-entry.sh" ] +USER user + +FROM nitro-node-validator as nitro-node-dev USER root # Copy in latest WASM module root RUN rm -f /home/user/target/machines/latest diff --git a/Makefile b/Makefile index f01effc41c..f4523f9d0c 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ -# Copyright 2021-2022, Offchain Labs, Inc. -# For license information, see https://github.com/nitro/blob/master/LICENSE +# Copyright 2021-2024, Offchain Labs, Inc. +# For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE # Docker builds mess up file timestamps. Then again, in docker builds we never # have to update an existing file. So - for docker, convert all dependencies @@ -28,31 +28,33 @@ ifneq ($(origin NITRO_MODIFIED),undefined) endif ifneq ($(origin GOLANG_LDFLAGS),undefined) - GOLANG_PARAMS = -ldflags="$(GOLANG_LDFLAGS)" + GOLANG_PARAMS = -ldflags="-extldflags '-ldl' $(GOLANG_LDFLAGS)" endif precompile_names = AddressTable Aggregator BLS Debug FunctionTable GasInfo Info osTest Owner RetryableTx Statistics Sys precompiles = $(patsubst %,./solgen/generated/%.go, $(precompile_names)) output_root=target +output_latest=$(output_root)/machines/latest -repo_dirs = arbos arbnode arbstate cmd precompiles solgen system_tests util validator wavmio -go_source = $(wildcard $(patsubst %,%/*.go, $(repo_dirs)) $(patsubst %,%/*/*.go, $(repo_dirs))) +repo_dirs = arbos arbcompress arbnode arbutil arbstate cmd das precompiles solgen system_tests util validator wavmio +go_source.go = $(wildcard $(patsubst %,%/*.go, $(repo_dirs)) $(patsubst %,%/*/*.go, $(repo_dirs))) +go_source.s = $(wildcard $(patsubst %,%/*.s, $(repo_dirs)) $(patsubst %,%/*/*.s, $(repo_dirs))) +go_source = $(go_source.go) $(go_source.s) color_pink = "\e[38;5;161;1m" color_reset = "\e[0;0m" done = "%bdone!%b\n" $(color_pink) $(color_reset) -replay_deps=arbos wavmio arbstate arbcompress solgen/go/node-interfacegen blsSignatures cmd/replay +replay_wasm=$(output_latest)/replay.wasm -replay_wasm=$(output_root)/machines/latest/replay.wasm +arb_brotli_files = $(wildcard arbitrator/brotli/src/*.* arbitrator/brotli/src/*/*.* arbitrator/brotli/*.toml arbitrator/brotli/*.rs) .make/cbrotli-lib .make/cbrotli-wasm arbitrator_generated_header=$(output_root)/include/arbitrator.h -arbitrator_wasm_libs_nogo=$(output_root)/machines/latest/wasi_stub.wasm $(output_root)/machines/latest/host_io.wasm $(output_root)/machines/latest/soft-float.wasm -arbitrator_wasm_libs=$(arbitrator_wasm_libs_nogo) $(patsubst %,$(output_root)/machines/latest/%.wasm, go_stub brotli) -arbitrator_prover_lib=$(output_root)/lib/libprover.a -arbitrator_prover_bin=$(output_root)/bin/prover +arbitrator_wasm_libs=$(patsubst %, $(output_root)/machines/latest/%.wasm, forward wasi_stub host_io soft-float arbcompress user_host program_exec) +arbitrator_stylus_lib=$(output_root)/lib/libstylus.a +prover_bin=$(output_root)/bin/prover arbitrator_jit=$(output_root)/bin/jit arbitrator_cases=arbitrator/prover/test-cases @@ -60,24 +62,87 @@ arbitrator_cases=arbitrator/prover/test-cases arbitrator_tests_wat=$(wildcard $(arbitrator_cases)/*.wat) arbitrator_tests_rust=$(wildcard $(arbitrator_cases)/rust/src/bin/*.rs) -arbitrator_test_wasms=$(patsubst %.wat,%.wasm, $(arbitrator_tests_wat)) $(patsubst $(arbitrator_cases)/rust/src/bin/%.rs,$(arbitrator_cases)/rust/target/wasm32-wasi/release/%.wasm, $(arbitrator_tests_rust)) $(arbitrator_cases)/go/main +arbitrator_test_wasms=$(patsubst %.wat,%.wasm, $(arbitrator_tests_wat)) $(patsubst $(arbitrator_cases)/rust/src/bin/%.rs,$(arbitrator_cases)/rust/target/wasm32-wasi/release/%.wasm, $(arbitrator_tests_rust)) $(arbitrator_cases)/go/testcase.wasm + +arbitrator_tests_link_info = $(shell cat $(arbitrator_cases)/link.txt | xargs) +arbitrator_tests_link_deps = $(patsubst %,$(arbitrator_cases)/%.wasm, $(arbitrator_tests_link_info)) + +arbitrator_tests_forward_wats = $(wildcard $(arbitrator_cases)/forward/*.wat) +arbitrator_tests_forward_deps = $(arbitrator_tests_forward_wats:wat=wasm) WASI_SYSROOT?=/opt/wasi-sdk/wasi-sysroot -arbitrator_wasm_lib_flags_nogo=$(patsubst %, -l %, $(arbitrator_wasm_libs_nogo)) arbitrator_wasm_lib_flags=$(patsubst %, -l %, $(arbitrator_wasm_libs)) -rust_arbutil_files = $(wildcard arbitrator/arbutil/src/*.* arbitrator/arbutil/*.toml) +rust_arbutil_files = $(wildcard arbitrator/arbutil/src/*.* arbitrator/arbutil/src/*/*.* arbitrator/arbutil/*.toml arbitrator/caller-env/src/*.* arbitrator/caller-env/src/*/*.* arbitrator/caller-env/*.toml) .make/cbrotli-lib -prover_src = arbitrator/prover/src -rust_prover_files = $(wildcard $(prover_src)/*.* $(prover_src)/*/*.* arbitrator/prover/*.toml) $(rust_arbutil_files) +prover_direct_includes = $(patsubst %,$(output_latest)/%.wasm, forward forward_stub) +prover_dir = arbitrator/prover/ +rust_prover_files = $(wildcard $(prover_dir)/src/*.* $(prover_dir)/src/*/*.* $(prover_dir)/*.toml $(prover_dir)/*.rs) $(rust_arbutil_files) $(prover_direct_includes) $(arb_brotli_files) -jit_dir = arbitrator/jit -jit_files = $(wildcard $(jit_dir)/*.toml $(jit_dir)/*.rs $(jit_dir)/src/*.rs) $(rust_arbutil_files) +wasm_lib = arbitrator/wasm-libraries +wasm_lib_deps = $(wildcard $(wasm_lib)/$(1)/*.toml $(wasm_lib)/$(1)/src/*.rs $(wasm_lib)/$(1)/*.rs) $(rust_arbutil_files) $(arb_brotli_files) .make/machines +wasm_lib_go_abi = $(call wasm_lib_deps,go-abi) +wasm_lib_forward = $(call wasm_lib_deps,forward) +wasm_lib_user_host_trait = $(call wasm_lib_deps,user-host-trait) +wasm_lib_user_host = $(call wasm_lib_deps,user-host) $(wasm_lib_user_host_trait) + +forward_dir = $(wasm_lib)/forward + +stylus_files = $(wildcard $(stylus_dir)/*.toml $(stylus_dir)/src/*.rs) $(wasm_lib_user_host_trait) $(rust_prover_files) -arbitrator_wasm_wasistub_files = $(wildcard arbitrator/wasm-libraries/wasi-stub/src/*/*) -arbitrator_wasm_gostub_files = $(wildcard arbitrator/wasm-libraries/go-stub/src/*/*) -arbitrator_wasm_hostio_files = $(wildcard arbitrator/wasm-libraries/host-io/src/*/*) +jit_dir = arbitrator/jit +jit_files = $(wildcard $(jit_dir)/*.toml $(jit_dir)/*.rs $(jit_dir)/src/*.rs $(jit_dir)/src/*/*.rs) $(stylus_files) + +wasm32_wasi = target/wasm32-wasi/release +wasm32_unknown = target/wasm32-unknown-unknown/release + +stylus_dir = arbitrator/stylus +stylus_test_dir = arbitrator/stylus/tests +stylus_cargo = arbitrator/stylus/tests/.cargo/config.toml + +rust_sdk = arbitrator/langs/rust +c_sdk = arbitrator/langs/c +stylus_lang_rust = $(wildcard $(rust_sdk)/*/src/*.rs $(rust_sdk)/*/src/*/*.rs $(rust_sdk)/*/*.toml) +stylus_lang_c = $(wildcard $(c_sdk)/*/*.c $(c_sdk)/*/*.h) +stylus_lang_bf = $(wildcard arbitrator/langs/bf/src/*.* arbitrator/langs/bf/src/*.toml) + +STYLUS_NIGHTLY_VER ?= "+nightly" + +cargo_nightly = cargo $(STYLUS_NIGHTLY_VER) build -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort + +get_stylus_test_wasm = $(stylus_test_dir)/$(1)/$(wasm32_unknown)/$(1).wasm +get_stylus_test_rust = $(wildcard $(stylus_test_dir)/$(1)/*.toml $(stylus_test_dir)/$(1)/src/*.rs) $(stylus_cargo) $(stylus_lang_rust) +get_stylus_test_c = $(wildcard $(c_sdk)/examples/$(1)/*.c $(c_sdk)/examples/$(1)/*.h) $(stylus_lang_c) +stylus_test_bfs = $(wildcard $(stylus_test_dir)/bf/*.b) + +stylus_test_keccak_wasm = $(call get_stylus_test_wasm,keccak) +stylus_test_keccak_src = $(call get_stylus_test_rust,keccak) +stylus_test_keccak-100_wasm = $(call get_stylus_test_wasm,keccak-100) +stylus_test_keccak-100_src = $(call get_stylus_test_rust,keccak-100) +stylus_test_fallible_wasm = $(call get_stylus_test_wasm,fallible) +stylus_test_fallible_src = $(call get_stylus_test_rust,fallible) +stylus_test_storage_wasm = $(call get_stylus_test_wasm,storage) +stylus_test_storage_src = $(call get_stylus_test_rust,storage) +stylus_test_multicall_wasm = $(call get_stylus_test_wasm,multicall) +stylus_test_multicall_src = $(call get_stylus_test_rust,multicall) +stylus_test_log_wasm = $(call get_stylus_test_wasm,log) +stylus_test_log_src = $(call get_stylus_test_rust,log) +stylus_test_create_wasm = $(call get_stylus_test_wasm,create) +stylus_test_create_src = $(call get_stylus_test_rust,create) +stylus_test_math_wasm = $(call get_stylus_test_wasm,math) +stylus_test_math_src = $(call get_stylus_test_rust,math) +stylus_test_evm-data_wasm = $(call get_stylus_test_wasm,evm-data) +stylus_test_evm-data_src = $(call get_stylus_test_rust,evm-data) +stylus_test_sdk-storage_wasm = $(call get_stylus_test_wasm,sdk-storage) +stylus_test_sdk-storage_src = $(call get_stylus_test_rust,sdk-storage) +stylus_test_erc20_wasm = $(call get_stylus_test_wasm,erc20) +stylus_test_erc20_src = $(call get_stylus_test_rust,erc20) +stylus_test_read-return-data_wasm = $(call get_stylus_test_wasm,read-return-data) +stylus_test_read-return-data_src = $(call get_stylus_test_rust,read-return-data) + +stylus_test_wasms = $(stylus_test_keccak_wasm) $(stylus_test_keccak-100_wasm) $(stylus_test_fallible_wasm) $(stylus_test_storage_wasm) $(stylus_test_multicall_wasm) $(stylus_test_log_wasm) $(stylus_test_create_wasm) $(stylus_test_math_wasm) $(stylus_test_sdk-storage_wasm) $(stylus_test_erc20_wasm) $(stylus_test_read-return-data_wasm) $(stylus_test_evm-data_wasm) $(stylus_test_bfs:.b=.wasm) +stylus_benchmarks = $(wildcard $(stylus_dir)/*.toml $(stylus_dir)/src/*.rs) $(stylus_test_wasms) # user targets @@ -95,17 +160,19 @@ build-node-deps: $(go_source) build-prover-header build-prover-lib build-jit .ma test-go-deps: \ build-replay-env \ + $(stylus_test_wasms) \ + $(arbitrator_stylus_lib) \ $(patsubst %,$(arbitrator_cases)/%.wasm, global-state read-inboxmsg-10 global-state-wrapper const) build-prover-header: $(arbitrator_generated_header) -build-prover-lib: $(arbitrator_prover_lib) +build-prover-lib: $(arbitrator_stylus_lib) -build-prover-bin: $(arbitrator_prover_bin) +build-prover-bin: $(prover_bin) build-jit: $(arbitrator_jit) -build-replay-env: $(arbitrator_prover_bin) $(arbitrator_jit) $(arbitrator_wasm_libs) $(replay_wasm) $(output_root)/machines/latest/machine.wavm.br +build-replay-env: $(prover_bin) $(arbitrator_jit) $(arbitrator_wasm_libs) $(replay_wasm) $(output_latest)/machine.wavm.br build-wasm-libs: $(arbitrator_wasm_libs) @@ -122,6 +189,10 @@ format fmt: .make/fmt lint: .make/lint @printf $(done) +stylus-benchmarks: $(stylus_benchmarks) + cargo test --manifest-path $< --release --features benchmark benchmark_ -- --nocapture + @printf $(done) + test-go: .make/test-go @printf $(done) @@ -129,22 +200,27 @@ test-go-challenge: test-go-deps go test -v -timeout 120m ./system_tests/... -run TestChallenge -tags challengetest @printf $(done) +test-go-stylus: test-go-deps + go test -v -timeout 120m ./system_tests/... -run TestProgramArbitrator -tags stylustest + @printf $(done) + test-go-redis: test-go-deps TEST_REDIS=redis://localhost:6379/0 go test -p 1 -run TestRedis ./system_tests/... ./arbnode/... @printf $(done) test-gen-proofs: \ + $(arbitrator_test_wasms) \ $(patsubst $(arbitrator_cases)/%.wat,contracts/test/prover/proofs/%.json, $(arbitrator_tests_wat)) \ $(patsubst $(arbitrator_cases)/rust/src/bin/%.rs,contracts/test/prover/proofs/rust-%.json, $(arbitrator_tests_rust)) \ contracts/test/prover/proofs/go.json -wasm-ci-build: $(arbitrator_wasm_libs) $(arbitrator_test_wasms) +wasm-ci-build: $(arbitrator_wasm_libs) $(arbitrator_test_wasms) $(stylus_test_wasms) $(output_latest)/user_test.wasm @printf $(done) clean: go clean -testcache rm -rf $(arbitrator_cases)/rust/target - rm -f $(arbitrator_cases)/*.wasm $(arbitrator_cases)/go/main + rm -f $(arbitrator_cases)/*.wasm $(arbitrator_cases)/go/testcase.wasm rm -rf arbitrator/wasm-testsuite/tests rm -rf $(output_root) rm -f contracts/test/prover/proofs/*.json contracts/test/prover/spec-proofs/*.json @@ -154,6 +230,8 @@ clean: rm -f arbitrator/wasm-libraries/soft-float/*.o rm -f arbitrator/wasm-libraries/soft-float/SoftFloat/build/Wasm-Clang/*.o rm -f arbitrator/wasm-libraries/soft-float/SoftFloat/build/Wasm-Clang/*.a + rm -f arbitrator/wasm-libraries/forward/*.wat + rm -rf arbitrator/stylus/tests/*/target/ arbitrator/stylus/tests/*/*.wasm @rm -rf contracts/build contracts/cache solgen/go/ @rm -f .make/* @@ -188,39 +266,38 @@ $(output_root)/bin/seq-coordinator-manager: $(DEP_PREDICATE) build-node-deps # recompile wasm, but don't change timestamp unless files differ $(replay_wasm): $(DEP_PREDICATE) $(go_source) .make/solgen mkdir -p `dirname $(replay_wasm)` - GOOS=js GOARCH=wasm go build -o $(output_root)/tmp/replay.wasm ./cmd/replay/... - if ! diff -qN $(output_root)/tmp/replay.wasm $@ > /dev/null; then cp $(output_root)/tmp/replay.wasm $@; fi + GOOS=wasip1 GOARCH=wasm go build -o $@ ./cmd/replay/... -$(arbitrator_prover_bin): $(DEP_PREDICATE) $(rust_prover_files) - mkdir -p `dirname $(arbitrator_prover_bin)` +$(prover_bin): $(DEP_PREDICATE) $(rust_prover_files) + mkdir -p `dirname $(prover_bin)` cargo build --manifest-path arbitrator/Cargo.toml --release --bin prover ${CARGOFLAGS} install arbitrator/target/release/prover $@ -$(arbitrator_prover_lib): $(DEP_PREDICATE) $(rust_prover_files) - mkdir -p `dirname $(arbitrator_prover_lib)` - cargo build --manifest-path arbitrator/Cargo.toml --release --lib -p prover ${CARGOFLAGS} - install arbitrator/target/release/libprover.a $@ +$(arbitrator_stylus_lib): $(DEP_PREDICATE) $(stylus_files) + mkdir -p `dirname $(arbitrator_stylus_lib)` + cargo build --manifest-path arbitrator/Cargo.toml --release --lib -p stylus ${CARGOFLAGS} + install arbitrator/target/release/libstylus.a $@ -$(arbitrator_jit): $(DEP_PREDICATE) .make/cbrotli-lib $(jit_files) +$(arbitrator_jit): $(DEP_PREDICATE) $(jit_files) mkdir -p `dirname $(arbitrator_jit)` - cargo build --manifest-path arbitrator/Cargo.toml --release --bin jit ${CARGOFLAGS} + cargo build --manifest-path arbitrator/Cargo.toml --release -p jit ${CARGOFLAGS} install arbitrator/target/release/jit $@ -$(arbitrator_cases)/rust/target/wasm32-wasi/release/%.wasm: $(arbitrator_cases)/rust/src/bin/%.rs $(arbitrator_cases)/rust/src/lib.rs - cargo build --manifest-path $(arbitrator_cases)/rust/Cargo.toml --release --target wasm32-wasi --bin $(patsubst $(arbitrator_cases)/rust/target/wasm32-wasi/release/%.wasm,%, $@) +$(arbitrator_cases)/rust/$(wasm32_wasi)/%.wasm: $(arbitrator_cases)/rust/src/bin/%.rs $(arbitrator_cases)/rust/src/lib.rs + cargo build --manifest-path $(arbitrator_cases)/rust/Cargo.toml --release --target wasm32-wasi --bin $(patsubst $(arbitrator_cases)/rust/$(wasm32_wasi)/%.wasm,%, $@) -$(arbitrator_cases)/go/main: $(arbitrator_cases)/go/main.go - cd $(arbitrator_cases)/go && GOOS=js GOARCH=wasm go build main.go +$(arbitrator_cases)/go/testcase.wasm: $(arbitrator_cases)/go/*.go .make/solgen + cd $(arbitrator_cases)/go && GOOS=wasip1 GOARCH=wasm go build -o testcase.wasm -$(arbitrator_generated_header): $(DEP_PREDICATE) arbitrator/prover/src/lib.rs arbitrator/prover/src/utils.rs +$(arbitrator_generated_header): $(DEP_PREDICATE) $(stylus_files) @echo creating ${PWD}/$(arbitrator_generated_header) mkdir -p `dirname $(arbitrator_generated_header)` - cd arbitrator && cbindgen --config cbindgen.toml --crate prover --output ../$(arbitrator_generated_header) + cd arbitrator/stylus && cbindgen --config cbindgen.toml --crate stylus --output ../../$(arbitrator_generated_header) + @touch -c $@ # cargo might decide to not rebuild the header -$(output_root)/machines/latest/wasi_stub.wasm: $(DEP_PREDICATE) $(arbitrator_wasm_wasistub_files) - mkdir -p $(output_root)/machines/latest +$(output_latest)/wasi_stub.wasm: $(DEP_PREDICATE) $(call wasm_lib_deps,wasi-stub) cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-unknown-unknown --package wasi-stub - install arbitrator/wasm-libraries/target/wasm32-unknown-unknown/release/wasi_stub.wasm $@ + install arbitrator/wasm-libraries/$(wasm32_unknown)/wasi_stub.wasm $@ arbitrator/wasm-libraries/soft-float/SoftFloat/build/Wasm-Clang/softfloat.a: $(DEP_PREDICATE) \ arbitrator/wasm-libraries/soft-float/SoftFloat/build/Wasm-Clang/Makefile \ @@ -237,12 +314,11 @@ arbitrator/wasm-libraries/soft-float/bindings32.o: $(DEP_PREDICATE) arbitrator/w arbitrator/wasm-libraries/soft-float/bindings64.o: $(DEP_PREDICATE) arbitrator/wasm-libraries/soft-float/bindings64.c clang arbitrator/wasm-libraries/soft-float/bindings64.c --sysroot $(WASI_SYSROOT) -I arbitrator/wasm-libraries/soft-float/SoftFloat/source/include -target wasm32-wasi -Wconversion -c -o $@ -$(output_root)/machines/latest/soft-float.wasm: $(DEP_PREDICATE) \ +$(output_latest)/soft-float.wasm: $(DEP_PREDICATE) \ arbitrator/wasm-libraries/soft-float/bindings32.o \ arbitrator/wasm-libraries/soft-float/bindings64.o \ arbitrator/wasm-libraries/soft-float/SoftFloat/build/Wasm-Clang/softfloat.a \ - .make/wasm-lib - mkdir -p $(output_root)/machines/latest + .make/wasm-lib .make/machines wasm-ld \ arbitrator/wasm-libraries/soft-float/bindings32.o \ arbitrator/wasm-libraries/soft-float/bindings64.o \ @@ -261,49 +337,133 @@ $(output_root)/machines/latest/soft-float.wasm: $(DEP_PREDICATE) \ --export wavm__f32_demote_f64 \ --export wavm__f64_promote_f32 -$(output_root)/machines/latest/go_stub.wasm: $(DEP_PREDICATE) $(wildcard arbitrator/wasm-libraries/go-stub/src/*) - mkdir -p $(output_root)/machines/latest - cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --package go-stub - install arbitrator/wasm-libraries/target/wasm32-wasi/release/go_stub.wasm $@ - -$(output_root)/machines/latest/host_io.wasm: $(DEP_PREDICATE) $(wildcard arbitrator/wasm-libraries/host-io/src/*) - mkdir -p $(output_root)/machines/latest +$(output_latest)/host_io.wasm: $(DEP_PREDICATE) $(call wasm_lib_deps,host-io) $(wasm_lib_go_abi) cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --package host-io - install arbitrator/wasm-libraries/target/wasm32-wasi/release/host_io.wasm $@ + install arbitrator/wasm-libraries/$(wasm32_wasi)/host_io.wasm $@ + +$(output_latest)/user_host.wasm: $(DEP_PREDICATE) $(wasm_lib_user_host) $(rust_prover_files) $(output_latest)/forward_stub.wasm .make/machines + cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --package user-host + install arbitrator/wasm-libraries/$(wasm32_wasi)/user_host.wasm $@ + +$(output_latest)/program_exec.wasm: $(DEP_PREDICATE) $(call wasm_lib_deps,program-exec) $(rust_prover_files) .make/machines + cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --package program-exec + install arbitrator/wasm-libraries/$(wasm32_wasi)/program_exec.wasm $@ + +$(output_latest)/user_test.wasm: $(DEP_PREDICATE) $(call wasm_lib_deps,user-test) $(rust_prover_files) .make/machines + cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --package user-test + install arbitrator/wasm-libraries/$(wasm32_wasi)/user_test.wasm $@ + +$(output_latest)/arbcompress.wasm: $(DEP_PREDICATE) $(call wasm_lib_deps,brotli) $(wasm_lib_go_abi) + cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --package arbcompress + install arbitrator/wasm-libraries/$(wasm32_wasi)/arbcompress.wasm $@ -$(output_root)/machines/latest/brotli.wasm: $(DEP_PREDICATE) $(wildcard arbitrator/wasm-libraries/brotli/src/*) .make/cbrotli-wasm - mkdir -p $(output_root)/machines/latest - cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --package brotli - install arbitrator/wasm-libraries/target/wasm32-wasi/release/brotli.wasm $@ +$(output_latest)/forward.wasm: $(DEP_PREDICATE) $(wasm_lib_forward) .make/machines + cargo run --manifest-path $(forward_dir)/Cargo.toml -- --path $(forward_dir)/forward.wat + wat2wasm $(wasm_lib)/forward/forward.wat -o $@ -$(output_root)/machines/latest/machine.wavm.br: $(DEP_PREDICATE) $(arbitrator_prover_bin) $(arbitrator_wasm_libs) $(replay_wasm) - $(arbitrator_prover_bin) $(replay_wasm) --generate-binaries $(output_root)/machines/latest -l $(output_root)/machines/latest/soft-float.wasm -l $(output_root)/machines/latest/wasi_stub.wasm -l $(output_root)/machines/latest/go_stub.wasm -l $(output_root)/machines/latest/host_io.wasm -l $(output_root)/machines/latest/brotli.wasm +$(output_latest)/forward_stub.wasm: $(DEP_PREDICATE) $(wasm_lib_forward) .make/machines + cargo run --manifest-path $(forward_dir)/Cargo.toml -- --path $(forward_dir)/forward_stub.wat --stub + wat2wasm $(wasm_lib)/forward/forward_stub.wat -o $@ + +$(output_latest)/machine.wavm.br: $(DEP_PREDICATE) $(prover_bin) $(arbitrator_wasm_libs) $(replay_wasm) + $(prover_bin) $(replay_wasm) --generate-binaries $(output_latest) \ + $(patsubst %,-l $(output_latest)/%.wasm, forward soft-float wasi_stub host_io user_host arbcompress program_exec) $(arbitrator_cases)/%.wasm: $(arbitrator_cases)/%.wat wat2wasm $< -o $@ -contracts/test/prover/proofs/float%.json: $(arbitrator_cases)/float%.wasm $(arbitrator_prover_bin) $(output_root)/machines/latest/soft-float.wasm - $(arbitrator_prover_bin) $< -l $(output_root)/machines/latest/soft-float.wasm -o $@ -b --allow-hostapi --require-success --always-merkleize +$(stylus_test_dir)/%.wasm: $(stylus_test_dir)/%.b $(stylus_lang_bf) + cargo run --manifest-path arbitrator/langs/bf/Cargo.toml $< -o $@ + +$(stylus_test_keccak_wasm): $(stylus_test_keccak_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary + +$(stylus_test_keccak-100_wasm): $(stylus_test_keccak-100_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary + +$(stylus_test_fallible_wasm): $(stylus_test_fallible_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary -contracts/test/prover/proofs/no-stack-pollution.json: $(arbitrator_cases)/no-stack-pollution.wasm $(arbitrator_prover_bin) - $(arbitrator_prover_bin) $< -o $@ --allow-hostapi --require-success --always-merkleize +$(stylus_test_storage_wasm): $(stylus_test_storage_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary + +$(stylus_test_multicall_wasm): $(stylus_test_multicall_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary + +$(stylus_test_log_wasm): $(stylus_test_log_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary + +$(stylus_test_create_wasm): $(stylus_test_create_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary + +$(stylus_test_math_wasm): $(stylus_test_math_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary + +$(stylus_test_evm-data_wasm): $(stylus_test_evm-data_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary + +$(stylus_test_read-return-data_wasm): $(stylus_test_read-return-data_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary + +$(stylus_test_sdk-storage_wasm): $(stylus_test_sdk-storage_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary + +$(stylus_test_erc20_wasm): $(stylus_test_erc20_src) + $(cargo_nightly) --manifest-path $< --release --config $(stylus_cargo) + @touch -c $@ # cargo might decide to not rebuild the binary + +contracts/test/prover/proofs/float%.json: $(arbitrator_cases)/float%.wasm $(prover_bin) $(output_latest)/soft-float.wasm + $(prover_bin) $< -l $(output_latest)/soft-float.wasm -o $@ -b --allow-hostapi --require-success --always-merkleize + +contracts/test/prover/proofs/no-stack-pollution.json: $(arbitrator_cases)/no-stack-pollution.wasm $(prover_bin) + $(prover_bin) $< -o $@ --allow-hostapi --require-success --always-merkleize target/testdata/preimages.bin: mkdir -p `dirname $@` python3 scripts/create-test-preimages.py $@ -contracts/test/prover/proofs/rust-%.json: $(arbitrator_cases)/rust/target/wasm32-wasi/release/%.wasm $(arbitrator_prover_bin) $(arbitrator_wasm_libs_nogo) target/testdata/preimages.bin - $(arbitrator_prover_bin) $< $(arbitrator_wasm_lib_flags_nogo) -o $@ -b --allow-hostapi --require-success --inbox-add-stub-headers --inbox $(arbitrator_cases)/rust/data/msg0.bin --inbox $(arbitrator_cases)/rust/data/msg1.bin --delayed-inbox $(arbitrator_cases)/rust/data/msg0.bin --delayed-inbox $(arbitrator_cases)/rust/data/msg1.bin --preimages target/testdata/preimages.bin +contracts/test/prover/proofs/rust-%.json: $(arbitrator_cases)/rust/$(wasm32_wasi)/%.wasm $(prover_bin) $(arbitrator_wasm_libs) target/testdata/preimages.bin + $(prover_bin) $< $(arbitrator_wasm_lib_flags) -o $@ -b --allow-hostapi --require-success --inbox-add-stub-headers --inbox $(arbitrator_cases)/rust/data/msg0.bin --inbox $(arbitrator_cases)/rust/data/msg1.bin --delayed-inbox $(arbitrator_cases)/rust/data/msg0.bin --delayed-inbox $(arbitrator_cases)/rust/data/msg1.bin --preimages target/testdata/preimages.bin -contracts/test/prover/proofs/go.json: $(arbitrator_cases)/go/main $(arbitrator_prover_bin) $(arbitrator_wasm_libs) target/testdata/preimages.bin - $(arbitrator_prover_bin) $< $(arbitrator_wasm_lib_flags) -o $@ -i 5000000 --require-success --preimages target/testdata/preimages.bin +contracts/test/prover/proofs/go.json: $(arbitrator_cases)/go/testcase.wasm $(prover_bin) $(arbitrator_wasm_libs) target/testdata/preimages.bin $(arbitrator_tests_link_deps) $(arbitrator_cases)/user.wasm + $(prover_bin) $< $(arbitrator_wasm_lib_flags) -o $@ -b --require-success --preimages target/testdata/preimages.bin --stylus-modules $(arbitrator_cases)/user.wasm + +# avoid testing user.wasm in onestepproofs. It can only run as stylus program. +contracts/test/prover/proofs/user.json: + echo "[]" > $@ # avoid testing read-inboxmsg-10 in onestepproofs. It's used for go challenge testing. contracts/test/prover/proofs/read-inboxmsg-10.json: echo "[]" > $@ -contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(arbitrator_prover_bin) - $(arbitrator_prover_bin) $< -o $@ --allow-hostapi --always-merkleize +contracts/test/prover/proofs/global-state.json: + echo "[]" > $@ + +contracts/test/prover/proofs/forward-test.json: $(arbitrator_cases)/forward-test.wasm $(arbitrator_tests_forward_deps) $(prover_bin) + $(prover_bin) $< -o $@ --allow-hostapi --always-merkleize $(patsubst %,-l %, $(arbitrator_tests_forward_deps)) + +contracts/test/prover/proofs/link.json: $(arbitrator_cases)/link.wasm $(arbitrator_tests_link_deps) $(prover_bin) + $(prover_bin) $< -o $@ --allow-hostapi --always-merkleize --stylus-modules $(arbitrator_tests_link_deps) --require-success + +contracts/test/prover/proofs/dynamic.json: $(patsubst %,$(arbitrator_cases)/%.wasm, dynamic user) $(prover_bin) + $(prover_bin) $< -o $@ --allow-hostapi --always-merkleize --stylus-modules $(arbitrator_cases)/user.wasm --require-success + +contracts/test/prover/proofs/bulk-memory.json: $(patsubst %,$(arbitrator_cases)/%.wasm, bulk-memory) $(prover_bin) + $(prover_bin) $< -o $@ --allow-hostapi --always-merkleize --stylus-modules $(arbitrator_cases)/user.wasm -b + +contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(prover_bin) + $(prover_bin) $< -o $@ --allow-hostapi --always-merkleize # strategic rules to minimize dependency building @@ -315,13 +475,14 @@ contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(arbitrator_pro .make/fmt: $(DEP_PREDICATE) build-node-deps .make/yarndeps $(ORDER_ONLY_PREDICATE) .make golangci-lint run --disable-all -E gofmt --fix - cargo fmt --all --manifest-path arbitrator/Cargo.toml -- --check + cargo fmt -p arbutil -p prover -p jit -p stylus --manifest-path arbitrator/Cargo.toml -- --check cargo fmt --all --manifest-path arbitrator/wasm-testsuite/Cargo.toml -- --check + cargo fmt --all --manifest-path arbitrator/langs/rust/Cargo.toml -- --check yarn --cwd contracts prettier:solidity @touch $@ .make/test-go: $(DEP_PREDICATE) $(go_source) build-node-deps test-go-deps $(ORDER_ONLY_PREDICATE) .make - gotestsum --format short-verbose + gotestsum --format short-verbose --no-color=false @touch $@ .make/solgen: $(DEP_PREDICATE) solgen/gen.go .make/solidity $(ORDER_ONLY_PREDICATE) .make @@ -357,6 +518,10 @@ contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(arbitrator_pro test -f arbitrator/wasm-libraries/soft-float/bindings64.o || ./scripts/build-brotli.sh -f -d -t .. @touch $@ +.make/machines: $(DEP_PREDICATE) $(ORDER_ONLY_PREDICATE) .make + mkdir -p $(output_latest) + touch $@ + .make: mkdir .make @@ -365,4 +530,4 @@ contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(arbitrator_pro always: # use this to force other rules to always build .DELETE_ON_ERROR: # causes a failure to delete its target -.PHONY: push all build build-node-deps test-go-deps build-prover-header build-prover-lib build-prover-bin build-jit build-replay-env build-solidity build-wasm-libs contracts format fmt lint test-go test-gen-proofs push clean docker +.PHONY: push all build build-node-deps test-go-deps build-prover-header build-prover-lib build-prover-bin build-jit build-replay-env build-solidity build-wasm-libs contracts format fmt lint stylus-benchmarks test-go test-gen-proofs push clean docker diff --git a/README.md b/README.md index 4a522be82f..a07772628b 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Arbitrum One successfully migrated from the Classic Arbitrum stack onto Nitro on ## License -Nitro is currently licensed under a [Business Source License](./LICENSE), similar to our friends at Uniswap and Aave, with an "Additional Use Grant" to ensure that everyone can have full comfort using and running nodes on all public Arbitrum chains. +Nitro is currently licensed under a [Business Source License](./LICENSE.md), similar to our friends at Uniswap and Aave, with an "Additional Use Grant" to ensure that everyone can have full comfort using and running nodes on all public Arbitrum chains. The Additional Use Grant also permits the deployment of the Nitro software, in a permissionless fashion and without cost, as a new blockchain provided that the chain settles to either Arbitrum One or Arbitrum Nova. diff --git a/arbcompress/compress_cgo.go b/arbcompress/compress_cgo.go deleted file mode 100644 index 47da429416..0000000000 --- a/arbcompress/compress_cgo.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE - -//go:build !js -// +build !js - -package arbcompress - -/* -#cgo CFLAGS: -g -Wall -I${SRCDIR}/../target/include/ -#cgo LDFLAGS: ${SRCDIR}/../target/lib/libbrotlidec-static.a ${SRCDIR}/../target/lib/libbrotlienc-static.a ${SRCDIR}/../target/lib/libbrotlicommon-static.a -lm -#include "brotli/encode.h" -#include "brotli/decode.h" -*/ -import "C" -import ( - "fmt" -) - -func Decompress(input []byte, maxSize int) ([]byte, error) { - outbuf := make([]byte, maxSize) - outsize := C.size_t(maxSize) - var ptr *C.uint8_t - if len(input) > 0 { - ptr = (*C.uint8_t)(&input[0]) - } - res := C.BrotliDecoderDecompress(C.size_t(len(input)), ptr, &outsize, (*C.uint8_t)(&outbuf[0])) - if res != 1 { - return nil, fmt.Errorf("failed decompression: %d", res) - } - if int(outsize) > maxSize { - return nil, fmt.Errorf("result too large: %d", outsize) - } - return outbuf[:outsize], nil -} - -func compressLevel(input []byte, level int) ([]byte, error) { - maxOutSize := compressedBufferSizeFor(len(input)) - outbuf := make([]byte, maxOutSize) - outSize := C.size_t(maxOutSize) - var inputPtr *C.uint8_t - if len(input) > 0 { - inputPtr = (*C.uint8_t)(&input[0]) - } - res := C.BrotliEncoderCompress(C.int(level), C.BROTLI_DEFAULT_WINDOW, C.BROTLI_MODE_GENERIC, - C.size_t(len(input)), inputPtr, &outSize, (*C.uint8_t)(&outbuf[0])) - if res != 1 { - return nil, fmt.Errorf("failed compression: %d", res) - } - return outbuf[:outSize], nil -} - -func CompressWell(input []byte) ([]byte, error) { - return compressLevel(input, LEVEL_WELL) -} diff --git a/arbcompress/compress_common.go b/arbcompress/compress_common.go index 990fd2e2be..a61dd9a171 100644 --- a/arbcompress/compress_common.go +++ b/arbcompress/compress_common.go @@ -1,8 +1,15 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package arbcompress +type Dictionary uint32 + +const ( + EmptyDictionary Dictionary = iota + StylusProgramDictionary +) + const LEVEL_WELL = 11 const WINDOW_SIZE = 22 // BROTLI_DEFAULT_WINDOW @@ -11,5 +18,5 @@ func compressedBufferSizeFor(length int) int { } func CompressLevel(input []byte, level int) ([]byte, error) { - return compressLevel(input, level) + return Compress(input, uint32(level), EmptyDictionary) } diff --git a/arbcompress/compress_wasm.go b/arbcompress/compress_wasm.go deleted file mode 100644 index ba2eb1d10c..0000000000 --- a/arbcompress/compress_wasm.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE - -//go:build js -// +build js - -package arbcompress - -import ( - "fmt" -) - -func brotliCompress(inBuf []byte, outBuf []byte, level int, windowSize int) int64 - -func brotliDecompress(inBuf []byte, outBuf []byte) int64 - -func Decompress(input []byte, maxSize int) ([]byte, error) { - outBuf := make([]byte, maxSize) - outLen := brotliDecompress(input, outBuf) - if outLen < 0 { - return nil, fmt.Errorf("failed decompression") - } - return outBuf[:outLen], nil -} - -func compressLevel(input []byte, level int) ([]byte, error) { - maxOutSize := compressedBufferSizeFor(len(input)) - outBuf := make([]byte, maxOutSize) - outLen := brotliCompress(input, outBuf, level, WINDOW_SIZE) - if outLen < 0 { - return nil, fmt.Errorf("failed compression") - } - return outBuf[:outLen], nil -} diff --git a/arbcompress/native.go b/arbcompress/native.go new file mode 100644 index 0000000000..4624d6222e --- /dev/null +++ b/arbcompress/native.go @@ -0,0 +1,76 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +//go:build !wasm +// +build !wasm + +package arbcompress + +/* +#cgo CFLAGS: -g -Wall -I${SRCDIR}/../target/include/ +#cgo LDFLAGS: ${SRCDIR}/../target/lib/libstylus.a -lm +#include "arbitrator.h" +*/ +import "C" +import "fmt" + +type u8 = C.uint8_t +type u32 = C.uint32_t +type usize = C.size_t + +type brotliBool = uint32 +type brotliBuffer = C.BrotliBuffer + +const ( + brotliFalse brotliBool = iota + brotliTrue +) + +func CompressWell(input []byte) ([]byte, error) { + return Compress(input, LEVEL_WELL, EmptyDictionary) +} + +func Compress(input []byte, level uint32, dictionary Dictionary) ([]byte, error) { + maxSize := compressedBufferSizeFor(len(input)) + output := make([]byte, maxSize) + outbuf := sliceToBuffer(output) + inbuf := sliceToBuffer(input) + + status := C.brotli_compress(inbuf, outbuf, C.Dictionary(dictionary), u32(level)) + if status != C.BrotliStatus_Success { + return nil, fmt.Errorf("failed decompression: %d", status) + } + output = output[:*outbuf.len] + return output, nil +} + +func Decompress(input []byte, maxSize int) ([]byte, error) { + return DecompressWithDictionary(input, maxSize, EmptyDictionary) +} + +func DecompressWithDictionary(input []byte, maxSize int, dictionary Dictionary) ([]byte, error) { + output := make([]byte, maxSize) + outbuf := sliceToBuffer(output) + inbuf := sliceToBuffer(input) + + status := C.brotli_decompress(inbuf, outbuf, C.Dictionary(dictionary)) + if status != C.BrotliStatus_Success { + return nil, fmt.Errorf("failed decompression: %d", status) + } + if *outbuf.len > usize(maxSize) { + return nil, fmt.Errorf("failed decompression: result too large: %d", *outbuf.len) + } + output = output[:*outbuf.len] + return output, nil +} + +func sliceToBuffer(slice []byte) brotliBuffer { + count := usize(len(slice)) + if count == 0 { + slice = []byte{0x00} // ensures pointer is not null (shouldn't be necessary, but brotli docs are picky about NULL) + } + return brotliBuffer{ + ptr: (*u8)(&slice[0]), + len: &count, + } +} diff --git a/arbcompress/raw.s b/arbcompress/raw.s deleted file mode 100644 index 5e4b053b92..0000000000 --- a/arbcompress/raw.s +++ /dev/null @@ -1,16 +0,0 @@ -// -// Copyright 2021, Offchain Labs, Inc. All rights reserved. -// - -//go:build js -// +build js - -#include "textflag.h" - -TEXT ·brotliCompress(SB), NOSPLIT, $0 - CallImport - RET - -TEXT ·brotliDecompress(SB), NOSPLIT, $0 - CallImport - RET diff --git a/arbcompress/wasm.go b/arbcompress/wasm.go new file mode 100644 index 0000000000..71d704ce03 --- /dev/null +++ b/arbcompress/wasm.go @@ -0,0 +1,64 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +//go:build wasm +// +build wasm + +package arbcompress + +import ( + "fmt" + "unsafe" + + "github.com/offchainlabs/nitro/arbutil" +) + +type brotliStatus = uint32 + +const ( + brotliFailure brotliStatus = iota + brotliSuccess +) + +//go:wasmimport arbcompress brotli_compress +func brotliCompress(inBuf unsafe.Pointer, inLen uint32, outBuf unsafe.Pointer, outLen unsafe.Pointer, level, windowSize uint32, dictionary Dictionary) brotliStatus + +//go:wasmimport arbcompress brotli_decompress +func brotliDecompress(inBuf unsafe.Pointer, inLen uint32, outBuf unsafe.Pointer, outLen unsafe.Pointer, dictionary Dictionary) brotliStatus + +func Compress(input []byte, level uint32, dictionary Dictionary) ([]byte, error) { + maxOutSize := compressedBufferSizeFor(len(input)) + outBuf := make([]byte, maxOutSize) + outLen := uint32(len(outBuf)) + status := brotliCompress( + arbutil.SliceToUnsafePointer(input), uint32(len(input)), + arbutil.SliceToUnsafePointer(outBuf), unsafe.Pointer(&outLen), + uint32(level), + WINDOW_SIZE, + dictionary, + ) + if status != brotliSuccess { + return nil, fmt.Errorf("failed compression") + } + return outBuf[:outLen], nil +} + +func Decompress(input []byte, maxSize int) ([]byte, error) { + return DecompressWithDictionary(input, maxSize, EmptyDictionary) +} + +func DecompressWithDictionary(input []byte, maxSize int, dictionary Dictionary) ([]byte, error) { + outBuf := make([]byte, maxSize) + outLen := uint32(len(outBuf)) + status := brotliDecompress( + arbutil.SliceToUnsafePointer(input), + uint32(len(input)), + arbutil.SliceToUnsafePointer(outBuf), + unsafe.Pointer(&outLen), + dictionary, + ) + if status != brotliSuccess { + return nil, fmt.Errorf("failed decompression") + } + return outBuf[:outLen], nil +} diff --git a/arbitrator/Cargo.lock b/arbitrator/Cargo.lock index 165fee89c3..a89dc5e97e 100644 --- a/arbitrator/Cargo.lock +++ b/arbitrator/Cargo.lock @@ -4,11 +4,11 @@ version = 3 [[package]] name = "addr2line" -version = "0.17.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ - "gimli", + "gimli 0.29.0", ] [[package]] @@ -19,54 +19,81 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.7.6" +version = "0.7.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" dependencies = [ "getrandom", "once_cell", "version_check", ] +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" -version = "0.7.19" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] -name = "aliasable" -version = "0.1.3" +name = "allocator-api2" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "ansi_term" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi", ] +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" + [[package]] name = "arbutil" version = "0.1.0" dependencies = [ "digest 0.10.7", + "eyre", + "fnv", + "hex", + "num-traits", "num_enum", + "ruint2", + "serde", "sha2 0.10.8", "sha3 0.10.8", + "siphasher", + "tiny-keccak", + "wasmparser", ] [[package]] name = "arrayvec" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4dc07131ffa69b8072d35f5007352af944213cde02545e2103680baed38fcd" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "atty" @@ -74,29 +101,29 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] [[package]] name = "autocfg" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" dependencies = [ "addr2line", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", - "object 0.29.0", + "object 0.35.0", "rustc-demangle", ] @@ -109,29 +136,6 @@ dependencies = [ "serde", ] -[[package]] -name = "bindgen" -version = "0.66.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" -dependencies = [ - "bitflags 2.4.1", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "prettyplease", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "syn 2.0.45", - "which", -] - [[package]] name = "bitflags" version = "1.3.2" @@ -140,9 +144,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] [[package]] name = "block-buffer" @@ -182,71 +198,70 @@ dependencies = [ ] [[package]] -name = "brotli-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" +name = "brotli" +version = "0.1.0" dependencies = [ - "cc", - "libc", + "lazy_static", + "num_enum", + "wasmer", + "wee_alloc", ] [[package]] -name = "brotli2" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" +name = "brotli-fuzz" +version = "0.0.0" dependencies = [ - "brotli-sys", - "libc", + "brotli", + "hex", + "libfuzzer-sys", ] [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytecheck" -version = "0.6.9" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" dependencies = [ "bytecheck_derive", "ptr_meta", + "simdutf8", ] [[package]] name = "bytecheck_derive" -version = "0.6.9" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" dependencies = [ "proc-macro2", "quote", - "syn 1.0.76", + "syn 1.0.109", ] [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.3.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "c-kzg" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32700dc7904064bb64e857d38a1766607372928e2466ee5f02a869829b3297d7" +checksum = "94a4bc5367b6284358d2a6a6a1dc2d92ec4b86034561c3b9d3341909752fd848" dependencies = [ - "bindgen", "blst", "cc", "glob", @@ -256,45 +271,44 @@ dependencies = [ ] [[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +name = "caller-env" +version = "0.1.0" dependencies = [ - "libc", + "brotli", + "num_enum", + "rand", + "rand_pcg", + "wasmer", ] [[package]] -name = "cexpr" -version = "0.6.0" +name = "cc" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" dependencies = [ - "nom", + "jobserver", + "libc", + "once_cell", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] -name = "clang-sys" -version = "1.7.0" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" -dependencies = [ - "glob", - "libc", - "libloading", -] +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "2.33.3" +version = "2.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", @@ -305,14 +319,20 @@ dependencies = [ "vec_map", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "corosensei" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9847f90f32a50b0dcbd68bc23ff242798b13080b97b0569f6ed96a45ce4cf2cd" +checksum = "80128832c58ea9cbd041d2a759ec449224487b2c1e400453d99d244eead87a8e" dependencies = [ "autocfg", - "cfg-if", + "cfg-if 1.0.0", "libc", "scopeguard", "windows-sys 0.33.0", @@ -320,34 +340,37 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] [[package]] name = "cranelift-bforest" -version = "0.86.1" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "529ffacce2249ac60edba2941672dfedf3d96558b415d0d8083cd007456e0f55" +checksum = "2a2ab4512dfd3a6f4be184403a195f76e81a8a9f9e6c898e19d2dc3ce20e0115" dependencies = [ "cranelift-entity", ] [[package]] name = "cranelift-codegen" -version = "0.86.1" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "427d105f617efc8cb55f8d036a7fded2e227892d8780b4985e5551f8d27c4a92" +checksum = "98b022ed2a5913a38839dfbafe6cf135342661293b08049843362df4301261dc" dependencies = [ + "arrayvec", + "bumpalo", "cranelift-bforest", "cranelift-codegen-meta", "cranelift-codegen-shared", + "cranelift-egraph", "cranelift-entity", "cranelift-isle", - "gimli", + "gimli 0.26.2", "log", "regalloc2", "smallvec", @@ -356,30 +379,44 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" -version = "0.86.1" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "551674bed85b838d45358e3eab4f0ffaa6790c70dc08184204b9a54b41cdb7d1" +checksum = "639307b45434ad112a98f8300c0f0ab085cbefcd767efcdef9ef19d4c0756e74" dependencies = [ "cranelift-codegen-shared", ] [[package]] name = "cranelift-codegen-shared" -version = "0.86.1" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "278e52e29c53fcf32431ef08406c295699a70306d05a0715c5b1bf50e33a9ab7" + +[[package]] +name = "cranelift-egraph" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b3a63ae57498c3eb495360944a33571754241e15e47e3bcae6082f40fec5866" +checksum = "624b54323b06e675293939311943ba82d323bb340468ce1889be5da7932c8d73" +dependencies = [ + "cranelift-entity", + "fxhash", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "log", + "smallvec", +] [[package]] name = "cranelift-entity" -version = "0.86.1" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11aa8aa624c72cc1c94ea3d0739fa61248260b5b14d3646f51593a88d67f3e6e" +checksum = "9a59bcbca89c3f1b70b93ab3cbba5e5e0cbf3e63dadb23c7525cb142e21a9d4c" [[package]] name = "cranelift-frontend" -version = "0.86.1" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "544ee8f4d1c9559c9aa6d46e7aaeac4a13856d620561094f35527356c7d21bd0" +checksum = "0d70abacb8cfef3dc8ff7e8836e9c1d70f7967dfdac824a4cd5e30223415aca6" dependencies = [ "cranelift-codegen", "log", @@ -389,53 +426,49 @@ dependencies = [ [[package]] name = "cranelift-isle" -version = "0.86.1" +version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed16b14363d929b8c37e3c557d0a7396791b383ecc302141643c054343170aad" +checksum = "393bc73c451830ff8dbb3a07f61843d6cb41a084f9996319917c0b291ed785bb" [[package]] -name = "crossbeam-channel" -version = "0.5.1" +name = "crossbeam-deque" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", + "crossbeam-epoch", "crossbeam-utils", ] [[package]] -name = "crossbeam-deque" -version = "0.8.1" +name = "crossbeam-epoch" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "cfg-if", - "crossbeam-epoch", "crossbeam-utils", ] [[package]] -name = "crossbeam-epoch" -version = "0.9.5" +name = "crossbeam-queue" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" -dependencies = [ - "cfg-if", - "lazy_static", -] +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-common" @@ -453,8 +486,18 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +dependencies = [ + "darling_core 0.20.9", + "darling_macro 0.20.9", ] [[package]] @@ -468,7 +511,20 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 1.0.76", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.66", ] [[package]] @@ -477,9 +533,57 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core", + "darling_core 0.13.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +dependencies = [ + "darling_core 0.20.9", "quote", - "syn 1.0.76", + "syn 2.0.66", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if 1.0.0", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", ] [[package]] @@ -501,11 +605,37 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2 0.5.10", +] + [[package]] name = "either" -version = "1.6.1" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "enum-iterator" @@ -524,28 +654,28 @@ checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" dependencies = [ "proc-macro2", "quote", - "syn 1.0.76", + "syn 1.0.109", ] [[package]] name = "enumset" -version = "1.0.11" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4799cdb24d48f1f8a7a98d06b7fde65a85a2d1e42b25a889f5406aa1fbefe074" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.6.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea83a3fbdc1d999ccfbcbee717eab36f8edf2d71693a23ce0d7cca19e085304c" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ - "darling", + "darling 0.20.9", "proc-macro2", "quote", - "syn 1.0.76", + "syn 2.0.66", ] [[package]] @@ -554,21 +684,11 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "errno" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "eyre" -version = "0.6.5" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "221239d1d5ea86bf5d6f91c9d6bc3646ffe471b08ff9b0f91c44f115ac969d2b" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -586,6 +706,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "fxhash" version = "0.2.1" @@ -597,9 +723,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.4" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -607,11 +733,11 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi", ] @@ -623,21 +749,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", - "indexmap 1.8.1", + "indexmap 1.9.3", "stable_deref_trait", ] [[package]] -name = "glob" -version = "0.3.1" +name = "gimli" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] -name = "hashbrown" -version = "0.11.2" +name = "glob" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "hashbrown" @@ -645,14 +771,18 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.8", ] [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] [[package]] name = "heck" @@ -663,12 +793,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - [[package]] name = "hermit-abi" version = "0.1.19" @@ -679,19 +803,16 @@ dependencies = [ ] [[package]] -name = "hex" -version = "0.4.3" +name = "hermit-abi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] -name = "home" -version = "0.5.9" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" -dependencies = [ - "windows-sys 0.52.0", -] +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "ident_case" @@ -707,108 +828,111 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "1.8.1" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown 0.11.2", + "hashbrown 0.12.3", ] [[package]] name = "indexmap" -version = "2.0.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.5", ] [[package]] name = "inkwell" -version = "0.1.0-beta.4" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2223d0eba0ae6d40a3e4680c6a3209143471e1f38b41746ea309aa36dde9f90b" +checksum = "bbac11e485159a525867fb7e6aa61981453e6a72f625fde6a4ab3047b0c6dec9" dependencies = [ "either", "inkwell_internals", "libc", "llvm-sys", "once_cell", - "parking_lot 0.11.2", - "regex", + "parking_lot", ] [[package]] name = "inkwell_internals" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7090af3d300424caa81976b8c97bca41cd70e861272c072e188ae082fb49f9" +checksum = "87d00c17e264ce02be5bc23d7bff959188ec7137beddd06b8b6b05a7c680ea85" dependencies = [ "proc-macro2", "quote", - "syn 1.0.76", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", + "syn 1.0.109", ] [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jit" version = "0.1.0" dependencies = [ "arbutil", + "brotli", + "caller-env", "eyre", "hex", "libc", - "ouroboros", - "parking_lot 0.12.1", + "parking_lot", + "prover", "rand", "rand_pcg", + "sha2 0.9.9", "sha3 0.9.1", "structopt", + "stylus", "thiserror", "wasmer", "wasmer-compiler-cranelift", "wasmer-compiler-llvm", ] +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + [[package]] name = "js-sys" -version = "0.3.60" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "keccak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" dependencies = [ "cpufeatures", ] @@ -819,12 +943,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "leb128" version = "0.2.5" @@ -833,44 +951,39 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] -name = "libloading" -version = "0.8.1" +name = "libfuzzer-sys" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" +checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "arbitrary", + "cc", + "once_cell", ] -[[package]] -name = "linux-raw-sys" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" - [[package]] name = "llvm-sys" -version = "120.2.4" +version = "150.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b716322964966a62377cf86e64f00ca7043505fdf27bd2ec7d41ae6682d1e7" +checksum = "bfd60e740af945d99c2446a52e3ab8cdba2f740a40a16c51f6871bdea2abc687" dependencies = [ "cc", "lazy_static", "libc", "regex", - "semver 0.11.0", + "semver", ] [[package]] name = "lock_api" -version = "0.4.8" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f80bf5aacaf25cbfc8210d1cfb718f2bf3b11c4c54e5afe36c236853a8ec390" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -878,11 +991,17 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "lru" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" dependencies = [ - "cfg-if", + "hashbrown 0.14.5", ] [[package]] @@ -894,41 +1013,65 @@ dependencies = [ "libc", ] +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + [[package]] name = "memchr" -version = "2.4.1" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "memmap2" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] [[package]] name = "memmap2" -version = "0.5.7" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.4" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + [[package]] name = "minimal-lexical" -version = "0.1.3" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c835948974f68e0bd58636fc6c5b1fbff7b297e3046f11b3b3c18bbac012c6d" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.3" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] @@ -941,13 +1084,12 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "nom" -version = "7.0.0" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffd9d26838a953b4af82cbeb9f1592c6798916983959be223a7124e992742c1" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", - "version_check", ] [[package]] @@ -963,9 +1105,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -977,39 +1119,48 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-complex" -version = "0.4.0" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "num-integer" -version = "0.1.44" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.42" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -1018,11 +1169,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -1030,42 +1180,42 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.14" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] [[package]] name = "num_enum" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.45", + "syn 2.0.66", ] [[package]] @@ -1079,136 +1229,66 @@ dependencies = [ [[package]] name = "object" -version = "0.29.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.16.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "ouroboros" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6a6d0919a92ba28d8109a103e0de08f89706be0eeaad1130fd1a34030dee84a" -dependencies = [ - "aliasable", - "ouroboros_macro", - "static_assertions", -] - -[[package]] -name = "ouroboros_macro" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bc2307dc3420554ae349230dac4969c66d7c2feead3a8cab05ea0c604daca6" -dependencies = [ - "heck 0.4.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.45", -] - -[[package]] -name = "parking_lot" -version = "0.11.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.5", -] +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.3", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall", "smallvec", - "windows-sys 0.36.1", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "pest" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0560d531d1febc25a3c9398a62a71256c0178f2e3443baedd9ad4bb8c9deb4" -dependencies = [ - "thiserror", - "ucd-trie", + "windows-targets", ] [[package]] name = "pin-project-lite" -version = "0.2.9" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] -name = "prettyplease" -version = "0.2.9" +name = "ppv-lite86" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9825a04601d60621feed79c4e6b56d65db77cdca55cef43b46b0de1096d1c282" -dependencies = [ - "proc-macro2", - "syn 2.0.45", -] +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "once_cell", "toml_edit", ] @@ -1221,7 +1301,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn 1.0.76", + "syn 1.0.109", "version_check", ] @@ -1238,9 +1318,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.74" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -1251,17 +1331,24 @@ version = "0.1.0" dependencies = [ "arbutil", "bincode", - "brotli2", + "brotli", "c-kzg", + "derivative", "digest 0.9.0", "eyre", "fnv", "hex", + "itertools", "lazy_static", "libc", + "lru", "nom", "nom-leb128", "num", + "num-derive", + "num-traits", + "once_cell", + "parking_lot", "rayon", "rustc-demangle", "serde", @@ -1272,7 +1359,10 @@ dependencies = [ "smallvec", "static_assertions", "structopt", - "wasmparser 0.84.0", + "wasmer", + "wasmer-compiler-singlepass", + "wasmer-types", + "wasmparser", "wat", ] @@ -1293,32 +1383,53 @@ checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ "proc-macro2", "quote", - "syn 1.0.76", + "syn 1.0.109", ] [[package]] name = "quote" -version = "1.0.34" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a37c9326af5ed140c86a46655b5278de879853be5573c01df185b6f49a580a" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] [[package]] name = "rand_pcg" @@ -1331,43 +1442,38 @@ dependencies = [ [[package]] name = "rayon" -version = "1.5.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "lazy_static", - "num_cpus", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.5.0", ] [[package]] name = "regalloc2" -version = "0.3.2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d43a209257d978ef079f3d446331d0f1794f5e0fc19b306a199983857833a779" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" dependencies = [ "fxhash", "log", @@ -1377,9 +1483,21 @@ dependencies = [ [[package]] name = "regex" -version = "1.6.0" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -1388,108 +1506,105 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "region" -version = "3.0.0" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" dependencies = [ "bitflags 1.3.2", "libc", - "mach", - "winapi", + "mach2", + "windows-sys 0.52.0", ] [[package]] name = "rend" -version = "0.3.6" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" dependencies = [ "bytecheck", ] [[package]] name = "rkyv" -version = "0.7.39" +version = "0.7.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" dependencies = [ + "bitvec", "bytecheck", + "bytes", "hashbrown 0.12.3", - "indexmap 1.8.1", + "indexmap 1.9.3", "ptr_meta", "rend", "rkyv_derive", "seahash", + "tinyvec", + "uuid", ] [[package]] name = "rkyv_derive" -version = "0.7.39" +version = "0.7.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" dependencies = [ "proc-macro2", "quote", - "syn 1.0.76", + "syn 1.0.109", ] [[package]] -name = "rustc-demangle" -version = "0.1.21" +name = "ruint2" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "b066b8e4fcea7fae86b6932d2449670b6b5545b7e8407841b2d3a916ff2a9f86" +dependencies = [ + "derive_more", + "ruint2-macro", + "rustc_version", + "thiserror", +] [[package]] -name = "rustc-hash" -version = "1.1.0" +name = "ruint2-macro" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "89dc553bc0cf4512a8b96caa2e21ed5f6e4b66bf28a1bd08fd9eb07c0b39b28c" [[package]] -name = "rustc_version" -version = "0.4.0" +name = "rustc-demangle" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver 1.0.13", -] +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] -name = "rustix" -version = "0.38.28" +name = "rustc_version" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "bitflags 2.4.1", - "errno", - "libc", - "linux-raw-sys", - "windows-sys 0.52.0", + "semver", ] -[[package]] -name = "rustversion" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" - [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "seahash" @@ -1498,34 +1613,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] -name = "semver" -version = "0.11.0" +name = "self_cell" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" -dependencies = [ - "semver-parser", -] +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" [[package]] name = "semver" -version = "1.0.13" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" - -[[package]] -name = "semver-parser" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" -dependencies = [ - "pest", -] +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] @@ -1543,20 +1646,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.45", + "syn 2.0.66", ] [[package]] name = "serde_json" -version = "1.0.109" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -1565,11 +1668,10 @@ dependencies = [ [[package]] name = "serde_with" -version = "1.12.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "946fa04a8ac43ff78a1f4b811990afb9ddbdf5890b46d6dda0ba1998230138b7" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ - "rustversion", "serde", "serde_with_macros", ] @@ -1580,10 +1682,10 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ - "darling", + "darling 0.13.4", "proc-macro2", "quote", - "syn 1.0.76", + "syn 1.0.109", ] [[package]] @@ -1593,7 +1695,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" dependencies = [ "block-buffer 0.9.0", - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.9.0", "opaque-debug", @@ -1605,7 +1707,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "cpufeatures", "digest 0.10.7", ] @@ -1633,22 +1735,38 @@ dependencies = [ ] [[package]] -name = "shlex" -version = "1.2.0" +name = "shared-buffer" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6c99835bad52957e7aa241d3975ed17c1e5f8c92026377d117a606f36b84b16" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "siphasher" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slice-group-by" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" [[package]] name = "smallvec" -version = "1.10.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" dependencies = [ "serde", ] @@ -1694,40 +1812,75 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ - "heck 0.3.3", + "heck", "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.76", + "syn 1.0.109", +] + +[[package]] +name = "stylus" +version = "0.1.0" +dependencies = [ + "arbutil", + "bincode", + "brotli", + "caller-env", + "derivative", + "eyre", + "fnv", + "hex", + "lazy_static", + "libc", + "lru", + "num-bigint", + "parking_lot", + "prover", + "rand", + "thiserror", + "user-host-trait", + "wasmer", + "wasmer-compiler-cranelift", + "wasmer-compiler-llvm", + "wasmer-compiler-singlepass", + "wasmer-types", + "wasmer-vm", ] [[package]] name = "syn" -version = "1.0.76" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] name = "syn" -version = "2.0.45" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eae3c679c56dc214320b67a1bc04ef3dfbd6411f6443974b5e4893231298e66" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "target-lexicon" -version = "0.12.4" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c02424087780c9b71cc96799eaeddff35af2bc513278cda5c99fc1f5d026d3c1" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" [[package]] name = "textwrap" @@ -1740,22 +1893,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.33" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0a539a918745651435ac7db7a18761589a94cd7e94cd56999f828bf73c8a57" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.33" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c251e90f708e16c49a16f4917dc2131e75222b72edfa9cb7f7c58ae56aae0c09" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 1.0.76", + "syn 2.0.66", ] [[package]] @@ -1767,30 +1920,53 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" [[package]] name = "toml_edit" -version = "0.19.12" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.0.0", + "indexmap 2.2.6", "toml_datetime", "winnow", ] [[package]] name = "tracing" -version = "0.1.34" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0ecdcb44a79f0fe9844f0c4f33a342cbcbb5117de8001e6ba0dc2351327d09" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1798,59 +1974,64 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.76", + "syn 2.0.66", ] [[package]] name = "tracing-core" -version = "0.1.26" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54c8ca710e81886d498c2fd3331b56c93aa248d49de2222ad2742247c60072f" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] name = "typenum" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" - -[[package]] -name = "ucd-trie" -version = "0.1.5" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" -version = "1.8.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" [[package]] -name = "unicode-xid" -version = "0.2.2" +name = "user-host-trait" +version = "0.1.0" +dependencies = [ + "arbutil", + "caller-env", + "eyre", + "prover", + "ruint2", +] + +[[package]] +name = "uuid" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" [[package]] name = "vec_map" @@ -1860,9 +2041,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" @@ -1872,57 +2053,34 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.76", + "syn 2.0.66", "wasm-bindgen-shared", ] -[[package]] -name = "wasm-bindgen-downcast" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dac026d43bcca6e7ce1c0956ba68f59edf6403e8e930a5d891be72c31a44340" -dependencies = [ - "js-sys", - "once_cell", - "wasm-bindgen", - "wasm-bindgen-downcast-macros", -] - -[[package]] -name = "wasm-bindgen-downcast-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5020cfa87c7cecefef118055d44e3c1fc122c7ec25701d528ee458a0b45f38f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.76", -] - [[package]] name = "wasm-bindgen-macro" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1930,49 +2088,50 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 1.0.76", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.83" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "wasm-encoder" -version = "0.25.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eff853c4f09eec94d76af527eddad4e9de13b11d6286a1ef7134bc30135a2b7" +checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" dependencies = [ "leb128", ] [[package]] name = "wasmer" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740f96c9e5d49f0056d716977657f3f7f8eea9923b41f46d1046946707aa038f" +version = "4.2.8" dependencies = [ "bytes", - "cfg-if", - "indexmap 1.8.1", + "cfg-if 1.0.0", + "derivative", + "indexmap 1.9.3", "js-sys", "more-asserts", + "rustc-demangle", "serde", "serde-wasm-bindgen", + "shared-buffer", "target-lexicon", "thiserror", + "tracing", "wasm-bindgen", - "wasm-bindgen-downcast", "wasmer-compiler", "wasmer-compiler-cranelift", "wasmer-derive", @@ -1984,38 +2143,37 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "001d072dd9823e5a06052621eadb531627b4a508d74b67da4590a3d5d9332dc8" +version = "4.2.8" dependencies = [ "backtrace", - "cfg-if", + "bytes", + "cfg-if 1.0.0", "enum-iterator", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", - "rustc-demangle", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", "wasmer-vm", - "wasmparser 0.83.0", + "wasmparser", "winapi", ] [[package]] name = "wasmer-compiler-cranelift" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2974856a7ce40eb033efc9db3d480845385c27079b6e33ce51751f2f3c67e9bd" +version = "4.2.8" dependencies = [ "cranelift-codegen", "cranelift-entity", "cranelift-frontend", - "gimli", + "gimli 0.26.2", "more-asserts", "rayon", "smallvec", @@ -2027,9 +2185,7 @@ dependencies = [ [[package]] name = "wasmer-compiler-llvm" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8354256545d5832658267b490948c8559dadaf6d60c5d3dde650acd84505624" +version = "4.2.8" dependencies = [ "byteorder", "cc", @@ -2041,7 +2197,7 @@ dependencies = [ "rayon", "regex", "rustc_version", - "semver 1.0.13", + "semver", "smallvec", "target-lexicon", "wasmer-compiler", @@ -2049,27 +2205,41 @@ dependencies = [ "wasmer-vm", ] +[[package]] +name = "wasmer-compiler-singlepass" +version = "4.2.8" +dependencies = [ + "byteorder", + "dynasm", + "dynasmrt", + "enumset", + "gimli 0.26.2", + "lazy_static", + "more-asserts", + "rayon", + "smallvec", + "wasmer-compiler", + "wasmer-types", +] + [[package]] name = "wasmer-derive" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36b23b52272494369a1f96428f0056425a85a66154610c988d971bbace8230f1" +version = "4.2.8" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 1.0.76", + "syn 1.0.109", ] [[package]] name = "wasmer-types" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bc6cd7a2d2d3bd901ff491f131188c1030694350685279e16e1233b9922846b" +version = "4.2.8" dependencies = [ + "bytecheck", "enum-iterator", "enumset", - "indexmap 1.8.1", + "indexmap 1.9.3", "more-asserts", "rkyv", "target-lexicon", @@ -2078,16 +2248,18 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67d0cd6c0ef4985d1ce9c7d7cccf34e910804417a230fa16ab7ee904efb4c34" +version = "4.2.8" dependencies = [ "backtrace", "cc", - "cfg-if", + "cfg-if 1.0.0", "corosensei", + "crossbeam-queue", + "dashmap", + "derivative", "enum-iterator", - "indexmap 1.8.1", + "fnv", + "indexmap 1.9.3", "lazy_static", "libc", "mach", @@ -2102,24 +2274,20 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.83.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" - -[[package]] -name = "wasmparser" -version = "0.84.0" +version = "0.121.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77dc97c22bb5ce49a47b745bed8812d30206eff5ef3af31424f2c1820c0974b2" +checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" dependencies = [ - "indexmap 1.8.1", + "bitflags 2.5.0", + "indexmap 2.2.6", + "semver", ] [[package]] name = "wast" -version = "55.0.0" +version = "64.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4984d3e1406571f4930ba5cf79bd70f75f41d0e87e17506e0bd19b0e5d085f05" +checksum = "a259b226fd6910225aa7baeba82f9d9933b6d00f2ce1b49b80fa4214328237cc" dependencies = [ "leb128", "memchr", @@ -2129,23 +2297,23 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.61" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af2b53f4da14db05d32e70e9c617abdf6620c575bd5dd972b7400037b4df2091" +checksum = "53253d920ab413fca1c7dc2161d601c79b4fdf631d0ba51dd4343bf9b556c3f6" dependencies = [ "wast", ] [[package]] -name = "which" -version = "4.4.2" +name = "wee_alloc" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" dependencies = [ - "either", - "home", - "once_cell", - "rustix", + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", ] [[package]] @@ -2183,78 +2351,36 @@ dependencies = [ "windows_x86_64_msvc 0.33.0", ] -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -2264,21 +2390,9 @@ checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -2288,21 +2402,15 @@ checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" [[package]] name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] -name = "windows_i686_gnu" -version = "0.52.0" +name = "windows_i686_gnullvm" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -2312,21 +2420,9 @@ checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" [[package]] name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -2336,33 +2432,15 @@ checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -2372,36 +2450,53 @@ checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" +name = "zerocopy" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +dependencies = [ + "zerocopy-derive", +] [[package]] -name = "winnow" -version = "0.4.7" +name = "zerocopy-derive" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0ace3845f0d96209f0375e6d367e3eb87eb65d27d445bdc9f1843a26f39448" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ - "memchr", + "proc-macro2", + "quote", + "syn 2.0.66", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -2414,5 +2509,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.45", + "syn 2.0.66", ] diff --git a/arbitrator/Cargo.toml b/arbitrator/Cargo.toml index c63a0b514b..138bdc2c69 100644 --- a/arbitrator/Cargo.toml +++ b/arbitrator/Cargo.toml @@ -1,9 +1,35 @@ [workspace] members = [ "arbutil", + "brotli", + "brotli/fuzz", + "caller-env", "prover", + "stylus", "jit", ] +exclude = [ + "stylus/tests/", + "tools/wasmer/", +] +resolver = "2" + +[workspace.package] +authors = ["Offchain Labs"] +edition = "2021" +homepage = "https://arbitrum.io" +license = "BSL" +repository = "https://github.com/OffchainLabs/nitro.git" +rust-version = "1.67" + +[workspace.dependencies] +cfg-if = "1.0.0" +lazy_static = "1.4.0" +lru = "0.12.3" +num_enum = { version = "0.7.2", default-features = false } +ruint2 = "1.9.0" +wasmparser = "0.121" +wee_alloc = "0.4.2" [profile.release] debug = true diff --git a/arbitrator/arbutil/Cargo.toml b/arbitrator/arbutil/Cargo.toml index cab0b22983..3fe1a9d134 100644 --- a/arbitrator/arbutil/Cargo.toml +++ b/arbitrator/arbutil/Cargo.toml @@ -5,6 +5,15 @@ edition = "2021" [dependencies] digest = "0.10.7" -num_enum = "0.7.0" +eyre = "0.6.5" +fnv = "1.0.7" +hex = "0.4.3" +num-traits = "0.2.17" +siphasher = "0.3.10" +tiny-keccak = { version = "2.0.2", features = ["keccak"] } +ruint2.workspace = true +wasmparser.workspace = true +serde = { version = "1.0.130", features = ["derive", "rc"] } +num_enum = "0.7.1" sha2 = "0.10.7" sha3 = "0.10.8" diff --git a/arbitrator/arbutil/src/color.rs b/arbitrator/arbutil/src/color.rs index c3b0745545..1ef6786a34 100644 --- a/arbitrator/arbutil/src/color.rs +++ b/arbitrator/arbutil/src/color.rs @@ -14,6 +14,7 @@ pub const RED: &str = "\x1b[31;1m"; pub const CLEAR: &str = "\x1b[0;0m"; pub const WHITE: &str = "\x1b[0;1m"; pub const YELLOW: &str = "\x1b[33;1m"; +pub const ORANGE: &str = "\x1b[38;5;202;1m"; pub trait Color { fn color(&self, color: &str) -> String; @@ -27,6 +28,7 @@ pub trait Color { fn red(&self) -> String; fn white(&self) -> String; fn yellow(&self) -> String; + fn orange(&self) -> String; } #[rustfmt::skip] @@ -45,6 +47,7 @@ impl Color for T where T: Display { fn red(&self) -> String { self.color(RED) } fn white(&self) -> String { self.color(WHITE) } fn yellow(&self) -> String { self.color(YELLOW) } + fn orange(&self) -> String { self.color(ORANGE) } } pub fn when(cond: bool, text: T, when_color: &str) -> String { @@ -66,6 +69,7 @@ pub trait DebugColor { fn debug_red(&self) -> String; fn debug_white(&self) -> String; fn debug_yellow(&self) -> String; + fn debug_orange(&self) -> String; } #[rustfmt::skip] @@ -84,4 +88,5 @@ impl DebugColor for T where T: Debug { fn debug_red(&self) -> String { self.debug_color(RED) } fn debug_white(&self) -> String { self.debug_color(WHITE) } fn debug_yellow(&self) -> String { self.debug_color(YELLOW) } + fn debug_orange(&self) -> String { self.debug_color(ORANGE) } } diff --git a/arbitrator/arbutil/src/crypto.rs b/arbitrator/arbutil/src/crypto.rs new file mode 100644 index 0000000000..3f5f57ca83 --- /dev/null +++ b/arbitrator/arbutil/src/crypto.rs @@ -0,0 +1,25 @@ +// Copyright 2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use siphasher::sip::SipHasher24; +use std::mem::MaybeUninit; +use tiny_keccak::{Hasher, Keccak}; + +pub fn keccak>(preimage: T) -> [u8; 32] { + let mut output = MaybeUninit::<[u8; 32]>::uninit(); + let mut hasher = Keccak::v256(); + hasher.update(preimage.as_ref()); + + // SAFETY: finalize() writes 32 bytes + unsafe { + hasher.finalize(&mut *output.as_mut_ptr()); + output.assume_init() + } +} + +pub fn siphash(preimage: &[u8], key: &[u8; 16]) -> u64 { + use std::hash::Hasher; + let mut hasher = SipHasher24::new_with_key(key); + hasher.write(preimage); + hasher.finish() +} diff --git a/arbitrator/arbutil/src/evm/api.rs b/arbitrator/arbutil/src/evm/api.rs new file mode 100644 index 0000000000..093e7f2984 --- /dev/null +++ b/arbitrator/arbutil/src/evm/api.rs @@ -0,0 +1,192 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{evm::user::UserOutcomeKind, Bytes20, Bytes32}; +use eyre::Result; +use num_enum::IntoPrimitive; +use std::sync::Arc; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, IntoPrimitive)] +#[repr(u8)] +pub enum EvmApiStatus { + Success, + Failure, + OutOfGas, + WriteProtection, +} + +impl From for EvmApiStatus { + fn from(value: u8) -> Self { + match value { + 0 => Self::Success, + 2 => Self::OutOfGas, + 3 => Self::WriteProtection, + _ => Self::Failure, + } + } +} + +#[derive(Clone, Copy, Debug)] +#[repr(u32)] +pub enum EvmApiMethod { + GetBytes32, + SetTrieSlots, + GetTransientBytes32, + SetTransientBytes32, + ContractCall, + DelegateCall, + StaticCall, + Create1, + Create2, + EmitLog, + AccountBalance, + AccountCode, + AccountCodeHash, + AddPages, + CaptureHostIO, +} + +/// This offset is added to EvmApiMethod when sending a request +/// in WASM - program done is also indicated by a "request", with the +/// id below that offset, indicating program status +pub const EVM_API_METHOD_REQ_OFFSET: u32 = 0x10000000; + +/// Copies data from Go into Rust. +/// Note: clone should not clone actual data, just the reader. +pub trait DataReader: Clone + Send + 'static { + fn slice(&self) -> &[u8]; +} + +/// Simple implementation for `DataReader`, in case data comes from a `Vec`. +#[derive(Clone, Debug)] +pub struct VecReader(Arc>); + +impl VecReader { + pub fn new(data: Vec) -> Self { + Self(Arc::new(data)) + } +} + +impl DataReader for VecReader { + fn slice(&self) -> &[u8] { + self.0.as_slice() + } +} + +pub trait EvmApi: Send + 'static { + /// Reads the 32-byte value in the EVM state trie at offset `key`. + /// Returns the value and the access cost in gas. + /// Analogous to `vm.SLOAD`. + fn get_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64); + + /// Stores the given value at the given key in Stylus VM's cache of the EVM state trie. + /// Note that the actual values only get written after calls to `set_trie_slots`. + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64; + + /// Persists any dirty values in the storage cache to the EVM state trie, dropping the cache entirely if requested. + /// Analogous to repeated invocations of `vm.SSTORE`. + fn flush_storage_cache(&mut self, clear: bool, gas_left: u64) -> Result; + + /// Reads the 32-byte value in the EVM's transient state trie at offset `key`. + /// Analogous to `vm.TLOAD`. + fn get_transient_bytes32(&mut self, key: Bytes32) -> Bytes32; + + /// Writes the 32-byte value in the EVM's transient state trie at offset `key`. + /// Analogous to `vm.TSTORE`. + fn set_transient_bytes32(&mut self, key: Bytes32, value: Bytes32) -> Result<()>; + + /// Calls the contract at the given address. + /// Returns the EVM return data's length, the gas cost, and whether the call succeeded. + /// Analogous to `vm.CALL`. + fn contract_call( + &mut self, + contract: Bytes20, + calldata: &[u8], + gas_left: u64, + gas_req: u64, + value: Bytes32, + ) -> (u32, u64, UserOutcomeKind); + + /// Delegate-calls the contract at the given address. + /// Returns the EVM return data's length, the gas cost, and whether the call succeeded. + /// Analogous to `vm.DELEGATECALL`. + fn delegate_call( + &mut self, + contract: Bytes20, + calldata: &[u8], + gas_left: u64, + gas_req: u64, + ) -> (u32, u64, UserOutcomeKind); + + /// Static-calls the contract at the given address. + /// Returns the EVM return data's length, the gas cost, and whether the call succeeded. + /// Analogous to `vm.STATICCALL`. + fn static_call( + &mut self, + contract: Bytes20, + calldata: &[u8], + gas_left: u64, + gas_req: u64, + ) -> (u32, u64, UserOutcomeKind); + + /// Deploys a new contract using the init code provided. + /// Returns the new contract's address on success, or the error reason on failure. + /// In both cases the EVM return data's length and the overall gas cost are returned too. + /// Analogous to `vm.CREATE`. + fn create1( + &mut self, + code: Vec, + endowment: Bytes32, + gas: u64, + ) -> (eyre::Result, u32, u64); + + /// Deploys a new contract using the init code provided, with an address determined in part by the `salt`. + /// Returns the new contract's address on success, or the error reason on failure. + /// In both cases the EVM return data's length and the overall gas cost are returned too. + /// Analogous to `vm.CREATE2`. + fn create2( + &mut self, + code: Vec, + endowment: Bytes32, + salt: Bytes32, + gas: u64, + ) -> (eyre::Result, u32, u64); + + /// Returns the EVM return data. + /// Analogous to `vm.RETURNDATACOPY`. + fn get_return_data(&self) -> D; + + /// Emits an EVM log with the given number of topics and data, the first bytes of which should be the topic data. + /// Returns an error message on failure. + /// Analogous to `vm.LOG(n)` where n ∈ [0, 4]. + fn emit_log(&mut self, data: Vec, topics: u32) -> Result<()>; + + /// Gets the balance of the given account. + /// Returns the balance and the access cost in gas. + /// Analogous to `vm.BALANCE`. + fn account_balance(&mut self, address: Bytes20) -> (Bytes32, u64); + + /// Returns the code and the access cost in gas. + /// Analogous to `vm.EXTCODECOPY`. + fn account_code(&mut self, address: Bytes20, gas_left: u64) -> (D, u64); + + /// Gets the hash of the given address's code. + /// Returns the hash and the access cost in gas. + /// Analogous to `vm.EXTCODEHASH`. + fn account_codehash(&mut self, address: Bytes20) -> (Bytes32, u64); + + /// Determines the cost in gas of allocating additional wasm pages. + /// Note: has the side effect of updating Geth's memory usage tracker. + /// Not analogous to any EVM opcode. + fn add_pages(&mut self, pages: u16) -> u64; + + /// Captures tracing information for hostio invocations during native execution. + fn capture_hostio( + &mut self, + name: &str, + args: &[u8], + outs: &[u8], + start_ink: u64, + end_ink: u64, + ); +} diff --git a/arbitrator/arbutil/src/evm/mod.rs b/arbitrator/arbutil/src/evm/mod.rs new file mode 100644 index 0000000000..1671e67072 --- /dev/null +++ b/arbitrator/arbutil/src/evm/mod.rs @@ -0,0 +1,101 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{Bytes20, Bytes32}; + +pub mod api; +pub mod req; +pub mod storage; +pub mod user; + +// params.SstoreSentryGasEIP2200 +pub const SSTORE_SENTRY_GAS: u64 = 2300; + +// params.ColdAccountAccessCostEIP2929 +pub const COLD_ACCOUNT_GAS: u64 = 2600; + +// params.ColdSloadCostEIP2929 +pub const COLD_SLOAD_GAS: u64 = 2100; + +// params.WarmStorageReadCostEIP2929 +pub const WARM_SLOAD_GAS: u64 = 100; + +// params.WarmStorageReadCostEIP2929 (see enable1153 in jump_table.go) +pub const TLOAD_GAS: u64 = WARM_SLOAD_GAS; +pub const TSTORE_GAS: u64 = WARM_SLOAD_GAS; + +// params.LogGas and params.LogDataGas +pub const LOG_TOPIC_GAS: u64 = 375; +pub const LOG_DATA_GAS: u64 = 8; + +// params.CopyGas +pub const COPY_WORD_GAS: u64 = 3; + +// params.Keccak256Gas +pub const KECCAK_256_GAS: u64 = 30; +pub const KECCAK_WORD_GAS: u64 = 6; + +// vm.GasQuickStep (see gas.go) +pub const GAS_QUICK_STEP: u64 = 2; + +// vm.GasQuickStep (see jump_table.go) +pub const ADDRESS_GAS: u64 = GAS_QUICK_STEP; + +// vm.GasQuickStep (see eips.go) +pub const BASEFEE_GAS: u64 = GAS_QUICK_STEP; + +// vm.GasQuickStep (see eips.go) +pub const CHAINID_GAS: u64 = GAS_QUICK_STEP; + +// vm.GasQuickStep (see jump_table.go) +pub const COINBASE_GAS: u64 = GAS_QUICK_STEP; + +// vm.GasQuickStep (see jump_table.go) +pub const GASLIMIT_GAS: u64 = GAS_QUICK_STEP; + +// vm.GasQuickStep (see jump_table.go) +pub const NUMBER_GAS: u64 = GAS_QUICK_STEP; + +// vm.GasQuickStep (see jump_table.go) +pub const TIMESTAMP_GAS: u64 = GAS_QUICK_STEP; + +// vm.GasQuickStep (see jump_table.go) +pub const GASLEFT_GAS: u64 = GAS_QUICK_STEP; + +// vm.GasQuickStep (see jump_table.go) +pub const CALLER_GAS: u64 = GAS_QUICK_STEP; + +// vm.GasQuickStep (see jump_table.go) +pub const CALLVALUE_GAS: u64 = GAS_QUICK_STEP; + +// vm.GasQuickStep (see jump_table.go) +pub const GASPRICE_GAS: u64 = GAS_QUICK_STEP; + +// vm.GasQuickStep (see jump_table.go) +pub const ORIGIN_GAS: u64 = GAS_QUICK_STEP; + +#[derive(Clone, Copy, Debug, Default)] +#[repr(C)] +pub struct EvmData { + pub block_basefee: Bytes32, + pub chainid: u64, + pub block_coinbase: Bytes20, + pub block_gas_limit: u64, + pub block_number: u64, + pub block_timestamp: u64, + pub contract_address: Bytes20, + pub module_hash: Bytes32, + pub msg_sender: Bytes20, + pub msg_value: Bytes32, + pub tx_gas_price: Bytes32, + pub tx_origin: Bytes20, + pub reentrant: u32, + pub return_data_len: u32, + pub cached: bool, + pub tracing: bool, +} + +/// Returns the minimum number of EVM words needed to store `bytes` bytes. +pub fn evm_words(bytes: u32) -> u32 { + crate::math::div_ceil::<32>(bytes as usize) as u32 +} diff --git a/arbitrator/arbutil/src/evm/req.rs b/arbitrator/arbutil/src/evm/req.rs new file mode 100644 index 0000000000..b1c8d99972 --- /dev/null +++ b/arbitrator/arbutil/src/evm/req.rs @@ -0,0 +1,301 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{ + evm::{ + api::{DataReader, EvmApi, EvmApiMethod, EvmApiStatus}, + storage::{StorageCache, StorageWord}, + user::UserOutcomeKind, + }, + format::Utf8OrHex, + pricing::EVM_API_INK, + Bytes20, Bytes32, +}; +use eyre::{bail, eyre, Result}; +use std::collections::hash_map::Entry; + +pub trait RequestHandler: Send + 'static { + fn request(&mut self, req_type: EvmApiMethod, req_data: impl AsRef<[u8]>) -> (Vec, D, u64); +} + +pub struct EvmApiRequestor> { + handler: H, + last_code: Option<(Bytes20, D)>, + last_return_data: Option, + storage_cache: StorageCache, +} + +impl> EvmApiRequestor { + pub fn new(handler: H) -> Self { + Self { + handler, + last_code: None, + last_return_data: None, + storage_cache: StorageCache::default(), + } + } + + fn request(&mut self, req_type: EvmApiMethod, req_data: impl AsRef<[u8]>) -> (Vec, D, u64) { + self.handler.request(req_type, req_data) + } + + /// Call out to a contract. + fn call_request( + &mut self, + call_type: EvmApiMethod, + contract: Bytes20, + input: &[u8], + gas_left: u64, + gas_req: u64, + value: Bytes32, + ) -> (u32, u64, UserOutcomeKind) { + let mut request = Vec::with_capacity(20 + 32 + 8 + 8 + input.len()); + request.extend(contract); + request.extend(value); + request.extend(gas_left.to_be_bytes()); + request.extend(gas_req.to_be_bytes()); + request.extend(input); + + let (res, data, cost) = self.request(call_type, &request); + let status: UserOutcomeKind = res[0].try_into().expect("unknown outcome"); + let data_len = data.slice().len() as u32; + self.last_return_data = Some(data); + (data_len, cost, status) + } + + pub fn request_handler(&mut self) -> &mut H { + &mut self.handler + } + + fn create_request( + &mut self, + create_type: EvmApiMethod, + code: Vec, + endowment: Bytes32, + salt: Option, + gas: u64, + ) -> (Result, u32, u64) { + let mut request = Vec::with_capacity(8 + 2 * 32 + code.len()); + request.extend(gas.to_be_bytes()); + request.extend(endowment); + if let Some(salt) = salt { + request.extend(salt); + } + request.extend(code); + + let (mut res, data, cost) = self.request(create_type, request); + if res.len() != 21 || res[0] == 0 { + if !res.is_empty() { + res.remove(0); + } + let err_string = String::from_utf8(res).unwrap_or("create_response_malformed".into()); + return (Err(eyre!(err_string)), 0, cost); + } + res.remove(0); + let address = res.try_into().unwrap(); + let data_len = data.slice().len() as u32; + self.last_return_data = Some(data); + (Ok(address), data_len, cost) + } +} + +impl> EvmApi for EvmApiRequestor { + fn get_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64) { + let cache = &mut self.storage_cache; + let mut cost = cache.read_gas(); + + let value = cache.entry(key).or_insert_with(|| { + let (res, _, gas) = self.handler.request(EvmApiMethod::GetBytes32, key); + cost = cost.saturating_add(gas).saturating_add(EVM_API_INK); + StorageWord::known(res.try_into().unwrap()) + }); + (value.value, cost) + } + + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64 { + let cost = self.storage_cache.write_gas(); + match self.storage_cache.entry(key) { + Entry::Occupied(mut key) => key.get_mut().value = value, + Entry::Vacant(slot) => drop(slot.insert(StorageWord::unknown(value))), + }; + cost + } + + fn flush_storage_cache(&mut self, clear: bool, gas_left: u64) -> Result { + let mut data = Vec::with_capacity(64 * self.storage_cache.len() + 8); + data.extend(gas_left.to_be_bytes()); + + for (key, value) in &mut self.storage_cache.slots { + if value.dirty() { + data.extend(*key); + data.extend(*value.value); + value.known = Some(value.value); + } + } + if clear { + self.storage_cache.clear(); + } + if data.len() == 8 { + return Ok(0); // no need to make request + } + + let (res, _, cost) = self.request(EvmApiMethod::SetTrieSlots, data); + if res[0] != EvmApiStatus::Success.into() { + bail!("{}", String::from_utf8_or_hex(res)); + } + Ok(cost) + } + + fn get_transient_bytes32(&mut self, key: Bytes32) -> Bytes32 { + let (res, ..) = self.request(EvmApiMethod::GetTransientBytes32, key); + res.try_into().unwrap() + } + + fn set_transient_bytes32(&mut self, key: Bytes32, value: Bytes32) -> Result<()> { + let mut data = Vec::with_capacity(64); + data.extend(key); + data.extend(value); + let (res, ..) = self.request(EvmApiMethod::SetTransientBytes32, data); + if res[0] != EvmApiStatus::Success.into() { + bail!("{}", String::from_utf8_or_hex(res)); + } + Ok(()) + } + + fn contract_call( + &mut self, + contract: Bytes20, + input: &[u8], + gas_left: u64, + gas_req: u64, + value: Bytes32, + ) -> (u32, u64, UserOutcomeKind) { + self.call_request( + EvmApiMethod::ContractCall, + contract, + input, + gas_left, + gas_req, + value, + ) + } + + fn delegate_call( + &mut self, + contract: Bytes20, + input: &[u8], + gas_left: u64, + gas_req: u64, + ) -> (u32, u64, UserOutcomeKind) { + self.call_request( + EvmApiMethod::DelegateCall, + contract, + input, + gas_left, + gas_req, + Bytes32::default(), + ) + } + + fn static_call( + &mut self, + contract: Bytes20, + input: &[u8], + gas_left: u64, + gas_req: u64, + ) -> (u32, u64, UserOutcomeKind) { + self.call_request( + EvmApiMethod::StaticCall, + contract, + input, + gas_left, + gas_req, + Bytes32::default(), + ) + } + + fn create1( + &mut self, + code: Vec, + endowment: Bytes32, + gas: u64, + ) -> (Result, u32, u64) { + self.create_request(EvmApiMethod::Create1, code, endowment, None, gas) + } + + fn create2( + &mut self, + code: Vec, + endowment: Bytes32, + salt: Bytes32, + gas: u64, + ) -> (Result, u32, u64) { + self.create_request(EvmApiMethod::Create2, code, endowment, Some(salt), gas) + } + + fn get_return_data(&self) -> D { + self.last_return_data.clone().expect("missing return data") + } + + fn emit_log(&mut self, data: Vec, topics: u32) -> Result<()> { + // TODO: remove copy + let mut request = Vec::with_capacity(4 + data.len()); + request.extend(topics.to_be_bytes()); + request.extend(data); + + let (res, _, _) = self.request(EvmApiMethod::EmitLog, request); + if !res.is_empty() { + bail!(String::from_utf8(res).unwrap_or("malformed emit-log response".into())) + } + Ok(()) + } + + fn account_balance(&mut self, address: Bytes20) -> (Bytes32, u64) { + let (res, _, cost) = self.request(EvmApiMethod::AccountBalance, address); + (res.try_into().unwrap(), cost) + } + + fn account_code(&mut self, address: Bytes20, gas_left: u64) -> (D, u64) { + if let Some((stored_address, data)) = self.last_code.as_ref() { + if address == *stored_address { + return (data.clone(), 0); + } + } + let mut req = Vec::with_capacity(20 + 8); + req.extend(address); + req.extend(gas_left.to_be_bytes()); + + let (_, data, cost) = self.request(EvmApiMethod::AccountCode, req); + self.last_code = Some((address, data.clone())); + (data, cost) + } + + fn account_codehash(&mut self, address: Bytes20) -> (Bytes32, u64) { + let (res, _, cost) = self.request(EvmApiMethod::AccountCodeHash, address); + (res.try_into().unwrap(), cost) + } + + fn add_pages(&mut self, pages: u16) -> u64 { + self.request(EvmApiMethod::AddPages, pages.to_be_bytes()).2 + } + + fn capture_hostio( + &mut self, + name: &str, + args: &[u8], + outs: &[u8], + start_ink: u64, + end_ink: u64, + ) { + let mut request = Vec::with_capacity(2 * 8 + 3 * 2 + name.len() + args.len() + outs.len()); + request.extend(start_ink.to_be_bytes()); + request.extend(end_ink.to_be_bytes()); + request.extend((name.len() as u16).to_be_bytes()); + request.extend((args.len() as u16).to_be_bytes()); + request.extend((outs.len() as u16).to_be_bytes()); + request.extend(name.as_bytes()); + request.extend(args); + request.extend(outs); + self.request(EvmApiMethod::CaptureHostIO, request); + } +} diff --git a/arbitrator/arbutil/src/evm/storage.rs b/arbitrator/arbutil/src/evm/storage.rs new file mode 100644 index 0000000000..32b60dd21b --- /dev/null +++ b/arbitrator/arbutil/src/evm/storage.rs @@ -0,0 +1,73 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use crate::Bytes32; +use fnv::FnvHashMap as HashMap; +use std::ops::{Deref, DerefMut}; + +/// Represents the EVM word at a given key. +#[derive(Debug)] +pub struct StorageWord { + /// The current value of the slot. + pub value: Bytes32, + /// The value in Geth, if known. + pub known: Option, +} + +impl StorageWord { + pub fn known(value: Bytes32) -> Self { + let known = Some(value); + Self { value, known } + } + + pub fn unknown(value: Bytes32) -> Self { + Self { value, known: None } + } + + pub fn dirty(&self) -> bool { + Some(self.value) != self.known + } +} + +#[derive(Default)] +pub struct StorageCache { + pub(crate) slots: HashMap, + reads: usize, + writes: usize, +} + +impl StorageCache { + pub const REQUIRED_ACCESS_GAS: u64 = 10; + + pub fn read_gas(&mut self) -> u64 { + self.reads += 1; + match self.reads { + 0..=32 => 0, + 33..=128 => 2, + _ => 10, + } + } + + pub fn write_gas(&mut self) -> u64 { + self.writes += 1; + match self.writes { + 0..=8 => 0, + 9..=64 => 7, + _ => 10, + } + } +} + +impl Deref for StorageCache { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.slots + } +} + +impl DerefMut for StorageCache { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.slots + } +} diff --git a/arbitrator/arbutil/src/evm/user.rs b/arbitrator/arbutil/src/evm/user.rs new file mode 100644 index 0000000000..c3673010ce --- /dev/null +++ b/arbitrator/arbutil/src/evm/user.rs @@ -0,0 +1,91 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use eyre::ErrReport; +use num_enum::{IntoPrimitive, TryFromPrimitive}; +use std::fmt::Display; + +#[derive(Debug)] +pub enum UserOutcome { + Success(Vec), + Revert(Vec), + Failure(ErrReport), + OutOfInk, + OutOfStack, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] +#[repr(u8)] +pub enum UserOutcomeKind { + Success, + Revert, + Failure, + OutOfInk, + OutOfStack, +} + +impl UserOutcome { + pub fn into_data(self) -> (UserOutcomeKind, Vec) { + let kind = self.kind(); + let data = match self { + Self::Success(out) => out, + Self::Revert(out) => out, + Self::Failure(err) => format!("{err:?}").as_bytes().to_vec(), + _ => vec![], + }; + (kind, data) + } + + pub fn kind(&self) -> UserOutcomeKind { + self.into() + } +} + +impl From<&UserOutcome> for UserOutcomeKind { + fn from(value: &UserOutcome) -> Self { + use UserOutcome::*; + match value { + Success(_) => Self::Success, + Revert(_) => Self::Revert, + Failure(_) => Self::Failure, + OutOfInk => Self::OutOfInk, + OutOfStack => Self::OutOfStack, + } + } +} + +impl From<&UserOutcome> for u8 { + fn from(value: &UserOutcome) -> Self { + UserOutcomeKind::from(value).into() + } +} + +impl Display for UserOutcome { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use UserOutcome::*; + match self { + Success(data) => write!(f, "success {}", hex::encode(data)), + Failure(err) => write!(f, "failure {:?}", err), + OutOfInk => write!(f, "out of ink"), + OutOfStack => write!(f, "out of stack"), + Revert(data) => { + let text = String::from_utf8(data.clone()).unwrap_or_else(|_| hex::encode(data)); + write!(f, "revert {text}") + } + } + } +} + +impl Display for UserOutcomeKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let as_u8 = *self as u8; + use UserOutcomeKind::*; + match self { + Success => write!(f, "success ({as_u8})"), + Revert => write!(f, "revert ({as_u8})"), + Failure => write!(f, "failure ({as_u8})"), + OutOfInk => write!(f, "out of ink ({as_u8})"), + OutOfStack => write!(f, "out of stack ({as_u8})"), + } + } +} diff --git a/arbitrator/arbutil/src/format.rs b/arbitrator/arbutil/src/format.rs index 2a5a04988f..de4c0968e8 100644 --- a/arbitrator/arbutil/src/format.rs +++ b/arbitrator/arbutil/src/format.rs @@ -1,8 +1,31 @@ -// Copyright 2022-2023, Offchain Labs, Inc. +// Copyright 2022, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE use crate::color::Color; -use std::fmt::Display; +use std::{ + fmt::{Debug, Display}, + time::Duration, +}; + +#[must_use] +pub fn time(span: Duration) -> String { + use crate::color::{MINT, RED, YELLOW}; + + let mut span = span.as_nanos() as f64; + let mut unit = 0; + let units = [ + "ns", "μs", "ms", "s", "min", "h", "d", "w", "mo", "yr", "dec", "cent", "mill", "eon", + ]; + let scale = [ + 1000., 1000., 1000., 60., 60., 24., 7., 4.34, 12., 10., 10., 10., 1_000_000., + ]; + let colors = [MINT, MINT, YELLOW, RED, RED, RED]; + while span >= scale[unit] && unit < scale.len() { + span /= scale[unit]; + unit += 1; + } + format!("{:6}", format!("{:.1}{}", span, units[unit])).color(colors[unit]) +} #[must_use] pub fn commas(items: U) -> String @@ -13,3 +36,30 @@ where let items: Vec<_> = items.into_iter().map(|x| format!("{x}")).collect(); items.join(&", ".grey()) } + +pub fn hex_fmt>(data: T, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(&hex::encode(data)) +} + +pub trait DebugBytes { + fn debug_bytes(self) -> Vec; +} + +impl DebugBytes for T { + fn debug_bytes(self) -> Vec { + format!("{:?}", self).as_bytes().to_vec() + } +} + +pub trait Utf8OrHex { + fn from_utf8_or_hex(data: impl Into>) -> String; +} + +impl Utf8OrHex for String { + fn from_utf8_or_hex(data: impl Into>) -> String { + match String::from_utf8(data.into()) { + Ok(string) => string, + Err(error) => hex::encode(error.as_bytes()), + } + } +} diff --git a/arbitrator/arbutil/src/lib.rs b/arbitrator/arbutil/src/lib.rs index aa748b84e8..9c48a9fefc 100644 --- a/arbitrator/arbutil/src/lib.rs +++ b/arbitrator/arbutil/src/lib.rs @@ -1,9 +1,50 @@ -// Copyright 2022-2023, Offchain Labs, Inc. +// Copyright 2022-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE +/// cbindgen:ignore pub mod color; +pub mod crypto; +pub mod evm; pub mod format; -mod types; +pub mod math; +pub mod operator; +pub mod pricing; +pub mod types; pub use color::{Color, DebugColor}; -pub use types::PreimageType; +use num_traits::Unsigned; +pub use types::{Bytes20, Bytes32, PreimageType}; + +/// Puts an arbitrary type on the heap. +/// Note: the type must be later freed or the value will be leaked. +pub fn heapify(value: T) -> *mut T { + Box::into_raw(Box::new(value)) +} + +/// Equivalent to &[start..offset], but truncates when out of bounds rather than panicking. +pub fn slice_with_runoff(data: &impl AsRef<[T]>, start: I, end: I) -> &[T] +where + I: TryInto + Unsigned, +{ + let start = start.try_into().unwrap_or(usize::MAX); + let end = end.try_into().unwrap_or(usize::MAX); + + let data = data.as_ref(); + if start >= data.len() || end < start { + return &[]; + } + &data[start..end.min(data.len())] +} + +#[test] +fn test_limit_vec() { + let testvec = vec![0, 1, 2, 3]; + assert_eq!(slice_with_runoff(&testvec, 4_u32, 4), &testvec[0..0]); + assert_eq!(slice_with_runoff(&testvec, 1_u16, 0), &testvec[0..0]); + assert_eq!(slice_with_runoff(&testvec, 0_u64, 0), &testvec[0..0]); + assert_eq!(slice_with_runoff(&testvec, 0_u32, 1), &testvec[0..1]); + assert_eq!(slice_with_runoff(&testvec, 1_u64, 3), &testvec[1..3]); + assert_eq!(slice_with_runoff(&testvec, 0_u16, 4), &testvec[0..4]); + assert_eq!(slice_with_runoff(&testvec, 0_u8, 5), &testvec[0..4]); + assert_eq!(slice_with_runoff(&testvec, 2, usize::MAX), &testvec[2..4]); +} diff --git a/arbitrator/arbutil/src/math.rs b/arbitrator/arbutil/src/math.rs new file mode 100644 index 0000000000..72d5025398 --- /dev/null +++ b/arbitrator/arbutil/src/math.rs @@ -0,0 +1,43 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use num_traits::{ops::saturating::SaturatingAdd, Zero}; +use std::ops::{BitAnd, Sub}; + +/// Checks if a number is a power of 2. +pub fn is_power_of_2(value: T) -> bool +where + T: Sub + BitAnd + PartialOrd + From + Copy, +{ + if value <= 0.into() { + return false; + } + value & (value - 1.into()) == 0.into() +} + +/// Calculates a sum, saturating in cases of overflow. +pub trait SaturatingSum { + type Number; + + fn saturating_sum(self) -> Self::Number; +} + +impl SaturatingSum for I +where + I: Iterator, + T: SaturatingAdd + Zero, +{ + type Number = T; + + fn saturating_sum(self) -> Self::Number { + self.fold(T::zero(), |acc, x| acc.saturating_add(&x)) + } +} + +/// Returns `num` divided by `N`, rounded up. +pub fn div_ceil(num: usize) -> usize { + match num % N { + 0 => num / N, + _ => num / N + 1, + } +} diff --git a/arbitrator/arbutil/src/operator.rs b/arbitrator/arbutil/src/operator.rs new file mode 100644 index 0000000000..cc1f684366 --- /dev/null +++ b/arbitrator/arbutil/src/operator.rs @@ -0,0 +1,1209 @@ +// Copyright 2021-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; +use std::hash::Hash; +use wasmparser::Operator; + +#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct OperatorCode(usize); + +impl OperatorCode { + // TODO: use std::mem::variant_count when it's stabilized + pub const OPERATOR_COUNT: usize = 529; +} + +impl Display for OperatorCode { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + let name = match self.0 { + 0x00 => "Unreachable", + 0x01 => "Nop", + 0x02 => "Block", + 0x03 => "Loop", + 0x04 => "If", + 0x05 => "Else", + 0x06 => "Try", + 0x07 => "Catch", + 0x08 => "Throw", + 0x09 => "Rethrow", + 0x0a => "ThrowRef", + 0x0b => "End", + 0x0c => "Br", + 0x0d => "BrIf", + 0x0e => "BrTable", + 0x0f => "Return", + 0x10 => "Call", + 0x11 => "CallIndirect", + 0x12 => "ReturnCall", + 0x13 => "ReturnCallIndirect", + 0x14 => "CallRef", + 0x15 => "ReturnCallRef", + 0x18 => "Delegate", + 0x19 => "CatchAll", + 0x1a => "Drop", + 0x1b => "Select", + 0x1c => "TypedSelect", + 0x1f => "TryTable", + 0x20 => "LocalGet", + 0x21 => "LocalSet", + 0x22 => "LocalTee", + 0x23 => "GlobalGet", + 0x24 => "GlobalSet", + 0x25 => "TableGet", + 0x26 => "TableSet", + 0x28 => "I32Load", + 0x29 => "I64Load", + 0x2a => "F32Load", + 0x2b => "F64Load", + 0x2c => "I32Load8S", + 0x2d => "I32Load8U", + 0x2e => "I32Load16S", + 0x2f => "I32Load16U", + 0x30 => "I64Load8S", + 0x31 => "I64Load8U", + 0x32 => "I64Load16S", + 0x33 => "I64Load16U", + 0x34 => "I64Load32S", + 0x35 => "I64Load32U", + 0x36 => "I32Store", + 0x37 => "I64Store", + 0x38 => "F32Store", + 0x39 => "F64Store", + 0x3a => "I32Store8", + 0x3b => "I32Store16", + 0x3c => "I64Store8", + 0x3d => "I64Store16", + 0x3e => "I64Store32", + 0x3f => "MemorySize", + 0x40 => "MemoryGrow", + 0x41 => "I32Const", + 0x42 => "I64Const", + 0x43 => "F32Const", + 0x44 => "F64Const", + 0x45 => "I32Eqz", + 0x46 => "I32Eq", + 0x47 => "I32Ne", + 0x48 => "I32LtS", + 0x49 => "I32LtU", + 0x4a => "I32GtS", + 0x4b => "I32GtU", + 0x4c => "I32LeS", + 0x4d => "I32LeU", + 0x4e => "I32GeS", + 0x4f => "I32GeU", + 0x50 => "I64Eqz", + 0x51 => "I64Eq", + 0x52 => "I64Ne", + 0x53 => "I64LtS", + 0x54 => "I64LtU", + 0x55 => "I64GtS", + 0x56 => "I64GtU", + 0x57 => "I64LeS", + 0x58 => "I64LeU", + 0x59 => "I64GeS", + 0x5a => "I64GeU", + 0x5b => "F32Eq", + 0x5c => "F32Ne", + 0x5d => "F32Lt", + 0x5e => "F32Gt", + 0x5f => "F32Le", + 0x60 => "F32Ge", + 0x61 => "F64Eq", + 0x62 => "F64Ne", + 0x63 => "F64Lt", + 0x64 => "F64Gt", + 0x65 => "F64Le", + 0x66 => "F64Ge", + 0x67 => "I32Clz", + 0x68 => "I32Ctz", + 0x69 => "I32Popcnt", + 0x6a => "I32Add", + 0x6b => "I32Sub", + 0x6c => "I32Mul", + 0x6d => "I32DivS", + 0x6e => "I32DivU", + 0x6f => "I32RemS", + 0x70 => "I32RemU", + 0x71 => "I32And", + 0x72 => "I32Or", + 0x73 => "I32Xor", + 0x74 => "I32Shl", + 0x75 => "I32ShrS", + 0x76 => "I32ShrU", + 0x77 => "I32Rotl", + 0x78 => "I32Rotr", + 0x79 => "I64Clz", + 0x7a => "I64Ctz", + 0x7b => "I64Popcnt", + 0x7c => "I64Add", + 0x7d => "I64Sub", + 0x7e => "I64Mul", + 0x7f => "I64DivS", + 0x80 => "I64DivU", + 0x81 => "I64RemS", + 0x82 => "I64RemU", + 0x83 => "I64And", + 0x84 => "I64Or", + 0x85 => "I64Xor", + 0x86 => "I64Shl", + 0x87 => "I64ShrS", + 0x88 => "I64ShrU", + 0x89 => "I64Rotl", + 0x8a => "I64Rotr", + 0x8b => "F32Abs", + 0x8c => "F32Neg", + 0x8d => "F32Ceil", + 0x8e => "F32Floor", + 0x8f => "F32Trunc", + 0x90 => "F32Nearest", + 0x91 => "F32Sqrt", + 0x92 => "F32Add", + 0x93 => "F32Sub", + 0x94 => "F32Mul", + 0x95 => "F32Div", + 0x96 => "F32Min", + 0x97 => "F32Max", + 0x98 => "F32Copysign", + 0x99 => "F64Abs", + 0x9a => "F64Neg", + 0x9b => "F64Ceil", + 0x9c => "F64Floor", + 0x9d => "F64Trunc", + 0x9e => "F64Nearest", + 0x9f => "F64Sqrt", + 0xa0 => "F64Add", + 0xa1 => "F64Sub", + 0xa2 => "F64Mul", + 0xa3 => "F64Div", + 0xa4 => "F64Min", + 0xa5 => "F64Max", + 0xa6 => "F64Copysign", + 0xa7 => "I32WrapI64", + 0xa8 => "I32TruncF32S", + 0xa9 => "I32TruncF32U", + 0xaa => "I32TruncF64S", + 0xab => "I32TruncF64U", + 0xac => "I64ExtendI32S", + 0xad => "I64ExtendI32U", + 0xae => "I64TruncF32S", + 0xaf => "I64TruncF32U", + 0xb0 => "I64TruncF64S", + 0xb1 => "I64TruncF64U", + 0xb2 => "F32ConvertI32S", + 0xb3 => "F32ConvertI32U", + 0xb4 => "F32ConvertI64S", + 0xb5 => "F32ConvertI64U", + 0xb6 => "F32DemoteF64", + 0xb7 => "F64ConvertI32S", + 0xb8 => "F64ConvertI32U", + 0xb9 => "F64ConvertI64S", + 0xba => "F64ConvertI64U", + 0xbb => "F64PromoteF32", + 0xbc => "I32ReinterpretF32", + 0xbd => "I64ReinterpretF64", + 0xbe => "F32ReinterpretI32", + 0xbf => "F64ReinterpretI64", + 0xc0 => "I32Extend8S", + 0xc1 => "I32Extend16S", + 0xc2 => "I64Extend8S", + 0xc3 => "I64Extend16S", + 0xc4 => "I64Extend32S", + 0xd0 => "RefNull", + 0xd1 => "RefIsNull", + 0xd2 => "RefFunc", + 0xd3 => "RefAsNonNull", + 0xd4 => "BrOnNull", + 0xd5 => "RefEq", + 0xd6 => "BrOnNonNull", + 0xfb00 => "StructNew", + 0xfb01 => "StructNewDefault", + 0xfb02 => "StructGet", + 0xfb03 => "StructGetS", + 0xfb04 => "StructGetU", + 0xfb05 => "StructSet", + 0xfb06 => "ArrayNew", + 0xfb07 => "ArrayNewDefault", + 0xfb08 => "ArrayNewFixed", + 0xfb09 => "ArrayNewData", + 0xfb0a => "ArrayNewElem", + 0xfb0b => "ArrayGet", + 0xfb0c => "ArrayGetS", + 0xfb0d => "ArrayGetU", + 0xfb0e => "ArraySet", + 0xfb0f => "ArrayLen", + 0xfb10 => "ArrayFill", + 0xfb11 => "ArrayCopy", + 0xfb12 => "ArrayInitData", + 0xfb13 => "ArrayInitElem", + 0xfb14 => "RefTestNonNull", + 0xfb15 => "RefTestNullable", + 0xfb16 => "RefCastNonNull", + 0xfb17 => "RefCastNullable", + 0xfb18 => "BrOnCast", + 0xfb19 => "BrOnCastFail", + 0xfb1a => "AnyConvertExtern", + 0xfb1b => "ExternConvertAny", + 0xfb1c => "RefI31", + 0xfb1d => "I31GetS", + 0xfb1e => "I31GetU", + 0xfc00 => "I32TruncSatF32S", + 0xfc01 => "I32TruncSatF32U", + 0xfc02 => "I32TruncSatF64S", + 0xfc03 => "I32TruncSatF64U", + 0xfc04 => "I64TruncSatF32S", + 0xfc05 => "I64TruncSatF32U", + 0xfc06 => "I64TruncSatF64S", + 0xfc07 => "I64TruncSatF64U", + 0xfc08 => "MemoryInit", + 0xfc09 => "DataDrop", + 0xfc0a => "MemoryCopy", + 0xfc0b => "MemoryFill", + 0xfc0c => "TableInit", + 0xfc0d => "ElemDrop", + 0xfc0e => "TableCopy", + 0xfc0f => "TableGrow", + 0xfc10 => "TableSize", + 0xfc11 => "TableFill", + 0xfc12 => "MemoryDiscard", + 0xfd00 => "V128Load", + 0xfd01 => "V128Load8x8S", + 0xfd02 => "V128Load8x8U", + 0xfd03 => "V128Load16x4S", + 0xfd04 => "V128Load16x4U", + 0xfd05 => "V128Load32x2S", + 0xfd06 => "V128Load32x2U", + 0xfd07 => "V128Load8Splat", + 0xfd08 => "V128Load16Splat", + 0xfd09 => "V128Load32Splat", + 0xfd0a => "V128Load64Splat", + 0xfd0b => "V128Store", + 0xfd0c => "V128Const", + 0xfd0d => "I8x16Shuffle", + 0xfd0e => "I8x16Swizzle", + 0xfd0f => "I8x16Splat", + 0xfd10 => "I16x8Splat", + 0xfd11 => "I32x4Splat", + 0xfd12 => "I64x2Splat", + 0xfd13 => "F32x4Splat", + 0xfd14 => "F64x2Splat", + 0xfd15 => "I8x16ExtractLaneS", + 0xfd16 => "I8x16ExtractLaneU", + 0xfd17 => "I8x16ReplaceLane", + 0xfd18 => "I16x8ExtractLaneS", + 0xfd19 => "I16x8ExtractLaneU", + 0xfd1a => "I16x8ReplaceLane", + 0xfd1b => "I32x4ExtractLane", + 0xfd1c => "I32x4ReplaceLane", + 0xfd1d => "I64x2ExtractLane", + 0xfd1e => "I64x2ReplaceLane", + 0xfd1f => "F32x4ExtractLane", + 0xfd20 => "F32x4ReplaceLane", + 0xfd21 => "F64x2ExtractLane", + 0xfd22 => "F64x2ReplaceLane", + 0xfd23 => "I8x16Eq", + 0xfd24 => "I8x16Ne", + 0xfd25 => "I8x16LtS", + 0xfd26 => "I8x16LtU", + 0xfd27 => "I8x16GtS", + 0xfd28 => "I8x16GtU", + 0xfd29 => "I8x16LeS", + 0xfd2a => "I8x16LeU", + 0xfd2b => "I8x16GeS", + 0xfd2c => "I8x16GeU", + 0xfd2d => "I16x8Eq", + 0xfd2e => "I16x8Ne", + 0xfd2f => "I16x8LtS", + 0xfd30 => "I16x8LtU", + 0xfd31 => "I16x8GtS", + 0xfd32 => "I16x8GtU", + 0xfd33 => "I16x8LeS", + 0xfd34 => "I16x8LeU", + 0xfd35 => "I16x8GeS", + 0xfd36 => "I16x8GeU", + 0xfd37 => "I32x4Eq", + 0xfd38 => "I32x4Ne", + 0xfd39 => "I32x4LtS", + 0xfd3a => "I32x4LtU", + 0xfd3b => "I32x4GtS", + 0xfd3c => "I32x4GtU", + 0xfd3d => "I32x4LeS", + 0xfd3e => "I32x4LeU", + 0xfd3f => "I32x4GeS", + 0xfd40 => "I32x4GeU", + 0xfd41 => "F32x4Eq", + 0xfd42 => "F32x4Ne", + 0xfd43 => "F32x4Lt", + 0xfd44 => "F32x4Gt", + 0xfd45 => "F32x4Le", + 0xfd46 => "F32x4Ge", + 0xfd47 => "F64x2Eq", + 0xfd48 => "F64x2Ne", + 0xfd49 => "F64x2Lt", + 0xfd4a => "F64x2Gt", + 0xfd4b => "F64x2Le", + 0xfd4c => "F64x2Ge", + 0xfd4d => "V128Not", + 0xfd4e => "V128And", + 0xfd4f => "V128AndNot", + 0xfd50 => "V128Or", + 0xfd51 => "V128Xor", + 0xfd52 => "V128Bitselect", + 0xfd53 => "V128AnyTrue", + 0xfd54 => "V128Load8Lane", + 0xfd55 => "V128Load16Lane", + 0xfd56 => "V128Load32Lane", + 0xfd57 => "V128Load64Lane", + 0xfd58 => "V128Store8Lane", + 0xfd59 => "V128Store16Lane", + 0xfd5a => "V128Store32Lane", + 0xfd5b => "V128Store64Lane", + 0xfd5c => "V128Load32Zero", + 0xfd5d => "V128Load64Zero", + 0xfd5e => "F32x4DemoteF64x2Zero", + 0xfd5f => "F64x2PromoteLowF32x4", + 0xfd60 => "I8x16Abs", + 0xfd61 => "I8x16Neg", + 0xfd62 => "I8x16Popcnt", + 0xfd63 => "I8x16AllTrue", + 0xfd64 => "I8x16Bitmask", + 0xfd65 => "I8x16NarrowI16x8S", + 0xfd66 => "I8x16NarrowI16x8U", + 0xfd67 => "F32x4Ceil", + 0xfd68 => "F32x4Floor", + 0xfd69 => "F32x4Trunc", + 0xfd6a => "F32x4Nearest", + 0xfd6b => "I8x16Shl", + 0xfd6c => "I8x16ShrS", + 0xfd6d => "I8x16ShrU", + 0xfd6e => "I8x16Add", + 0xfd6f => "I8x16AddSatS", + 0xfd70 => "I8x16AddSatU", + 0xfd71 => "I8x16Sub", + 0xfd72 => "I8x16SubSatS", + 0xfd73 => "I8x16SubSatU", + 0xfd74 => "F64x2Ceil", + 0xfd75 => "F64x2Floor", + 0xfd76 => "I8x16MinS", + 0xfd77 => "I8x16MinU", + 0xfd78 => "I8x16MaxS", + 0xfd79 => "I8x16MaxU", + 0xfd7a => "F64x2Trunc", + 0xfd7b => "I8x16AvgrU", + 0xfd7c => "I16x8ExtAddPairwiseI8x16S", + 0xfd7d => "I16x8ExtAddPairwiseI8x16U", + 0xfd7e => "I32x4ExtAddPairwiseI16x8S", + 0xfd7f => "I32x4ExtAddPairwiseI16x8U", + 0xfd80 => "I16x8Abs", + 0xfd81 => "I16x8Neg", + 0xfd82 => "I16x8Q15MulrSatS", + 0xfd83 => "I16x8AllTrue", + 0xfd84 => "I16x8Bitmask", + 0xfd85 => "I16x8NarrowI32x4S", + 0xfd86 => "I16x8NarrowI32x4U", + 0xfd87 => "I16x8ExtendLowI8x16S", + 0xfd88 => "I16x8ExtendHighI8x16S", + 0xfd89 => "I16x8ExtendLowI8x16U", + 0xfd8a => "I16x8ExtendHighI8x16U", + 0xfd8b => "I16x8Shl", + 0xfd8c => "I16x8ShrS", + 0xfd8d => "I16x8ShrU", + 0xfd8e => "I16x8Add", + 0xfd8f => "I16x8AddSatS", + 0xfd90 => "I16x8AddSatU", + 0xfd91 => "I16x8Sub", + 0xfd92 => "I16x8SubSatS", + 0xfd93 => "I16x8SubSatU", + 0xfd94 => "F64x2Nearest", + 0xfd95 => "I16x8Mul", + 0xfd96 => "I16x8MinS", + 0xfd97 => "I16x8MinU", + 0xfd98 => "I16x8MaxS", + 0xfd99 => "I16x8MaxU", + 0xfd9b => "I16x8AvgrU", + 0xfd9c => "I16x8ExtMulLowI8x16S", + 0xfd9d => "I16x8ExtMulHighI8x16S", + 0xfd9e => "I16x8ExtMulLowI8x16U", + 0xfd9f => "I16x8ExtMulHighI8x16U", + 0xfda0 => "I32x4Abs", + 0xfda2 => "I8x16RelaxedSwizzle", + 0xfda1 => "I32x4Neg", + 0xfda3 => "I32x4AllTrue", + 0xfda4 => "I32x4Bitmask", + 0xfda5 => "I32x4RelaxedTruncF32x4S", + 0xfda6 => "I32x4RelaxedTruncF32x4U", + 0xfda7 => "I32x4ExtendLowI16x8S", + 0xfda8 => "I32x4ExtendHighI16x8S", + 0xfda9 => "I32x4ExtendLowI16x8U", + 0xfdaa => "I32x4ExtendHighI16x8U", + 0xfdab => "I32x4Shl", + 0xfdac => "I32x4ShrS", + 0xfdad => "I32x4ShrU", + 0xfdae => "I32x4Add", + 0xfdaf => "F32x4RelaxedMadd", + 0xfdb0 => "F32x4RelaxedNmadd", + 0xfdb1 => "I32x4Sub", + 0xfdb2 => "I8x16RelaxedLaneselect", + 0xfdb3 => "I16x8RelaxedLaneselect", + 0xfdb4 => "F32x4RelaxedMin", + 0xfdb5 => "I32x4Mul", + 0xfdb6 => "I32x4MinS", + 0xfdb7 => "I32x4MinU", + 0xfdb8 => "I32x4MaxS", + 0xfdb9 => "I32x4MaxU", + 0xfdba => "I32x4DotI16x8S", + 0xfdbc => "I32x4ExtMulLowI16x8S", + 0xfdbd => "I32x4ExtMulHighI16x8S", + 0xfdbe => "I32x4ExtMulLowI16x8U", + 0xfdbf => "I32x4ExtMulHighI16x8U", + 0xfdc0 => "I64x2Abs", + 0xfdc1 => "I64x2Neg", + 0xfdc3 => "I64x2AllTrue", + 0xfdc4 => "I64x2Bitmask", + 0xfdc5 => "I32x4RelaxedTruncF64x2SZero", + 0xfdc6 => "I32x4RelaxedTruncF64x2UZero", + 0xfdc7 => "I64x2ExtendLowI32x4S", + 0xfdc8 => "I64x2ExtendHighI32x4S", + 0xfdc9 => "I64x2ExtendLowI32x4U", + 0xfdca => "I64x2ExtendHighI32x4U", + 0xfdcb => "I64x2Shl", + 0xfdcc => "I64x2ShrS", + 0xfdcd => "I64x2ShrU", + 0xfdce => "I64x2Add", + 0xfdcf => "F64x2RelaxedMadd", + 0xfdd0 => "F64x2RelaxedNmadd", + 0xfdd1 => "I64x2Sub", + 0xfdd2 => "I32x4RelaxedLaneselect", + 0xfdd3 => "I64x2RelaxedLaneselect", + 0xfdd4 => "F64x2RelaxedMin", + 0xfdd5 => "I64x2Mul", + 0xfdd6 => "I64x2Eq", + 0xfdd7 => "I64x2Ne", + 0xfdd8 => "I64x2LtS", + 0xfdd9 => "I64x2GtS", + 0xfdda => "I64x2LeS", + 0xfddb => "I64x2GeS", + 0xfddc => "I64x2ExtMulLowI32x4S", + 0xfddd => "I64x2ExtMulHighI32x4S", + 0xfdde => "I64x2ExtMulLowI32x4U", + 0xfddf => "I64x2ExtMulHighI32x4U", + 0xfde0 => "F32x4Abs", + 0xfde1 => "F32x4Neg", + 0xfde2 => "F32x4RelaxedMax", + 0xfde3 => "F32x4Sqrt", + 0xfde4 => "F32x4Add", + 0xfde5 => "F32x4Sub", + 0xfde6 => "F32x4Mul", + 0xfde7 => "F32x4Div", + 0xfde8 => "F32x4Min", + 0xfde9 => "F32x4Max", + 0xfdea => "F32x4PMin", + 0xfdeb => "F32x4PMax", + 0xfdec => "F64x2Abs", + 0xfded => "F64x2Neg", + 0xfdee => "F64x2RelaxedMax", + 0xfdef => "F64x2Sqrt", + 0xfdf0 => "F64x2Add", + 0xfdf1 => "F64x2Sub", + 0xfdf2 => "F64x2Mul", + 0xfdf3 => "F64x2Div", + 0xfdf4 => "F64x2Min", + 0xfdf5 => "F64x2Max", + 0xfdf6 => "F64x2PMin", + 0xfdf7 => "F64x2PMax", + 0xfdf8 => "I32x4TruncSatF32x4S", + 0xfdf9 => "I32x4TruncSatF32x4U", + 0xfdfa => "F32x4ConvertI32x4S", + 0xfdfb => "F32x4ConvertI32x4U", + 0xfdfc => "I32x4TruncSatF64x2SZero", + 0xfdfd => "I32x4TruncSatF64x2UZero", + 0xfdfe => "F64x2ConvertLowI32x4S", + 0xfdff => "F64x2ConvertLowI32x4U", + 0xfe00 => "MemoryAtomicNotify", + 0xfe01 => "MemoryAtomicWait32", + 0xfe02 => "MemoryAtomicWait64", + 0xfe03 => "AtomicFence", + 0xfe10 => "I32AtomicLoad", + 0xfe11 => "I64AtomicLoad", + 0xfe12 => "I32AtomicLoad8U", + 0xfe13 => "I32AtomicLoad16U", + 0xfe14 => "I64AtomicLoad8U", + 0xfe15 => "I64AtomicLoad16U", + 0xfe16 => "I64AtomicLoad32U", + 0xfe17 => "I32AtomicStore", + 0xfe18 => "I64AtomicStore", + 0xfe19 => "I32AtomicStore8", + 0xfe1a => "I32AtomicStore16", + 0xfe1b => "I64AtomicStore8", + 0xfe1c => "I64AtomicStore16", + 0xfe1d => "I64AtomicStore32", + 0xfe1e => "I32AtomicRmwAdd", + 0xfe1f => "I64AtomicRmwAdd", + 0xfe20 => "I32AtomicRmw8AddU", + 0xfe21 => "I32AtomicRmw16AddU", + 0xfe22 => "I64AtomicRmw8AddU", + 0xfe23 => "I64AtomicRmw16AddU", + 0xfe24 => "I64AtomicRmw32AddU", + 0xfe25 => "I32AtomicRmwSub", + 0xfe26 => "I64AtomicRmwSub", + 0xfe27 => "I32AtomicRmw8SubU", + 0xfe28 => "I32AtomicRmw16SubU", + 0xfe29 => "I64AtomicRmw8SubU", + 0xfe2a => "I64AtomicRmw16SubU", + 0xfe2b => "I64AtomicRmw32SubU", + 0xfe2c => "I32AtomicRmwAnd", + 0xfe2d => "I64AtomicRmwAnd", + 0xfe2e => "I32AtomicRmw8AndU", + 0xfe2f => "I32AtomicRmw16AndU", + 0xfe30 => "I64AtomicRmw8AndU", + 0xfe31 => "I64AtomicRmw16AndU", + 0xfe32 => "I64AtomicRmw32AndU", + 0xfe33 => "I32AtomicRmwOr", + 0xfe34 => "I64AtomicRmwOr", + 0xfe35 => "I32AtomicRmw8OrU", + 0xfe36 => "I32AtomicRmw16OrU", + 0xfe37 => "I64AtomicRmw8OrU", + 0xfe38 => "I64AtomicRmw16OrU", + 0xfe39 => "I64AtomicRmw32OrU", + 0xfe3a => "I32AtomicRmwXor", + 0xfe3b => "I64AtomicRmwXor", + 0xfe3c => "I32AtomicRmw8XorU", + 0xfe3d => "I32AtomicRmw16XorU", + 0xfe3e => "I64AtomicRmw8XorU", + 0xfe3f => "I64AtomicRmw16XorU", + 0xfe40 => "I64AtomicRmw32XorU", + 0xfe41 => "I32AtomicRmwXchg", + 0xfe42 => "I64AtomicRmwXchg", + 0xfe43 => "I32AtomicRmw8XchgU", + 0xfe44 => "I32AtomicRmw16XchgU", + 0xfe45 => "I64AtomicRmw8XchgU", + 0xfe46 => "I64AtomicRmw16XchgU", + 0xfe47 => "I64AtomicRmw32XchgU", + 0xfe48 => "I32AtomicRmwCmpxchg", + 0xfe49 => "I64AtomicRmwCmpxchg", + 0xfe4a => "I32AtomicRmw8CmpxchgU", + 0xfe4b => "I32AtomicRmw16CmpxchgU", + 0xfe4c => "I64AtomicRmw8CmpxchgU", + 0xfe4d => "I64AtomicRmw16CmpxchgU", + 0xfe4e => "I64AtomicRmw32CmpxchgU", + 0xfd111 => "I16x8RelaxedQ15mulrS", + 0xfd112 => "I16x8RelaxedDotI8x16I7x16S", + 0xfd113 => "I32x4RelaxedDotI8x16I7x16AddS", + _ => "UNKNOWN", + }; + write!(f, "{name}") + } +} + +impl<'a> From> for OperatorCode { + fn from(op: Operator) -> Self { + OperatorCode::from(&op) + } +} + +impl<'a> From<&Operator<'a>> for OperatorCode { + fn from(op: &Operator) -> Self { + use Operator as O; + + OperatorCode(match op { + O::Unreachable => 0x00, + O::Nop => 0x01, + O::Block { .. } => 0x02, + O::Loop { .. } => 0x03, + O::If { .. } => 0x04, + O::Else => 0x05, + O::Try { .. } => 0x06, + O::Catch { .. } => 0x07, + O::Throw { .. } => 0x08, + O::Rethrow { .. } => 0x09, + O::ThrowRef { .. } => 0x0A, + O::End => 0x0b, + O::Br { .. } => 0x0c, + O::BrIf { .. } => 0x0d, + O::BrTable { .. } => 0x0e, + O::Return => 0x0f, + O::Call { .. } => 0x10, + O::CallIndirect { .. } => 0x11, + O::ReturnCall { .. } => 0x12, + O::ReturnCallIndirect { .. } => 0x13, + O::CallRef { .. } => 0x14, + O::ReturnCallRef { .. } => 0x15, + O::Delegate { .. } => 0x18, + O::CatchAll => 0x19, + O::Drop => 0x1a, + O::Select => 0x1b, + O::TypedSelect { .. } => 0x1c, + O::TryTable { .. } => 0x1f, + O::LocalGet { .. } => 0x20, + O::LocalSet { .. } => 0x21, + O::LocalTee { .. } => 0x22, + O::GlobalGet { .. } => 0x23, + O::GlobalSet { .. } => 0x24, + O::TableGet { .. } => 0x25, + O::TableSet { .. } => 0x26, + O::I32Load { .. } => 0x28, + O::I64Load { .. } => 0x29, + O::F32Load { .. } => 0x2a, + O::F64Load { .. } => 0x2b, + O::I32Load8S { .. } => 0x2c, + O::I32Load8U { .. } => 0x2d, + O::I32Load16S { .. } => 0x2e, + O::I32Load16U { .. } => 0x2f, + O::I64Load8S { .. } => 0x30, + O::I64Load8U { .. } => 0x31, + O::I64Load16S { .. } => 0x32, + O::I64Load16U { .. } => 0x33, + O::I64Load32S { .. } => 0x34, + O::I64Load32U { .. } => 0x35, + O::I32Store { .. } => 0x36, + O::I64Store { .. } => 0x37, + O::F32Store { .. } => 0x38, + O::F64Store { .. } => 0x39, + O::I32Store8 { .. } => 0x3a, + O::I32Store16 { .. } => 0x3b, + O::I64Store8 { .. } => 0x3c, + O::I64Store16 { .. } => 0x3d, + O::I64Store32 { .. } => 0x3e, + O::MemorySize { .. } => 0x3f, + O::MemoryGrow { .. } => 0x40, + O::I32Const { .. } => 0x41, + O::I64Const { .. } => 0x42, + O::F32Const { .. } => 0x43, + O::F64Const { .. } => 0x44, + O::I32Eqz => 0x45, + O::I32Eq => 0x46, + O::I32Ne => 0x47, + O::I32LtS => 0x48, + O::I32LtU => 0x49, + O::I32GtS => 0x4a, + O::I32GtU => 0x4b, + O::I32LeS => 0x4c, + O::I32LeU => 0x4d, + O::I32GeS => 0x4e, + O::I32GeU => 0x4f, + O::I64Eqz => 0x50, + O::I64Eq => 0x51, + O::I64Ne => 0x52, + O::I64LtS => 0x53, + O::I64LtU => 0x54, + O::I64GtS => 0x55, + O::I64GtU => 0x56, + O::I64LeS => 0x57, + O::I64LeU => 0x58, + O::I64GeS => 0x59, + O::I64GeU => 0x5a, + O::F32Eq => 0x5b, + O::F32Ne => 0x5c, + O::F32Lt => 0x5d, + O::F32Gt => 0x5e, + O::F32Le => 0x5f, + O::F32Ge => 0x60, + O::F64Eq => 0x61, + O::F64Ne => 0x62, + O::F64Lt => 0x63, + O::F64Gt => 0x64, + O::F64Le => 0x65, + O::F64Ge => 0x66, + O::I32Clz => 0x67, + O::I32Ctz => 0x68, + O::I32Popcnt => 0x69, + O::I32Add => 0x6a, + O::I32Sub => 0x6b, + O::I32Mul => 0x6c, + O::I32DivS => 0x6d, + O::I32DivU => 0x6e, + O::I32RemS => 0x6f, + O::I32RemU => 0x70, + O::I32And => 0x71, + O::I32Or => 0x72, + O::I32Xor => 0x73, + O::I32Shl => 0x74, + O::I32ShrS => 0x75, + O::I32ShrU => 0x76, + O::I32Rotl => 0x77, + O::I32Rotr => 0x78, + O::I64Clz => 0x79, + O::I64Ctz => 0x7a, + O::I64Popcnt => 0x7b, + O::I64Add => 0x7c, + O::I64Sub => 0x7d, + O::I64Mul => 0x7e, + O::I64DivS => 0x7f, + O::I64DivU => 0x80, + O::I64RemS => 0x81, + O::I64RemU => 0x82, + O::I64And => 0x83, + O::I64Or => 0x84, + O::I64Xor => 0x85, + O::I64Shl => 0x86, + O::I64ShrS => 0x87, + O::I64ShrU => 0x88, + O::I64Rotl => 0x89, + O::I64Rotr => 0x8a, + O::F32Abs => 0x8b, + O::F32Neg => 0x8c, + O::F32Ceil => 0x8d, + O::F32Floor => 0x8e, + O::F32Trunc => 0x8f, + O::F32Nearest => 0x90, + O::F32Sqrt => 0x91, + O::F32Add => 0x92, + O::F32Sub => 0x93, + O::F32Mul => 0x94, + O::F32Div => 0x95, + O::F32Min => 0x96, + O::F32Max => 0x97, + O::F32Copysign => 0x98, + O::F64Abs => 0x99, + O::F64Neg => 0x9a, + O::F64Ceil => 0x9b, + O::F64Floor => 0x9c, + O::F64Trunc => 0x9d, + O::F64Nearest => 0x9e, + O::F64Sqrt => 0x9f, + O::F64Add => 0xa0, + O::F64Sub => 0xa1, + O::F64Mul => 0xa2, + O::F64Div => 0xa3, + O::F64Min => 0xa4, + O::F64Max => 0xa5, + O::F64Copysign => 0xa6, + O::I32WrapI64 => 0xa7, + O::I32TruncF32S => 0xa8, + O::I32TruncF32U => 0xa9, + O::I32TruncF64S => 0xaa, + O::I32TruncF64U => 0xab, + O::I64ExtendI32S => 0xac, + O::I64ExtendI32U => 0xad, + O::I64TruncF32S => 0xae, + O::I64TruncF32U => 0xaf, + O::I64TruncF64S => 0xb0, + O::I64TruncF64U => 0xb1, + O::F32ConvertI32S => 0xb2, + O::F32ConvertI32U => 0xb3, + O::F32ConvertI64S => 0xb4, + O::F32ConvertI64U => 0xb5, + O::F32DemoteF64 => 0xb6, + O::F64ConvertI32S => 0xb7, + O::F64ConvertI32U => 0xb8, + O::F64ConvertI64S => 0xb9, + O::F64ConvertI64U => 0xba, + O::F64PromoteF32 => 0xbb, + O::I32ReinterpretF32 => 0xbc, + O::I64ReinterpretF64 => 0xbd, + O::F32ReinterpretI32 => 0xbe, + O::F64ReinterpretI64 => 0xbf, + O::I32Extend8S => 0xc0, + O::I32Extend16S => 0xc1, + O::I64Extend8S => 0xc2, + O::I64Extend16S => 0xc3, + O::I64Extend32S => 0xc4, + O::RefNull { .. } => 0xd0, + O::RefIsNull => 0xd1, + O::RefFunc { .. } => 0xd2, + O::RefAsNonNull => 0xd3, + O::BrOnNull { .. } => 0xd4, + O::RefEq { .. } => 0xd5, + O::BrOnNonNull { .. } => 0xd6, + O::StructNew { .. } => 0xfb00, + O::StructNewDefault { .. } => 0xfb01, + O::StructGet { .. } => 0xfb02, + O::StructGetS { .. } => 0xfb03, + O::StructGetU { .. } => 0xfb04, + O::StructSet { .. } => 0xfb05, + O::ArrayNew { .. } => 0xfb06, + O::ArrayNewDefault { .. } => 0xfb07, + O::ArrayNewFixed { .. } => 0xfb08, + O::ArrayNewData { .. } => 0xfb09, + O::ArrayNewElem { .. } => 0xfb0a, + O::ArrayGet { .. } => 0xfb0b, + O::ArrayGetS { .. } => 0xfb0c, + O::ArrayGetU { .. } => 0xfb0d, + O::ArraySet { .. } => 0xfb0e, + O::ArrayLen { .. } => 0xfb0f, + O::ArrayFill { .. } => 0xfb10, + O::ArrayCopy { .. } => 0xfb11, + O::ArrayInitData { .. } => 0xfb12, + O::ArrayInitElem { .. } => 0xfb13, + O::RefTestNonNull { .. } => 0xfb14, + O::RefTestNullable { .. } => 0xfb15, + O::RefCastNonNull { .. } => 0xfb16, + O::RefCastNullable { .. } => 0xfb17, + O::BrOnCast { .. } => 0xfb18, + O::BrOnCastFail { .. } => 0xfb19, + O::AnyConvertExtern => 0xfb1a, + O::ExternConvertAny => 0xfb1b, + O::RefI31 { .. } => 0xfb1c, + O::I31GetS => 0xfb1d, + O::I31GetU => 0xfb1e, + O::I32TruncSatF32S => 0xfc00, + O::I32TruncSatF32U => 0xfc01, + O::I32TruncSatF64S => 0xfc02, + O::I32TruncSatF64U => 0xfc03, + O::I64TruncSatF32S => 0xfc04, + O::I64TruncSatF32U => 0xfc05, + O::I64TruncSatF64S => 0xfc06, + O::I64TruncSatF64U => 0xfc07, + O::MemoryInit { .. } => 0xfc08, + O::DataDrop { .. } => 0xfc09, + O::MemoryCopy { .. } => 0xfc0a, + O::MemoryFill { .. } => 0xfc0b, + O::TableInit { .. } => 0xfc0c, + O::ElemDrop { .. } => 0xfc0d, + O::TableCopy { .. } => 0xfc0e, + O::TableGrow { .. } => 0xfc0f, + O::TableSize { .. } => 0xfc10, + O::TableFill { .. } => 0xfc11, + O::MemoryDiscard { .. } => 0xfc12, + O::V128Load { .. } => 0xfd00, + O::V128Load8x8S { .. } => 0xfd01, + O::V128Load8x8U { .. } => 0xfd02, + O::V128Load16x4S { .. } => 0xfd03, + O::V128Load16x4U { .. } => 0xfd04, + O::V128Load32x2S { .. } => 0xfd05, + O::V128Load32x2U { .. } => 0xfd06, + O::V128Load8Splat { .. } => 0xfd07, + O::V128Load16Splat { .. } => 0xfd08, + O::V128Load32Splat { .. } => 0xfd09, + O::V128Load64Splat { .. } => 0xfd0a, + O::V128Store { .. } => 0xfd0b, + O::V128Const { .. } => 0xfd0c, + O::I8x16Shuffle { .. } => 0xfd0d, + O::I8x16Swizzle => 0xfd0e, + O::I8x16Splat => 0xfd0f, + O::I16x8Splat => 0xfd10, + O::I32x4Splat => 0xfd11, + O::I64x2Splat => 0xfd12, + O::F32x4Splat => 0xfd13, + O::F64x2Splat => 0xfd14, + O::I8x16ExtractLaneS { .. } => 0xfd15, + O::I8x16ExtractLaneU { .. } => 0xfd16, + O::I8x16ReplaceLane { .. } => 0xfd17, + O::I16x8ExtractLaneS { .. } => 0xfd18, + O::I16x8ExtractLaneU { .. } => 0xfd19, + O::I16x8ReplaceLane { .. } => 0xfd1a, + O::I32x4ExtractLane { .. } => 0xfd1b, + O::I32x4ReplaceLane { .. } => 0xfd1c, + O::I64x2ExtractLane { .. } => 0xfd1d, + O::I64x2ReplaceLane { .. } => 0xfd1e, + O::F32x4ExtractLane { .. } => 0xfd1f, + O::F32x4ReplaceLane { .. } => 0xfd20, + O::F64x2ExtractLane { .. } => 0xfd21, + O::F64x2ReplaceLane { .. } => 0xfd22, + O::I8x16Eq => 0xfd23, + O::I8x16Ne => 0xfd24, + O::I8x16LtS => 0xfd25, + O::I8x16LtU => 0xfd26, + O::I8x16GtS => 0xfd27, + O::I8x16GtU => 0xfd28, + O::I8x16LeS => 0xfd29, + O::I8x16LeU => 0xfd2a, + O::I8x16GeS => 0xfd2b, + O::I8x16GeU => 0xfd2c, + O::I16x8Eq => 0xfd2d, + O::I16x8Ne => 0xfd2e, + O::I16x8LtS => 0xfd2f, + O::I16x8LtU => 0xfd30, + O::I16x8GtS => 0xfd31, + O::I16x8GtU => 0xfd32, + O::I16x8LeS => 0xfd33, + O::I16x8LeU => 0xfd34, + O::I16x8GeS => 0xfd35, + O::I16x8GeU => 0xfd36, + O::I32x4Eq => 0xfd37, + O::I32x4Ne => 0xfd38, + O::I32x4LtS => 0xfd39, + O::I32x4LtU => 0xfd3a, + O::I32x4GtS => 0xfd3b, + O::I32x4GtU => 0xfd3c, + O::I32x4LeS => 0xfd3d, + O::I32x4LeU => 0xfd3e, + O::I32x4GeS => 0xfd3f, + O::I32x4GeU => 0xfd40, + O::F32x4Eq => 0xfd41, + O::F32x4Ne => 0xfd42, + O::F32x4Lt => 0xfd43, + O::F32x4Gt => 0xfd44, + O::F32x4Le => 0xfd45, + O::F32x4Ge => 0xfd46, + O::F64x2Eq => 0xfd47, + O::F64x2Ne => 0xfd48, + O::F64x2Lt => 0xfd49, + O::F64x2Gt => 0xfd4a, + O::F64x2Le => 0xfd4b, + O::F64x2Ge => 0xfd4c, + O::V128Not => 0xfd4d, + O::V128And => 0xfd4e, + O::V128AndNot => 0xfd4f, + O::V128Or => 0xfd50, + O::V128Xor => 0xfd51, + O::V128Bitselect => 0xfd52, + O::V128AnyTrue => 0xfd53, + O::V128Load8Lane { .. } => 0xfd54, + O::V128Load16Lane { .. } => 0xfd55, + O::V128Load32Lane { .. } => 0xfd56, + O::V128Load64Lane { .. } => 0xfd57, + O::V128Store8Lane { .. } => 0xfd58, + O::V128Store16Lane { .. } => 0xfd59, + O::V128Store32Lane { .. } => 0xfd5a, + O::V128Store64Lane { .. } => 0xfd5b, + O::V128Load32Zero { .. } => 0xfd5c, + O::V128Load64Zero { .. } => 0xfd5d, + O::F32x4DemoteF64x2Zero => 0xfd5e, + O::F64x2PromoteLowF32x4 => 0xfd5f, + O::I8x16Abs => 0xfd60, + O::I8x16Neg => 0xfd61, + O::I8x16Popcnt => 0xfd62, + O::I8x16AllTrue => 0xfd63, + O::I8x16Bitmask => 0xfd64, + O::I8x16NarrowI16x8S => 0xfd65, + O::I8x16NarrowI16x8U => 0xfd66, + O::F32x4Ceil => 0xfd67, + O::F32x4Floor => 0xfd68, + O::F32x4Trunc => 0xfd69, + O::F32x4Nearest => 0xfd6a, + O::I8x16Shl => 0xfd6b, + O::I8x16ShrS => 0xfd6c, + O::I8x16ShrU => 0xfd6d, + O::I8x16Add => 0xfd6e, + O::I8x16AddSatS => 0xfd6f, + O::I8x16AddSatU => 0xfd70, + O::I8x16Sub => 0xfd71, + O::I8x16SubSatS => 0xfd72, + O::I8x16SubSatU => 0xfd73, + O::F64x2Ceil => 0xfd74, + O::F64x2Floor => 0xfd75, + O::I8x16MinS => 0xfd76, + O::I8x16MinU => 0xfd77, + O::I8x16MaxS => 0xfd78, + O::I8x16MaxU => 0xfd79, + O::F64x2Trunc => 0xfd7a, + O::I8x16AvgrU => 0xfd7b, + O::I16x8ExtAddPairwiseI8x16S => 0xfd7c, + O::I16x8ExtAddPairwiseI8x16U => 0xfd7d, + O::I32x4ExtAddPairwiseI16x8S => 0xfd7e, + O::I32x4ExtAddPairwiseI16x8U => 0xfd7f, + O::I16x8Abs => 0xfd80, + O::I16x8Neg => 0xfd81, + O::I16x8Q15MulrSatS => 0xfd82, + O::I16x8AllTrue => 0xfd83, + O::I16x8Bitmask => 0xfd84, + O::I16x8NarrowI32x4S => 0xfd85, + O::I16x8NarrowI32x4U => 0xfd86, + O::I16x8ExtendLowI8x16S => 0xfd87, + O::I16x8ExtendHighI8x16S => 0xfd88, + O::I16x8ExtendLowI8x16U => 0xfd89, + O::I16x8ExtendHighI8x16U => 0xfd8a, + O::I16x8Shl => 0xfd8b, + O::I16x8ShrS => 0xfd8c, + O::I16x8ShrU => 0xfd8d, + O::I16x8Add => 0xfd8e, + O::I16x8AddSatS => 0xfd8f, + O::I16x8AddSatU => 0xfd90, + O::I16x8Sub => 0xfd91, + O::I16x8SubSatS => 0xfd92, + O::I16x8SubSatU => 0xfd93, + O::F64x2Nearest => 0xfd94, + O::I16x8Mul => 0xfd95, + O::I16x8MinS => 0xfd96, + O::I16x8MinU => 0xfd97, + O::I16x8MaxS => 0xfd98, + O::I16x8MaxU => 0xfd99, + O::I16x8AvgrU => 0xfd9b, + O::I16x8ExtMulLowI8x16S => 0xfd9c, + O::I16x8ExtMulHighI8x16S => 0xfd9d, + O::I16x8ExtMulLowI8x16U => 0xfd9e, + O::I16x8ExtMulHighI8x16U => 0xfd9f, + O::I32x4Abs => 0xfda0, + O::I8x16RelaxedSwizzle => 0xfda2, + O::I32x4Neg => 0xfda1, + O::I32x4AllTrue => 0xfda3, + O::I32x4Bitmask => 0xfda4, + O::I32x4RelaxedTruncF32x4S => 0xfda5, + O::I32x4RelaxedTruncF32x4U => 0xfda6, + O::I32x4ExtendLowI16x8S => 0xfda7, + O::I32x4ExtendHighI16x8S => 0xfda8, + O::I32x4ExtendLowI16x8U => 0xfda9, + O::I32x4ExtendHighI16x8U => 0xfdaa, + O::I32x4Shl => 0xfdab, + O::I32x4ShrS => 0xfdac, + O::I32x4ShrU => 0xfdad, + O::I32x4Add => 0xfdae, + O::F32x4RelaxedMadd => 0xfdaf, + O::F32x4RelaxedNmadd => 0xfdb0, + O::I32x4Sub => 0xfdb1, + O::I8x16RelaxedLaneselect => 0xfdb2, + O::I16x8RelaxedLaneselect => 0xfdb3, + O::F32x4RelaxedMin => 0xfdb4, + O::I32x4Mul => 0xfdb5, + O::I32x4MinS => 0xfdb6, + O::I32x4MinU => 0xfdb7, + O::I32x4MaxS => 0xfdb8, + O::I32x4MaxU => 0xfdb9, + O::I32x4DotI16x8S => 0xfdba, + O::I32x4ExtMulLowI16x8S => 0xfdbc, + O::I32x4ExtMulHighI16x8S => 0xfdbd, + O::I32x4ExtMulLowI16x8U => 0xfdbe, + O::I32x4ExtMulHighI16x8U => 0xfdbf, + O::I64x2Abs => 0xfdc0, + O::I64x2Neg => 0xfdc1, + O::I64x2AllTrue => 0xfdc3, + O::I64x2Bitmask => 0xfdc4, + O::I32x4RelaxedTruncF64x2SZero => 0xfdc5, + O::I32x4RelaxedTruncF64x2UZero => 0xfdc6, + O::I64x2ExtendLowI32x4S => 0xfdc7, + O::I64x2ExtendHighI32x4S => 0xfdc8, + O::I64x2ExtendLowI32x4U => 0xfdc9, + O::I64x2ExtendHighI32x4U => 0xfdca, + O::I64x2Shl => 0xfdcb, + O::I64x2ShrS => 0xfdcc, + O::I64x2ShrU => 0xfdcd, + O::I64x2Add => 0xfdce, + O::F64x2RelaxedMadd => 0xfdcf, + O::F64x2RelaxedNmadd => 0xfdd0, + O::I64x2Sub => 0xfdd1, + O::I32x4RelaxedLaneselect => 0xfdd2, + O::I64x2RelaxedLaneselect => 0xfdd3, + O::F64x2RelaxedMin => 0xfdd4, + O::I64x2Mul => 0xfdd5, + O::I64x2Eq => 0xfdd6, + O::I64x2Ne => 0xfdd7, + O::I64x2LtS => 0xfdd8, + O::I64x2GtS => 0xfdd9, + O::I64x2LeS => 0xfdda, + O::I64x2GeS => 0xfddb, + O::I64x2ExtMulLowI32x4S => 0xfddc, + O::I64x2ExtMulHighI32x4S => 0xfddd, + O::I64x2ExtMulLowI32x4U => 0xfdde, + O::I64x2ExtMulHighI32x4U => 0xfddf, + O::F32x4Abs => 0xfde0, + O::F32x4Neg => 0xfde1, + O::F32x4RelaxedMax => 0xfde2, + O::F32x4Sqrt => 0xfde3, + O::F32x4Add => 0xfde4, + O::F32x4Sub => 0xfde5, + O::F32x4Mul => 0xfde6, + O::F32x4Div => 0xfde7, + O::F32x4Min => 0xfde8, + O::F32x4Max => 0xfde9, + O::F32x4PMin => 0xfdea, + O::F32x4PMax => 0xfdeb, + O::F64x2Abs => 0xfdec, + O::F64x2Neg => 0xfded, + O::F64x2RelaxedMax => 0xfdee, + O::F64x2Sqrt => 0xfdef, + O::F64x2Add => 0xfdf0, + O::F64x2Sub => 0xfdf1, + O::F64x2Mul => 0xfdf2, + O::F64x2Div => 0xfdf3, + O::F64x2Min => 0xfdf4, + O::F64x2Max => 0xfdf5, + O::F64x2PMin => 0xfdf6, + O::F64x2PMax => 0xfdf7, + O::I32x4TruncSatF32x4S => 0xfdf8, + O::I32x4TruncSatF32x4U => 0xfdf9, + O::F32x4ConvertI32x4S => 0xfdfa, + O::F32x4ConvertI32x4U => 0xfdfb, + O::I32x4TruncSatF64x2SZero => 0xfdfc, + O::I32x4TruncSatF64x2UZero => 0xfdfd, + O::F64x2ConvertLowI32x4S => 0xfdfe, + O::F64x2ConvertLowI32x4U => 0xfdff, + O::MemoryAtomicNotify { .. } => 0xfe00, + O::MemoryAtomicWait32 { .. } => 0xfe01, + O::MemoryAtomicWait64 { .. } => 0xfe02, + O::AtomicFence { .. } => 0xfe03, + O::I32AtomicLoad { .. } => 0xfe10, + O::I64AtomicLoad { .. } => 0xfe11, + O::I32AtomicLoad8U { .. } => 0xfe12, + O::I32AtomicLoad16U { .. } => 0xfe13, + O::I64AtomicLoad8U { .. } => 0xfe14, + O::I64AtomicLoad16U { .. } => 0xfe15, + O::I64AtomicLoad32U { .. } => 0xfe16, + O::I32AtomicStore { .. } => 0xfe17, + O::I64AtomicStore { .. } => 0xfe18, + O::I32AtomicStore8 { .. } => 0xfe19, + O::I32AtomicStore16 { .. } => 0xfe1a, + O::I64AtomicStore8 { .. } => 0xfe1b, + O::I64AtomicStore16 { .. } => 0xfe1c, + O::I64AtomicStore32 { .. } => 0xfe1d, + O::I32AtomicRmwAdd { .. } => 0xfe1e, + O::I64AtomicRmwAdd { .. } => 0xfe1f, + O::I32AtomicRmw8AddU { .. } => 0xfe20, + O::I32AtomicRmw16AddU { .. } => 0xfe21, + O::I64AtomicRmw8AddU { .. } => 0xfe22, + O::I64AtomicRmw16AddU { .. } => 0xfe23, + O::I64AtomicRmw32AddU { .. } => 0xfe24, + O::I32AtomicRmwSub { .. } => 0xfe25, + O::I64AtomicRmwSub { .. } => 0xfe26, + O::I32AtomicRmw8SubU { .. } => 0xfe27, + O::I32AtomicRmw16SubU { .. } => 0xfe28, + O::I64AtomicRmw8SubU { .. } => 0xfe29, + O::I64AtomicRmw16SubU { .. } => 0xfe2a, + O::I64AtomicRmw32SubU { .. } => 0xfe2b, + O::I32AtomicRmwAnd { .. } => 0xfe2c, + O::I64AtomicRmwAnd { .. } => 0xfe2d, + O::I32AtomicRmw8AndU { .. } => 0xfe2e, + O::I32AtomicRmw16AndU { .. } => 0xfe2f, + O::I64AtomicRmw8AndU { .. } => 0xfe30, + O::I64AtomicRmw16AndU { .. } => 0xfe31, + O::I64AtomicRmw32AndU { .. } => 0xfe32, + O::I32AtomicRmwOr { .. } => 0xfe33, + O::I64AtomicRmwOr { .. } => 0xfe34, + O::I32AtomicRmw8OrU { .. } => 0xfe35, + O::I32AtomicRmw16OrU { .. } => 0xfe36, + O::I64AtomicRmw8OrU { .. } => 0xfe37, + O::I64AtomicRmw16OrU { .. } => 0xfe38, + O::I64AtomicRmw32OrU { .. } => 0xfe39, + O::I32AtomicRmwXor { .. } => 0xfe3a, + O::I64AtomicRmwXor { .. } => 0xfe3b, + O::I32AtomicRmw8XorU { .. } => 0xfe3c, + O::I32AtomicRmw16XorU { .. } => 0xfe3d, + O::I64AtomicRmw8XorU { .. } => 0xfe3e, + O::I64AtomicRmw16XorU { .. } => 0xfe3f, + O::I64AtomicRmw32XorU { .. } => 0xfe40, + O::I32AtomicRmwXchg { .. } => 0xfe41, + O::I64AtomicRmwXchg { .. } => 0xfe42, + O::I32AtomicRmw8XchgU { .. } => 0xfe43, + O::I32AtomicRmw16XchgU { .. } => 0xfe44, + O::I64AtomicRmw8XchgU { .. } => 0xfe45, + O::I64AtomicRmw16XchgU { .. } => 0xfe46, + O::I64AtomicRmw32XchgU { .. } => 0xfe47, + O::I32AtomicRmwCmpxchg { .. } => 0xfe48, + O::I64AtomicRmwCmpxchg { .. } => 0xfe49, + O::I32AtomicRmw8CmpxchgU { .. } => 0xfe4a, + O::I32AtomicRmw16CmpxchgU { .. } => 0xfe4b, + O::I64AtomicRmw8CmpxchgU { .. } => 0xfe4c, + O::I64AtomicRmw16CmpxchgU { .. } => 0xfe4d, + O::I64AtomicRmw32CmpxchgU { .. } => 0xfe4e, + O::I16x8RelaxedQ15mulrS { .. } => 0xfd111, + O::I16x8RelaxedDotI8x16I7x16S { .. } => 0xfd112, + O::I32x4RelaxedDotI8x16I7x16AddS { .. } => 0xfd113, + }) + } +} + +pub trait OperatorInfo { + fn ends_basic_block(&self) -> bool; + fn code(&self) -> OperatorCode; +} + +impl OperatorInfo for Operator<'_> { + fn ends_basic_block(&self) -> bool { + use Operator::*; + + macro_rules! dot { + ($first:ident $(,$opcode:ident)*) => { + $first { .. } $(| $opcode { .. })* + }; + } + + matches!( + self, + End | Else | Return | dot!(Loop, Br, BrTable, BrIf, If, Call, CallIndirect) + ) + } + + fn code(&self) -> OperatorCode { + self.into() + } +} diff --git a/arbitrator/arbutil/src/pricing.rs b/arbitrator/arbutil/src/pricing.rs new file mode 100644 index 0000000000..4614b02a2a --- /dev/null +++ b/arbitrator/arbutil/src/pricing.rs @@ -0,0 +1,20 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +/// For hostios that may return something. +pub const HOSTIO_INK: u64 = 8400; + +/// For hostios that include pointers. +pub const PTR_INK: u64 = 13440 - HOSTIO_INK; + +/// For hostios that involve an API cost. +pub const EVM_API_INK: u64 = 59673; + +/// For hostios that involve a div or mod. +pub const DIV_INK: u64 = 20000; + +/// For hostios that involve a mulmod. +pub const MUL_MOD_INK: u64 = 24100; + +/// For hostios that involve an addmod. +pub const ADD_MOD_INK: u64 = 21000; diff --git a/arbitrator/arbutil/src/types.rs b/arbitrator/arbutil/src/types.rs index 9b6cf46382..6cf1d6cdf7 100644 --- a/arbitrator/arbutil/src/types.rs +++ b/arbitrator/arbutil/src/types.rs @@ -2,6 +2,13 @@ // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE use num_enum::{IntoPrimitive, TryFromPrimitive}; +use ruint2::Uint; +use serde::{Deserialize, Serialize}; +use std::{ + borrow::Borrow, + fmt, + ops::{Deref, DerefMut}, +}; // These values must be kept in sync with `arbutil/preimage_type.go`, // and the if statement in `contracts/src/osp/OneStepProverHostIo.sol` (search for "UNKNOWN_PREIMAGE_TYPE"). @@ -14,3 +21,231 @@ pub enum PreimageType { Sha2_256, EthVersionedHash, } + +/// cbindgen:field-names=[bytes] +#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[repr(C)] +pub struct Bytes32(pub [u8; 32]); + +impl Deref for Bytes32 { + type Target = [u8; 32]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Bytes32 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsRef<[u8]> for Bytes32 { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Borrow<[u8]> for Bytes32 { + fn borrow(&self) -> &[u8] { + &self.0 + } +} + +impl From<[u8; 32]> for Bytes32 { + fn from(x: [u8; 32]) -> Self { + Self(x) + } +} + +impl From for Bytes32 { + fn from(x: u32) -> Self { + let mut b = [0u8; 32]; + b[(32 - 4)..].copy_from_slice(&x.to_be_bytes()); + Self(b) + } +} + +impl From for Bytes32 { + fn from(x: u64) -> Self { + let mut b = [0u8; 32]; + b[(32 - 8)..].copy_from_slice(&x.to_be_bytes()); + Self(b) + } +} + +impl From for Bytes32 { + fn from(x: usize) -> Self { + let mut b = [0u8; 32]; + b[(32 - (usize::BITS as usize / 8))..].copy_from_slice(&x.to_be_bytes()); + Self(b) + } +} + +impl TryFrom<&[u8]> for Bytes32 { + type Error = std::array::TryFromSliceError; + + fn try_from(value: &[u8]) -> Result { + let value: [u8; 32] = value.try_into()?; + Ok(Self(value)) + } +} + +impl TryFrom> for Bytes32 { + type Error = std::array::TryFromSliceError; + + fn try_from(value: Vec) -> Result { + Self::try_from(value.as_slice()) + } +} + +impl IntoIterator for Bytes32 { + type Item = u8; + type IntoIter = std::array::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIterator::into_iter(self.0) + } +} + +impl fmt::Display for Bytes32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self)) + } +} + +impl fmt::Debug for Bytes32 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self)) + } +} + +type GenericBytes32 = digest::generic_array::GenericArray; + +impl From for Bytes32 { + fn from(x: GenericBytes32) -> Self { + <[u8; 32]>::from(x).into() + } +} + +type U256 = Uint<256, 4>; + +impl From for U256 { + fn from(value: Bytes32) -> Self { + U256::from_be_bytes(value.0) + } +} + +impl From for Bytes32 { + fn from(value: U256) -> Self { + Self(value.to_be_bytes()) + } +} + +/// cbindgen:field-names=[bytes] +#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] +#[repr(C)] +pub struct Bytes20(pub [u8; 20]); + +impl Deref for Bytes20 { + type Target = [u8; 20]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for Bytes20 { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsRef<[u8]> for Bytes20 { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +impl Borrow<[u8]> for Bytes20 { + fn borrow(&self) -> &[u8] { + &self.0 + } +} + +impl From<[u8; 20]> for Bytes20 { + fn from(x: [u8; 20]) -> Self { + Self(x) + } +} + +impl From for Bytes20 { + fn from(x: u32) -> Self { + let mut b = [0u8; 20]; + b[(20 - 4)..].copy_from_slice(&x.to_be_bytes()); + Self(b) + } +} + +impl From for Bytes20 { + fn from(x: u64) -> Self { + let mut b = [0u8; 20]; + b[(20 - 8)..].copy_from_slice(&x.to_be_bytes()); + Self(b) + } +} + +impl From for Bytes20 { + fn from(x: usize) -> Self { + let mut b = [0u8; 20]; + b[(32 - (usize::BITS as usize / 8))..].copy_from_slice(&x.to_be_bytes()); + Self(b) + } +} + +impl TryFrom<&[u8]> for Bytes20 { + type Error = std::array::TryFromSliceError; + + fn try_from(value: &[u8]) -> Result { + let value: [u8; 20] = value.try_into()?; + Ok(Self(value)) + } +} + +impl TryFrom> for Bytes20 { + type Error = std::array::TryFromSliceError; + + fn try_from(value: Vec) -> Result { + Self::try_from(value.as_slice()) + } +} + +impl IntoIterator for Bytes20 { + type Item = u8; + type IntoIter = std::array::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + IntoIterator::into_iter(self.0) + } +} + +impl fmt::Display for Bytes20 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self)) + } +} + +impl fmt::Debug for Bytes20 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", hex::encode(self)) + } +} + +type GenericBytes20 = digest::generic_array::GenericArray; + +impl From for Bytes20 { + fn from(x: GenericBytes20) -> Self { + <[u8; 20]>::from(x).into() + } +} diff --git a/arbitrator/brotli/Cargo.toml b/arbitrator/brotli/Cargo.toml new file mode 100644 index 0000000000..7dba0ffdd8 --- /dev/null +++ b/arbitrator/brotli/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "brotli" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +lazy_static.workspace = true +num_enum.workspace = true +wasmer = { path = "../tools/wasmer/lib/api", optional = true } +wee_alloc.workspace = true + +[lib] +crate-type = ["lib"] + +[features] +wasmer_traits = ["dep:wasmer"] diff --git a/arbitrator/brotli/build.rs b/arbitrator/brotli/build.rs new file mode 100644 index 0000000000..8ed54397b9 --- /dev/null +++ b/arbitrator/brotli/build.rs @@ -0,0 +1,18 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use std::env; + +fn main() { + let target_arch = env::var("TARGET").unwrap(); + + if target_arch.contains("wasm32") { + println!("cargo:rustc-link-search=../../target/lib-wasm/"); + } else { + println!("cargo:rustc-link-search=../target/lib/"); + println!("cargo:rustc-link-search=../../target/lib/"); + } + println!("cargo:rustc-link-lib=static=brotlienc-static"); + println!("cargo:rustc-link-lib=static=brotlidec-static"); + println!("cargo:rustc-link-lib=static=brotlicommon-static"); +} diff --git a/arbitrator/brotli/fuzz/.gitignore b/arbitrator/brotli/fuzz/.gitignore new file mode 100644 index 0000000000..1a45eee776 --- /dev/null +++ b/arbitrator/brotli/fuzz/.gitignore @@ -0,0 +1,4 @@ +target +corpus +artifacts +coverage diff --git a/arbitrator/brotli/fuzz/Cargo.toml b/arbitrator/brotli/fuzz/Cargo.toml new file mode 100644 index 0000000000..2dc6993349 --- /dev/null +++ b/arbitrator/brotli/fuzz/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "brotli-fuzz" +version = "0.0.0" +publish = false +edition = "2021" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +libfuzzer-sys = "0.4" +hex = "0.4.3" + +[dependencies.brotli] +path = ".." + +[[bin]] +name = "compress" +path = "fuzz_targets/compress.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "decompress" +path = "fuzz_targets/decompress.rs" +test = false +doc = false +bench = false + +[[bin]] +name = "round-trip" +path = "fuzz_targets/round_trip.rs" +test = false +doc = false +bench = false diff --git a/arbitrator/brotli/fuzz/README b/arbitrator/brotli/fuzz/README new file mode 100644 index 0000000000..e00f4c3437 --- /dev/null +++ b/arbitrator/brotli/fuzz/README @@ -0,0 +1,13 @@ + +Fuzzing for brotli. You'll need `cargo-fuzz`. Install it with `cargo install +cargo-fuzz`. You'll also need to use the Rust nightly compiler - `rustup +default nightly`. + +Then you can fuzz with +```bash +cargo +nightly fuzz run compress -- -max_len=262144 +``` +or +```bash +cargo +nightly fuzz run decompress -- -max_len=262144 +``` diff --git a/arbitrator/brotli/fuzz/fuzz_targets/compress.rs b/arbitrator/brotli/fuzz/fuzz_targets/compress.rs new file mode 100644 index 0000000000..6141ede76e --- /dev/null +++ b/arbitrator/brotli/fuzz/fuzz_targets/compress.rs @@ -0,0 +1,18 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +#![no_main] + +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|arg: (&[u8], u32, u32)| { + let data = arg.0; + let quality = arg.1; + let window = arg.2; + let _ = brotli::compress( + data, + 1 + quality % 12, + 10 + window % 15, + brotli::Dictionary::StylusProgram, + ); +}); diff --git a/arbitrator/brotli/fuzz/fuzz_targets/decompress.rs b/arbitrator/brotli/fuzz/fuzz_targets/decompress.rs new file mode 100644 index 0000000000..dd36d6483f --- /dev/null +++ b/arbitrator/brotli/fuzz/fuzz_targets/decompress.rs @@ -0,0 +1,28 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +#![no_main] + +use brotli::Dictionary; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + let mut data = data; + let dict = Dictionary::StylusProgram; + + let mut space = 0_u32; + if data.len() >= 4 { + space = u32::from_le_bytes(data[..4].try_into().unwrap()); + data = &data[4..]; + } + + let mut array = Vec::with_capacity(space as usize % 65536); + let array = &mut array.spare_capacity_mut(); + + let plain = brotli::decompress(data, dict); + let fixed = brotli::decompress_fixed(data, array, dict); + + if let Ok(fixed) = fixed { + assert_eq!(fixed.len(), plain.unwrap().len()); // fixed succeeding implies both do + } +}); diff --git a/arbitrator/brotli/fuzz/fuzz_targets/round_trip.rs b/arbitrator/brotli/fuzz/fuzz_targets/round_trip.rs new file mode 100644 index 0000000000..2f47584cfd --- /dev/null +++ b/arbitrator/brotli/fuzz/fuzz_targets/round_trip.rs @@ -0,0 +1,23 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +#![no_main] + +use brotli::Dictionary; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + let dict = Dictionary::Empty; + let split = data + .first() + .map(|x| *x as usize) + .unwrap_or_default() + .min(data.len()); + + let (header, data) = data.split_at(split); + let image = brotli::compress_into(data, header.to_owned(), 0, 22, dict).unwrap(); + let prior = brotli::decompress(&image[split..], dict).unwrap(); + + assert_eq!(&image[..split], header); + assert_eq!(prior, data); +}); diff --git a/arbitrator/brotli/src/cgo.rs b/arbitrator/brotli/src/cgo.rs new file mode 100644 index 0000000000..3581d024f5 --- /dev/null +++ b/arbitrator/brotli/src/cgo.rs @@ -0,0 +1,66 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{BrotliStatus, Dictionary, DEFAULT_WINDOW_SIZE}; +use core::{mem::MaybeUninit, slice}; + +/// Mechanism for passing data between Go and Rust where Rust can specify the initialized length. +#[derive(Clone, Copy)] +#[repr(C)] +pub struct BrotliBuffer { + /// Points to data owned by Go. + ptr: *mut u8, + /// The length in bytes. Rust may mutate this value to indicate the number of bytes initialized. + len: *mut usize, +} + +impl BrotliBuffer { + /// Interprets the underlying Go data as a Rust slice. + fn as_slice(&self) -> &[u8] { + let len = unsafe { *self.len }; + if len == 0 { + return &[]; + } + unsafe { slice::from_raw_parts(self.ptr, len) } + } + + /// Interprets the underlying Go data as a Rust slice of uninitialized data. + fn as_uninit(&mut self) -> &mut [MaybeUninit] { + let len = unsafe { *self.len }; + if len == 0 { + return &mut []; + } + unsafe { slice::from_raw_parts_mut(self.ptr as _, len) } + } +} + +/// Brotli compresses the given Go data into a buffer of limited capacity. +#[no_mangle] +pub extern "C" fn brotli_compress( + input: BrotliBuffer, + mut output: BrotliBuffer, + dictionary: Dictionary, + level: u32, +) -> BrotliStatus { + let window = DEFAULT_WINDOW_SIZE; + let buffer = output.as_uninit(); + match crate::compress_fixed(input.as_slice(), buffer, level, window, dictionary) { + Ok(slice) => unsafe { *output.len = slice.len() }, + Err(status) => return status, + } + BrotliStatus::Success +} + +/// Brotli decompresses the given Go data into a buffer of limited capacity. +#[no_mangle] +pub extern "C" fn brotli_decompress( + input: BrotliBuffer, + mut output: BrotliBuffer, + dictionary: Dictionary, +) -> BrotliStatus { + match crate::decompress_fixed(input.as_slice(), output.as_uninit(), dictionary) { + Ok(slice) => unsafe { *output.len = slice.len() }, + Err(status) => return status, + } + BrotliStatus::Success +} diff --git a/arbitrator/brotli/src/dicts/mod.rs b/arbitrator/brotli/src/dicts/mod.rs new file mode 100644 index 0000000000..40d6c16963 --- /dev/null +++ b/arbitrator/brotli/src/dicts/mod.rs @@ -0,0 +1,97 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{ + types::BrotliSharedDictionaryType, BrotliStatus, CustomAllocator, EncoderPreparedDictionary, + HeapItem, +}; +use core::{ffi::c_int, ptr}; +use lazy_static::lazy_static; +use num_enum::{IntoPrimitive, TryFromPrimitive}; + +extern "C" { + /// Prepares an LZ77 dictionary for use during compression. + fn BrotliEncoderPrepareDictionary( + dict_type: BrotliSharedDictionaryType, + dict_len: c_int, + dictionary: *const u8, + quality: c_int, + alloc: Option *mut HeapItem>, + free: Option, + opaque: *mut CustomAllocator, + ) -> *mut EncoderPreparedDictionary; + + /// Nonzero when valid. + fn BrotliEncoderGetPreparedDictionarySize( + dictionary: *const EncoderPreparedDictionary, + ) -> usize; +} + +/// Forces a type to implement [`Sync`]. +struct ForceSync(T); + +unsafe impl Sync for ForceSync {} + +lazy_static! { + /// Memoizes dictionary preperation. + static ref STYLUS_PROGRAM_DICT: ForceSync<*const EncoderPreparedDictionary> = + ForceSync(unsafe { + let data = Dictionary::StylusProgram.slice().unwrap(); + let dict = BrotliEncoderPrepareDictionary( + BrotliSharedDictionaryType::Raw, + data.len() as c_int, + data.as_ptr(), + 11, + None, + None, + ptr::null_mut(), + ); + assert!(BrotliEncoderGetPreparedDictionarySize(dict) > 0); // check integrity + dict as _ + }); +} + +/// Brotli dictionary selection. +#[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] +#[repr(u32)] +pub enum Dictionary { + Empty, + StylusProgram, +} + +impl Dictionary { + /// Gets the raw bytes of the underlying LZ77 dictionary. + pub fn slice(&self) -> Option<&[u8]> { + match self { + Self::StylusProgram => Some(include_bytes!("stylus-program-11.lz")), + _ => None, + } + } + + /// Returns a pointer to a compression-ready instance of the given dictionary. + /// Note: this function fails when the specified level doesn't match. + pub fn ptr( + &self, + level: u32, + ) -> Result, BrotliStatus> { + Ok(match self { + Self::StylusProgram if level == 11 => Some(STYLUS_PROGRAM_DICT.0), + Self::StylusProgram => return Err(BrotliStatus::Failure), + _ => None, + }) + } +} + +impl From for u8 { + fn from(value: Dictionary) -> Self { + value as u32 as u8 + } +} + +impl TryFrom for Dictionary { + type Error = >::Error; + + fn try_from(value: u8) -> Result { + (value as u32).try_into() + } +} diff --git a/arbitrator/brotli/src/dicts/stylus-program-11.lz b/arbitrator/brotli/src/dicts/stylus-program-11.lz new file mode 100644 index 0000000000..073a29abf7 Binary files /dev/null and b/arbitrator/brotli/src/dicts/stylus-program-11.lz differ diff --git a/arbitrator/brotli/src/lib.rs b/arbitrator/brotli/src/lib.rs new file mode 100644 index 0000000000..5bc2e8dcf6 --- /dev/null +++ b/arbitrator/brotli/src/lib.rs @@ -0,0 +1,310 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![cfg_attr(target_arch = "wasm32", no_std)] + +extern crate alloc; + +#[cfg(target_arch = "wasm32")] +use alloc::vec::Vec; + +use core::{ + ffi::c_void, + mem::{self, MaybeUninit}, + ptr, +}; + +pub mod cgo; +mod dicts; +mod types; + +#[cfg(feature = "wasmer_traits")] +mod wasmer_traits; + +pub use dicts::Dictionary; +use types::*; +pub use types::{BrotliStatus, DEFAULT_WINDOW_SIZE}; + +type DecoderState = c_void; +type EncoderState = c_void; +type EncoderPreparedDictionary = c_void; +type CustomAllocator = c_void; +type HeapItem = c_void; + +// compression API +extern "C" { + fn BrotliEncoderCreateInstance( + alloc: Option *mut HeapItem>, + free: Option, + opaque: *mut CustomAllocator, + ) -> *mut EncoderState; + + /// Quality must be at least 2 for this bound to be correct. + fn BrotliEncoderMaxCompressedSize(input_size: usize) -> usize; + + fn BrotliEncoderSetParameter( + state: *mut EncoderState, + param: BrotliEncoderParameter, + value: u32, + ) -> BrotliBool; + + fn BrotliEncoderAttachPreparedDictionary( + state: *mut EncoderState, + dictionary: *const EncoderPreparedDictionary, + ) -> BrotliBool; + + fn BrotliEncoderCompressStream( + state: *mut EncoderState, + op: BrotliEncoderOperation, + input_len: *mut usize, + input_ptr: *mut *const u8, + out_left: *mut usize, + out_ptr: *mut *mut u8, + out_len: *mut usize, + ) -> BrotliBool; + + fn BrotliEncoderIsFinished(state: *mut EncoderState) -> BrotliBool; + + fn BrotliEncoderDestroyInstance(state: *mut EncoderState); +} + +// decompression API +extern "C" { + fn BrotliDecoderCreateInstance( + alloc: Option *mut HeapItem>, + free: Option, + opaque: *mut CustomAllocator, + ) -> *mut DecoderState; + + fn BrotliDecoderAttachDictionary( + state: *mut DecoderState, + kind: BrotliSharedDictionaryType, + len: usize, + dictionary: *const u8, + ) -> BrotliBool; + + fn BrotliDecoderDecompressStream( + state: *mut DecoderState, + input_len: *mut usize, + input_ptr: *mut *const u8, + out_left: *mut usize, + out_ptr: *mut *mut u8, + out_len: *mut usize, + ) -> BrotliStatus; + + fn BrotliDecoderIsFinished(state: *const DecoderState) -> BrotliBool; + + fn BrotliDecoderDestroyInstance(state: *mut DecoderState); +} + +/// Determines the maximum size a brotli compression could be. +/// Note: assumes the user never calls "flush" except during "finish" at the end. +pub fn compression_bound(len: usize, level: u32) -> usize { + let mut bound = unsafe { BrotliEncoderMaxCompressedSize(len) }; + if level <= 2 { + bound = bound.max(len + (len >> 10) * 8 + 64); + } + bound +} + +/// Brotli compresses a slice into a vec. +pub fn compress( + input: &[u8], + level: u32, + window_size: u32, + dictionary: Dictionary, +) -> Result, BrotliStatus> { + compress_into(input, Vec::new(), level, window_size, dictionary) +} + +/// Brotli compresses a slice, extending the `output` specified. +pub fn compress_into( + input: &[u8], + mut output: Vec, + level: u32, + window_size: u32, + dictionary: Dictionary, +) -> Result, BrotliStatus> { + let max_size = compression_bound(input.len(), level); + output.reserve_exact(max_size); + + let space = output.spare_capacity_mut(); + let count = compress_fixed(input, space, level, window_size, dictionary)?.len(); + unsafe { output.set_len(output.len() + count) } + Ok(output) +} + +/// Brotli compresses a slice into a buffer of limited capacity. +pub fn compress_fixed<'a>( + input: &'a [u8], + output: &'a mut [MaybeUninit], + level: u32, + window_size: u32, + dictionary: Dictionary, +) -> Result<&'a [u8], BrotliStatus> { + unsafe { + let state = BrotliEncoderCreateInstance(None, None, ptr::null_mut()); + + macro_rules! check { + ($ret:expr) => { + if $ret.is_err() { + BrotliEncoderDestroyInstance(state); + return Err(BrotliStatus::Failure); + } + }; + } + + check!(BrotliEncoderSetParameter( + state, + BrotliEncoderParameter::Quality, + level + )); + check!(BrotliEncoderSetParameter( + state, + BrotliEncoderParameter::WindowSize, + window_size + )); + + // attach a custom dictionary if requested + match dictionary.ptr(level) { + Ok(Some(dict)) => check!(BrotliEncoderAttachPreparedDictionary(state, dict)), + Err(status) => check!(status), + _ => {} + } + + let mut in_len = input.len(); + let mut in_ptr = input.as_ptr(); + let mut out_left = output.len(); + let mut out_ptr = output.as_mut_ptr() as *mut u8; + let mut out_len = out_left; + + let status = BrotliEncoderCompressStream( + state, + BrotliEncoderOperation::Finish, + &mut in_len as _, + &mut in_ptr as _, + &mut out_left as _, + &mut out_ptr as _, + &mut out_len as _, + ); + check!(status); + check!(BrotliEncoderIsFinished(state)); + BrotliEncoderDestroyInstance(state); + + // SAFETY: brotli initialized this span of bytes + let output = mem::transmute(&output[..out_len]); + Ok(output) + } +} + +/// Brotli compresses a slice into a buffer of limited capacity. +pub fn decompress(input: &[u8], dictionary: Dictionary) -> Result, BrotliStatus> { + unsafe { + let state = BrotliDecoderCreateInstance(None, None, ptr::null_mut()); + let mut output: Vec = Vec::with_capacity(4 * input.len()); + + macro_rules! check { + ($ret:expr) => { + if $ret.is_err() { + BrotliDecoderDestroyInstance(state); + return Err(BrotliStatus::Failure); + } + }; + } + + // TODO: consider window and quality check? + // TODO: fuzz + if let Some(dict) = dictionary.slice() { + let attatched = BrotliDecoderAttachDictionary( + state, + BrotliSharedDictionaryType::Raw, + dict.len(), + dict.as_ptr(), + ); + check!(attatched); + } + + let mut in_len = input.len(); + let mut in_ptr = input.as_ptr(); + let mut out_left = output.capacity(); + let mut out_ptr = output.as_mut_ptr(); + let mut out_len = out_left; + + loop { + let status = BrotliDecoderDecompressStream( + state, + &mut in_len as _, + &mut in_ptr as _, + &mut out_left as _, + &mut out_ptr as _, + &mut out_len as _, + ); + output.set_len(out_len); + + if status == BrotliStatus::NeedsMoreOutput { + output.reserve(24 * 1024); + out_ptr = output.as_mut_ptr().add(out_len); + out_left = output.capacity() - out_len; + continue; + } + check!(status); + check!(BrotliDecoderIsFinished(state)); + break; + } + + BrotliDecoderDestroyInstance(state); + Ok(output) + } +} + +/// Brotli decompresses a slice into +pub fn decompress_fixed<'a>( + input: &'a [u8], + output: &'a mut [MaybeUninit], + dictionary: Dictionary, +) -> Result<&'a [u8], BrotliStatus> { + unsafe { + let state = BrotliDecoderCreateInstance(None, None, ptr::null_mut()); + + macro_rules! check { + ($cond:expr) => { + if !$cond { + BrotliDecoderDestroyInstance(state); + return Err(BrotliStatus::Failure); + } + }; + } + + if let Some(dict) = dictionary.slice() { + let attatched = BrotliDecoderAttachDictionary( + state, + BrotliSharedDictionaryType::Raw, + dict.len(), + dict.as_ptr(), + ); + check!(attatched == BrotliBool::True); + } + + let mut in_len = input.len(); + let mut in_ptr = input.as_ptr(); + let mut out_left = output.len(); + let mut out_ptr = output.as_mut_ptr() as *mut u8; + let mut out_len = out_left; + + let status = BrotliDecoderDecompressStream( + state, + &mut in_len as _, + &mut in_ptr as _, + &mut out_left as _, + &mut out_ptr as _, + &mut out_len as _, + ); + check!(status == BrotliStatus::Success); + check!(BrotliDecoderIsFinished(state) == BrotliBool::True); + BrotliDecoderDestroyInstance(state); + + // SAFETY: brotli initialized this span of bytes + let output = mem::transmute(&output[..out_len]); + Ok(output) + } +} diff --git a/arbitrator/brotli/src/types.rs b/arbitrator/brotli/src/types.rs new file mode 100644 index 0000000000..ace44f3890 --- /dev/null +++ b/arbitrator/brotli/src/types.rs @@ -0,0 +1,102 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![allow(dead_code)] + +use num_enum::{IntoPrimitive, TryFromPrimitive}; + +/// The default window size used during compression. +pub const DEFAULT_WINDOW_SIZE: u32 = 22; + +/// Represents the outcome of a brotli operation. +#[derive(Debug, PartialEq, IntoPrimitive, TryFromPrimitive)] +#[repr(u32)] +pub enum BrotliStatus { + Failure, + Success, + NeedsMoreInput, + NeedsMoreOutput, +} + +impl BrotliStatus { + /// Whether the outcome of the operation was successful. + pub fn is_ok(&self) -> bool { + self == &Self::Success + } + + /// Whether the outcome of the operation was an error of any kind. + pub fn is_err(&self) -> bool { + !self.is_ok() + } +} + +/// A portable `bool`. +#[derive(PartialEq)] +#[repr(usize)] +pub(super) enum BrotliBool { + False, + True, +} + +impl BrotliBool { + /// Whether the type is `True`. This function exists since the API conflates `BrotliBool` and `BrotliStatus` at times. + pub fn is_ok(&self) -> bool { + self == &Self::True + } + + /// Whether the type is `False`. This function exists since the API conflates `BrotliBool` and `BrotliStatus` at times. + pub fn is_err(&self) -> bool { + !self.is_ok() + } +} + +/// The dictionary policy. +#[repr(C)] +pub(super) enum BrotliEncoderMode { + /// Start with an empty dictionary. + Generic, + /// Use the pre-built dictionary for text. + Text, + /// Use the pre-built dictionary for fonts. + Font, +} + +/// Configuration options for brotli compression. +#[repr(C)] +pub(super) enum BrotliEncoderParameter { + /// The dictionary policy. + Mode, + /// The brotli level. Ranges from 0 to 11. + Quality, + /// The size of the window. Defaults to 22. + WindowSize, + BlockSize, + DisableContextModeling, + SizeHint, + LargeWindowMode, + PostfixBits, + DirectDistanceCodes, + StreamOffset, +} + +/// Streaming operations for use when encoding. +#[repr(C)] +pub(super) enum BrotliEncoderOperation { + /// Produce as much output as possible. + Process, + /// Flush the contents of the encoder. + Flush, + /// Flush and finalize the contents of the encoder. + Finish, + /// Emit metadata info. + Metadata, +} + +/// Type of custom dictionary. +#[repr(C)] +pub(super) enum BrotliSharedDictionaryType { + /// LZ77 prefix dictionary + Raw, + /// Serialized dictionary + Serialized, +} diff --git a/arbitrator/brotli/src/wasmer_traits.rs b/arbitrator/brotli/src/wasmer_traits.rs new file mode 100644 index 0000000000..169b2862ae --- /dev/null +++ b/arbitrator/brotli/src/wasmer_traits.rs @@ -0,0 +1,29 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{dicts::Dictionary, types::BrotliStatus}; +use wasmer::FromToNativeWasmType; + +unsafe impl FromToNativeWasmType for BrotliStatus { + type Native = i32; + + fn from_native(native: i32) -> Self { + Self::try_from(u32::from_native(native)).expect("unknown brotli status") + } + + fn to_native(self) -> i32 { + (self as u32).to_native() + } +} + +unsafe impl FromToNativeWasmType for Dictionary { + type Native = i32; + + fn from_native(native: i32) -> Self { + Self::try_from(u32::from_native(native)).expect("unknown brotli dictionary") + } + + fn to_native(self) -> i32 { + (self as u32).to_native() + } +} diff --git a/arbitrator/caller-env/Cargo.toml b/arbitrator/caller-env/Cargo.toml new file mode 100644 index 0000000000..ad4d07ccad --- /dev/null +++ b/arbitrator/caller-env/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "caller-env" +version = "0.1.0" +edition.workspace = true + +[dependencies] +brotli = { path = "../brotli/", optional = true } +num_enum.workspace = true +rand_pcg = { version = "0.3.1", default-features = false } +rand = { version = "0.8.4", default-features = false } +wasmer = { path = "../tools/wasmer/lib/api", optional = true } + +[features] +default = ["brotli"] +brotli = ["dep:brotli"] +static_caller = [] +wasmer_traits = ["dep:wasmer", "brotli?/wasmer_traits"] diff --git a/arbitrator/caller-env/src/brotli/mod.rs b/arbitrator/caller-env/src/brotli/mod.rs new file mode 100644 index 0000000000..2ba8c6e6f1 --- /dev/null +++ b/arbitrator/caller-env/src/brotli/mod.rs @@ -0,0 +1,72 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![allow(clippy::too_many_arguments)] + +use crate::{ExecEnv, GuestPtr, MemAccess}; +use alloc::vec::Vec; +use brotli::{BrotliStatus, Dictionary}; + +/// Brotli compresses a go slice +/// +/// The output buffer must be sufficiently large. +/// The pointers must not be null. +pub fn brotli_compress( + mem: &mut M, + _env: &mut E, + in_buf_ptr: GuestPtr, + in_buf_len: u32, + out_buf_ptr: GuestPtr, + out_len_ptr: GuestPtr, + level: u32, + window_size: u32, + dictionary: Dictionary, +) -> BrotliStatus { + let input = mem.read_slice(in_buf_ptr, in_buf_len as usize); + let mut output = Vec::with_capacity(mem.read_u32(out_len_ptr) as usize); + + let result = brotli::compress_fixed( + &input, + output.spare_capacity_mut(), + level, + window_size, + dictionary, + ); + match result { + Ok(slice) => { + mem.write_slice(out_buf_ptr, slice); + mem.write_u32(out_len_ptr, slice.len() as u32); + BrotliStatus::Success + } + Err(status) => status, + } +} + +/// Brotli decompresses a go slice using a custom dictionary. +/// +/// # Safety +/// +/// The output buffer must be sufficiently large. +/// The pointers must not be null. +pub fn brotli_decompress( + mem: &mut M, + _env: &mut E, + in_buf_ptr: GuestPtr, + in_buf_len: u32, + out_buf_ptr: GuestPtr, + out_len_ptr: GuestPtr, + dictionary: Dictionary, +) -> BrotliStatus { + let input = mem.read_slice(in_buf_ptr, in_buf_len as usize); + let mut output = Vec::with_capacity(mem.read_u32(out_len_ptr) as usize); + + let result = brotli::decompress_fixed(&input, output.spare_capacity_mut(), dictionary); + match result { + Ok(slice) => { + mem.write_slice(out_buf_ptr, slice); + mem.write_u32(out_len_ptr, slice.len() as u32); + BrotliStatus::Success + } + Err(status) => status, + } +} diff --git a/arbitrator/caller-env/src/guest_ptr.rs b/arbitrator/caller-env/src/guest_ptr.rs new file mode 100644 index 0000000000..cbef490c61 --- /dev/null +++ b/arbitrator/caller-env/src/guest_ptr.rs @@ -0,0 +1,49 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use core::ops::{Add, AddAssign, Deref}; + +/// Represents a pointer to a Guest WASM's memory. +#[derive(Clone, Copy, Eq, PartialEq)] +#[repr(transparent)] +pub struct GuestPtr(pub u32); + +impl Add for GuestPtr { + type Output = Self; + + fn add(self, rhs: u32) -> Self::Output { + Self(self.0 + rhs) + } +} + +impl AddAssign for GuestPtr { + fn add_assign(&mut self, rhs: u32) { + *self = *self + rhs; + } +} + +impl From for u32 { + fn from(value: GuestPtr) -> Self { + value.0 + } +} + +impl From for u64 { + fn from(value: GuestPtr) -> Self { + value.0.into() + } +} + +impl Deref for GuestPtr { + type Target = u32; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl GuestPtr { + pub fn to_u64(self) -> u64 { + self.into() + } +} diff --git a/arbitrator/caller-env/src/lib.rs b/arbitrator/caller-env/src/lib.rs new file mode 100644 index 0000000000..ba3874919a --- /dev/null +++ b/arbitrator/caller-env/src/lib.rs @@ -0,0 +1,67 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![cfg_attr(target_arch = "wasm32", no_std)] + +extern crate alloc; + +use alloc::vec::Vec; +use rand_pcg::Pcg32; + +pub use guest_ptr::GuestPtr; +pub use wasip1_stub::Errno; + +#[cfg(feature = "static_caller")] +pub mod static_caller; + +#[cfg(feature = "wasmer_traits")] +pub mod wasmer_traits; + +#[cfg(feature = "brotli")] +pub mod brotli; + +mod guest_ptr; +pub mod wasip1_stub; + +/// Initializes a deterministic, psuedo-random number generator with a fixed seed. +pub fn create_pcg() -> Pcg32 { + const PCG_INIT_STATE: u64 = 0xcafef00dd15ea5e5; + const PCG_INIT_STREAM: u64 = 0xa02bdbf7bb3c0a7; + Pcg32::new(PCG_INIT_STATE, PCG_INIT_STREAM) +} + +/// Access Guest memory. +pub trait MemAccess { + fn read_u8(&self, ptr: GuestPtr) -> u8; + + fn read_u16(&self, ptr: GuestPtr) -> u16; + + fn read_u32(&self, ptr: GuestPtr) -> u32; + + fn read_u64(&self, ptr: GuestPtr) -> u64; + + fn write_u8(&mut self, ptr: GuestPtr, x: u8); + + fn write_u16(&mut self, ptr: GuestPtr, x: u16); + + fn write_u32(&mut self, ptr: GuestPtr, x: u32); + + fn write_u64(&mut self, ptr: GuestPtr, x: u64); + + fn read_slice(&self, ptr: GuestPtr, len: usize) -> Vec; + + fn read_fixed(&self, ptr: GuestPtr) -> [u8; N]; + + fn write_slice(&mut self, ptr: GuestPtr, data: &[u8]); +} + +/// Update the Host environment. +pub trait ExecEnv { + fn advance_time(&mut self, ns: u64); + + fn get_time(&self) -> u64; + + fn next_rand_u32(&mut self) -> u32; + + fn print_string(&mut self, message: &[u8]); +} diff --git a/arbitrator/caller-env/src/static_caller.rs b/arbitrator/caller-env/src/static_caller.rs new file mode 100644 index 0000000000..46a2a3f48d --- /dev/null +++ b/arbitrator/caller-env/src/static_caller.rs @@ -0,0 +1,119 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{create_pcg, ExecEnv, GuestPtr, MemAccess}; +use alloc::vec::Vec; +use rand::RngCore; +use rand_pcg::Pcg32; + +extern crate alloc; + +static mut TIME: u64 = 0; +static mut RNG: Option = None; + +pub struct StaticMem; +pub struct StaticExecEnv; + +pub static mut STATIC_MEM: StaticMem = StaticMem; +pub static mut STATIC_ENV: StaticExecEnv = StaticExecEnv; + +extern "C" { + fn wavm_caller_load8(ptr: GuestPtr) -> u8; + fn wavm_caller_load32(ptr: GuestPtr) -> u32; + fn wavm_caller_store8(ptr: GuestPtr, val: u8); + fn wavm_caller_store32(ptr: GuestPtr, val: u32); +} + +impl MemAccess for StaticMem { + fn read_u8(&self, ptr: GuestPtr) -> u8 { + unsafe { wavm_caller_load8(ptr) } + } + + fn read_u16(&self, ptr: GuestPtr) -> u16 { + let lsb = self.read_u8(ptr); + let msb = self.read_u8(ptr + 1); + (msb as u16) << 8 | (lsb as u16) + } + + fn read_u32(&self, ptr: GuestPtr) -> u32 { + unsafe { wavm_caller_load32(ptr) } + } + + fn read_u64(&self, ptr: GuestPtr) -> u64 { + let lsb = self.read_u32(ptr); + let msb = self.read_u32(ptr + 4); + (msb as u64) << 32 | (lsb as u64) + } + + fn write_u8(&mut self, ptr: GuestPtr, x: u8) { + unsafe { wavm_caller_store8(ptr, x) } + } + + fn write_u16(&mut self, ptr: GuestPtr, x: u16) { + self.write_u8(ptr, (x & 0xff) as u8); + self.write_u8(ptr + 1, ((x >> 8) & 0xff) as u8); + } + + fn write_u32(&mut self, ptr: GuestPtr, x: u32) { + unsafe { wavm_caller_store32(ptr, x) } + } + + fn write_u64(&mut self, ptr: GuestPtr, x: u64) { + self.write_u32(ptr, (x & 0xffffffff) as u32); + self.write_u32(ptr + 4, ((x >> 32) & 0xffffffff) as u32); + } + + fn read_slice(&self, mut ptr: GuestPtr, mut len: usize) -> Vec { + let mut data = Vec::with_capacity(len); + if len == 0 { + return data; + } + while len >= 4 { + data.extend(self.read_u32(ptr).to_le_bytes()); + ptr += 4; + len -= 4; + } + for _ in 0..len { + data.push(self.read_u8(ptr)); + ptr += 1; + } + data + } + + fn read_fixed(&self, ptr: GuestPtr) -> [u8; N] { + self.read_slice(ptr, N).try_into().unwrap() + } + + fn write_slice(&mut self, mut ptr: GuestPtr, mut src: &[u8]) { + while src.len() >= 4 { + let mut arr = [0; 4]; + arr.copy_from_slice(&src[..4]); + self.write_u32(ptr, u32::from_le_bytes(arr)); + ptr += 4; + src = &src[4..]; + } + for &byte in src { + self.write_u8(ptr, byte); + ptr += 1; + } + } +} + +impl ExecEnv for StaticExecEnv { + fn print_string(&mut self, _data: &[u8]) { + // printing is done by arbitrator machine host_call_hook + // capturing the fd_write call directly + } + + fn get_time(&self) -> u64 { + unsafe { TIME } + } + + fn advance_time(&mut self, delta: u64) { + unsafe { TIME += delta } + } + + fn next_rand_u32(&mut self) -> u32 { + unsafe { RNG.get_or_insert_with(create_pcg) }.next_u32() + } +} diff --git a/arbitrator/caller-env/src/wasip1_stub.rs b/arbitrator/caller-env/src/wasip1_stub.rs new file mode 100644 index 0000000000..2f07cd7e53 --- /dev/null +++ b/arbitrator/caller-env/src/wasip1_stub.rs @@ -0,0 +1,407 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +//! A stub impl of [WASI Preview 1][Wasi] for proving fraud. +//! +//! [Wasi]: https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md + +#![allow(clippy::too_many_arguments)] + +use crate::{ExecEnv, GuestPtr, MemAccess}; + +#[repr(transparent)] +pub struct Errno(pub(crate) u16); + +pub const ERRNO_SUCCESS: Errno = Errno(0); +pub const ERRNO_BADF: Errno = Errno(8); +pub const ERRNO_INVAL: Errno = Errno(28); + +/// Writes the number and total size of args passed by the OS. +/// Note that this currently consists of just the program name `bin`. +pub fn args_sizes_get( + mem: &mut M, + _: &mut E, + length_ptr: GuestPtr, + data_size_ptr: GuestPtr, +) -> Errno { + mem.write_u32(length_ptr, 1); + mem.write_u32(data_size_ptr, 4); + ERRNO_SUCCESS +} + +/// Writes the args passed by the OS. +/// Note that this currently consists of just the program name `bin`. +pub fn args_get( + mem: &mut M, + _: &mut E, + argv_buf: GuestPtr, + data_buf: GuestPtr, +) -> Errno { + mem.write_u32(argv_buf, data_buf.into()); + mem.write_u32(data_buf, 0x6E6962); // "bin\0" + ERRNO_SUCCESS +} + +/// Writes the number and total size of OS environment variables. +/// Note that none exist in Nitro. +pub fn environ_sizes_get( + mem: &mut M, + _env: &mut E, + length_ptr: GuestPtr, + data_size_ptr: GuestPtr, +) -> Errno { + mem.write_u32(length_ptr, 0); + mem.write_u32(data_size_ptr, 0); + ERRNO_SUCCESS +} + +/// Writes the number and total size of OS environment variables. +/// Note that none exist in Nitro. +pub fn environ_get( + _: &mut M, + _: &mut E, + _: GuestPtr, + _: GuestPtr, +) -> Errno { + ERRNO_SUCCESS +} + +/// Writes to the given file descriptor. +/// Note that we only support stdout and stderr. +pub fn fd_write( + mem: &mut M, + env: &mut E, + fd: u32, + iovecs_ptr: GuestPtr, + iovecs_len: u32, + ret_ptr: GuestPtr, +) -> Errno { + if fd != 1 && fd != 2 { + return ERRNO_BADF; + } + let mut size = 0; + for i in 0..iovecs_len { + let ptr = iovecs_ptr + i * 8; + let len = mem.read_u32(ptr + 4); + let ptr = mem.read_u32(ptr); // TODO: string might be split across utf-8 character boundary + let data = mem.read_slice(GuestPtr(ptr), len as usize); + env.print_string(&data); + size += len; + } + mem.write_u32(ret_ptr, size); + ERRNO_SUCCESS +} + +/// Closes the given file descriptor. Unsupported. +pub fn fd_close(_: &mut M, _: &mut E, _: u32) -> Errno { + ERRNO_BADF +} + +/// Reads from the given file descriptor. Unsupported. +pub fn fd_read( + _: &mut M, + _: &mut E, + _: u32, + _: u32, + _: u32, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Reads the contents of a directory. Unsupported. +pub fn fd_readdir( + _: &mut M, + _: &mut E, + _fd: u32, + _: u32, + _: u32, + _: u64, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Syncs a file to disk. Unsupported. +pub fn fd_sync(_: &mut M, _: &mut E, _: u32) -> Errno { + ERRNO_SUCCESS +} + +/// Move within a file. Unsupported. +pub fn fd_seek( + _: &mut M, + _: &mut E, + _fd: u32, + _offset: u64, + _whence: u8, + _filesize: u32, +) -> Errno { + ERRNO_BADF +} + +/// Syncs file contents to disk. Unsupported. +pub fn fd_datasync(_: &mut M, _: &mut E, _fd: u32) -> Errno { + ERRNO_BADF +} + +/// Retrieves attributes about a file descriptor. Unsupported. +pub fn fd_fdstat_get(_: &mut M, _: &mut E, _: u32, _: u32) -> Errno { + ERRNO_INVAL +} + +/// Sets the attributes of a file descriptor. Unsupported. +pub fn fd_fdstat_set_flags( + _: &mut M, + _: &mut E, + _: u32, + _: u32, +) -> Errno { + ERRNO_INVAL +} + +/// Opens the file or directory at the given path. Unsupported. +pub fn path_open( + _: &mut M, + _: &mut E, + _: u32, + _: u32, + _: u32, + _: u32, + _: u32, + _: u64, + _: u64, + _: u32, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Creates a directory. Unsupported. +pub fn path_create_directory( + _: &mut M, + _: &mut E, + _: u32, + _: u32, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Unlinks a directory. Unsupported. +pub fn path_remove_directory( + _: &mut M, + _: &mut E, + _: u32, + _: u32, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Resolves a symbolic link. Unsupported. +pub fn path_readlink( + _: &mut M, + _: &mut E, + _: u32, + _: u32, + _: u32, + _: u32, + _: u32, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Moves a file. Unsupported. +pub fn path_rename( + _: &mut M, + _: &mut E, + _: u32, + _: u32, + _: u32, + _: u32, + _: u32, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Retrieves info about an open file. Unsupported. +pub fn path_filestat_get( + _: &mut M, + _: &mut E, + _: u32, + _: u32, + _: u32, + _: u32, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Unlinks the file at the given path. Unsupported. +pub fn path_unlink_file( + _: &mut M, + _: &mut E, + _: u32, + _: u32, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Retrieves info about a file. Unsupported. +pub fn fd_prestat_get(_: &mut M, _: &mut E, _: u32, _: u32) -> Errno { + ERRNO_BADF +} + +/// Retrieves info about a directory. Unsupported. +pub fn fd_prestat_dir_name( + _: &mut M, + _: &mut E, + _: u32, + _: u32, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Retrieves info about a file. Unsupported. +pub fn fd_filestat_get( + _: &mut M, + _: &mut E, + _fd: u32, + _filestat: u32, +) -> Errno { + ERRNO_BADF +} + +/// Sets the size of an open file. Unsupported. +pub fn fd_filestat_set_size( + _: &mut M, + _: &mut E, + _fd: u32, + _: u64, +) -> Errno { + ERRNO_BADF +} + +/// Peaks within a descriptor without modifying its state. Unsupported. +pub fn fd_pread( + _: &mut M, + _: &mut E, + _fd: u32, + _: u32, + _: u32, + _: u64, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Writes to a descriptor without modifying the current offset. Unsupported. +pub fn fd_pwrite( + _: &mut M, + _: &mut E, + _fd: u32, + _: u32, + _: u32, + _: u64, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Accepts a new connection. Unsupported. +pub fn sock_accept( + _: &mut M, + _: &mut E, + _fd: u32, + _: u32, + _: u32, +) -> Errno { + ERRNO_BADF +} + +/// Shuts down a socket. Unsupported. +pub fn sock_shutdown(_: &mut M, _: &mut E, _: u32, _: u32) -> Errno { + ERRNO_BADF +} + +/// Yields execution to the OS scheduler. Effectively does nothing in Nitro due to the lack of threads. +pub fn sched_yield(_: &mut M, _: &mut E) -> Errno { + ERRNO_SUCCESS +} + +/// 10ms in ns +static TIME_INTERVAL: u64 = 10_000_000; + +/// Retrieves the time in ns of the given clock. +/// Note that in Nitro, all clocks point to the same deterministic counter that advances 10ms whenever +/// this function is called. +pub fn clock_time_get( + mem: &mut M, + env: &mut E, + _clock_id: u32, + _precision: u64, + time_ptr: GuestPtr, +) -> Errno { + env.advance_time(TIME_INTERVAL); + mem.write_u64(time_ptr, env.get_time()); + ERRNO_SUCCESS +} + +/// Fills a slice with psuedo-random bytes. +/// Note that in Nitro, the bytes are deterministically generated from a common seed. +pub fn random_get( + mem: &mut M, + env: &mut E, + mut buf: GuestPtr, + mut len: u32, +) -> Errno { + while len >= 4 { + let next_rand = env.next_rand_u32(); + mem.write_u32(buf, next_rand); + buf += 4; + len -= 4; + } + if len > 0 { + let mut rem = env.next_rand_u32(); + for _ in 0..len { + mem.write_u8(buf, rem as u8); + buf += 1; + rem >>= 8; + } + } + ERRNO_SUCCESS +} + +/// Poll for events. +/// Note that we always simulate a timeout and skip all others. +pub fn poll_oneoff( + mem: &mut M, + env: &mut E, + in_subs: GuestPtr, + out_evt: GuestPtr, + num_subscriptions: u32, + num_events_ptr: GuestPtr, +) -> Errno { + // simulate the passage of time each poll request + env.advance_time(TIME_INTERVAL); + + const SUBSCRIPTION_SIZE: u32 = 48; // user data + 40-byte union + for index in 0..num_subscriptions { + let subs_base = in_subs + (SUBSCRIPTION_SIZE * index); + let subs_type = mem.read_u32(subs_base + 8); + if subs_type != 0 { + // not a clock subscription type + continue; + } + let user_data = mem.read_u32(subs_base); + mem.write_u32(out_evt, user_data); + mem.write_u32(out_evt + 8, subs_type); + mem.write_u32(num_events_ptr, 1); + return ERRNO_SUCCESS; + } + ERRNO_INVAL +} diff --git a/arbitrator/caller-env/src/wasmer_traits.rs b/arbitrator/caller-env/src/wasmer_traits.rs new file mode 100644 index 0000000000..babc22c6fa --- /dev/null +++ b/arbitrator/caller-env/src/wasmer_traits.rs @@ -0,0 +1,35 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{Errno, GuestPtr}; +use wasmer::{FromToNativeWasmType, WasmPtr}; + +unsafe impl FromToNativeWasmType for GuestPtr { + type Native = i32; + + fn from_native(native: i32) -> Self { + Self(u32::from_native(native)) + } + + fn to_native(self) -> i32 { + self.0.to_native() + } +} + +unsafe impl FromToNativeWasmType for Errno { + type Native = i32; + + fn from_native(native: i32) -> Self { + Self(u16::from_native(native)) + } + + fn to_native(self) -> i32 { + self.0.to_native() + } +} + +impl From for WasmPtr { + fn from(value: GuestPtr) -> Self { + WasmPtr::new(value.0) + } +} diff --git a/arbitrator/cbindgen.toml b/arbitrator/cbindgen.toml deleted file mode 100644 index 08094f28fc..0000000000 --- a/arbitrator/cbindgen.toml +++ /dev/null @@ -1 +0,0 @@ -language = "C" diff --git a/arbitrator/jit/Cargo.toml b/arbitrator/jit/Cargo.toml index 75b3e3a74c..fb49b871b3 100644 --- a/arbitrator/jit/Cargo.toml +++ b/arbitrator/jit/Cargo.toml @@ -5,9 +5,13 @@ edition = "2021" [dependencies] arbutil = { path = "../arbutil/" } -wasmer = "3.1.0" -wasmer-compiler-cranelift = "3.1.0" -wasmer-compiler-llvm = { version = "3.1.0", optional = true } +brotli = { path = "../brotli/", features = ["wasmer_traits"] } +caller-env = { path = "../caller-env/", features = ["wasmer_traits"] } +prover = { path = "../prover/", default-features = false, features = ["native"] } +stylus = { path = "../stylus/", default-features = false } +wasmer = { path = "../tools/wasmer/lib/api/" } +wasmer-compiler-llvm = { path = "../tools/wasmer/lib/compiler-llvm/", optional = true } +wasmer-compiler-cranelift = { path = "../tools/wasmer/lib/compiler-cranelift/" } eyre = "0.6.5" parking_lot = "0.12.1" rand = { version = "0.8.4", default-features = false } @@ -17,7 +21,7 @@ hex = "0.4.3" structopt = "0.3.26" sha3 = "0.9.1" libc = "0.2.132" -ouroboros = "0.16.0" +sha2 = "0.9.9" [features] llvm = ["dep:wasmer-compiler-llvm"] diff --git a/arbitrator/jit/build.rs b/arbitrator/jit/build.rs deleted file mode 100644 index e18155017e..0000000000 --- a/arbitrator/jit/build.rs +++ /dev/null @@ -1,7 +0,0 @@ -fn main() { - // Tell Cargo that if the given file changes, to rerun this build script. - println!("cargo:rustc-link-search=../target/lib/"); - println!("cargo:rustc-link-lib=static=brotlienc-static"); - println!("cargo:rustc-link-lib=static=brotlidec-static"); - println!("cargo:rustc-link-lib=static=brotlicommon-static"); -} diff --git a/arbitrator/jit/src/arbcompress.rs b/arbitrator/jit/src/arbcompress.rs index 469b218958..8000d51b21 100644 --- a/arbitrator/jit/src/arbcompress.rs +++ b/arbitrator/jit/src/arbcompress.rs @@ -1,93 +1,41 @@ -// Copyright 2022, Offchain Labs, Inc. +// Copyright 2022-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -use crate::{gostack::GoStack, machine::WasmEnvMut}; - -extern "C" { - pub fn BrotliDecoderDecompress( - encoded_size: usize, - encoded_buffer: *const u8, - decoded_size: *mut usize, - decoded_buffer: *mut u8, - ) -> u32; - - pub fn BrotliEncoderCompress( - quality: u32, - lgwin: u32, - mode: u32, - input_size: usize, - input_buffer: *const u8, - encoded_size: *mut usize, - encoded_buffer: *mut u8, - ) -> u32; -} - -const BROTLI_MODE_GENERIC: u32 = 0; -const BROTLI_RES_SUCCESS: u32 = 1; - -pub fn brotli_compress(mut env: WasmEnvMut, sp: u32) { - let (sp, _) = GoStack::new(sp, &mut env); - - //(inBuf []byte, outBuf []byte, level int, windowSize int) int - let in_buf_ptr = sp.read_u64(0); - let in_buf_len = sp.read_u64(1); - let out_buf_ptr = sp.read_u64(3); - let out_buf_len = sp.read_u64(4); - let level = sp.read_u64(6) as u32; - let windowsize = sp.read_u64(7) as u32; - let output_arg = 8; - - let in_slice = sp.read_slice(in_buf_ptr, in_buf_len); - let mut output = vec![0u8; out_buf_len as usize]; - let mut output_len = out_buf_len as usize; - - let res = unsafe { - BrotliEncoderCompress( - level, - windowsize, - BROTLI_MODE_GENERIC, - in_buf_len as usize, - in_slice.as_ptr(), - &mut output_len, - output.as_mut_ptr(), - ) +use crate::caller_env::{JitEnv, JitExecEnv}; +use crate::machine::Escape; +use crate::machine::WasmEnvMut; +use brotli::{BrotliStatus, Dictionary}; +use caller_env::{self, GuestPtr}; + +macro_rules! wrap { + ($(fn $func_name:ident ($($arg_name:ident : $arg_type:ty),* ) -> $return_type:ty);*) => { + $( + #[allow(clippy::too_many_arguments)] + pub fn $func_name(mut src: WasmEnvMut, $($arg_name : $arg_type),*) -> Result<$return_type, Escape> { + let (mut mem, wenv) = src.jit_env(); + + Ok(caller_env::brotli::$func_name(&mut mem, &mut JitExecEnv { wenv }, $($arg_name),*)) + } + )* }; - - if (res != BROTLI_RES_SUCCESS) || (output_len as u64 > out_buf_len) { - sp.write_u64(output_arg, u64::MAX); - return; - } - sp.write_slice(out_buf_ptr, &output[..output_len]); - sp.write_u64(output_arg, output_len as u64); } -pub fn brotli_decompress(mut env: WasmEnvMut, sp: u32) { - let (sp, _) = GoStack::new(sp, &mut env); - - //(inBuf []byte, outBuf []byte) int - let in_buf_ptr = sp.read_u64(0); - let in_buf_len = sp.read_u64(1); - let out_buf_ptr = sp.read_u64(3); - let out_buf_len = sp.read_u64(4); - let output_arg = 6; - - let in_slice = sp.read_slice(in_buf_ptr, in_buf_len); - let mut output = vec![0u8; out_buf_len as usize]; - let mut output_len = out_buf_len as usize; - - let res = unsafe { - BrotliDecoderDecompress( - in_buf_len as usize, - in_slice.as_ptr(), - &mut output_len, - output.as_mut_ptr(), - ) - }; - - if (res != BROTLI_RES_SUCCESS) || (output_len as u64 > out_buf_len) { - sp.write_u64(output_arg, u64::MAX); - return; - } - sp.write_slice(out_buf_ptr, &output[..output_len]); - sp.write_u64(output_arg, output_len as u64); +wrap! { + fn brotli_compress( + in_buf_ptr: GuestPtr, + in_buf_len: u32, + out_buf_ptr: GuestPtr, + out_len_ptr: GuestPtr, + level: u32, + window_size: u32, + dictionary: Dictionary + ) -> BrotliStatus; + + fn brotli_decompress( + in_buf_ptr: GuestPtr, + in_buf_len: u32, + out_buf_ptr: GuestPtr, + out_len_ptr: GuestPtr, + dictionary: Dictionary + ) -> BrotliStatus } diff --git a/arbitrator/jit/src/caller_env.rs b/arbitrator/jit/src/caller_env.rs new file mode 100644 index 0000000000..f4fbff10ae --- /dev/null +++ b/arbitrator/jit/src/caller_env.rs @@ -0,0 +1,185 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use crate::machine::{WasmEnv, WasmEnvMut}; +use arbutil::{Bytes20, Bytes32}; +use caller_env::{ExecEnv, GuestPtr, MemAccess}; +use rand::RngCore; +use rand_pcg::Pcg32; +use std::{ + cmp::Ordering, + collections::{BTreeSet, BinaryHeap}, + fmt::Debug, + mem::{self, MaybeUninit}, +}; +use wasmer::{Memory, MemoryView, StoreMut, WasmPtr}; + +pub struct JitMemAccess<'s> { + pub memory: Memory, + pub store: StoreMut<'s>, +} + +pub struct JitExecEnv<'s> { + pub wenv: &'s mut WasmEnv, +} + +pub(crate) trait JitEnv<'a> { + fn jit_env(&mut self) -> (JitMemAccess<'_>, &mut WasmEnv); +} + +impl<'a> JitEnv<'a> for WasmEnvMut<'a> { + fn jit_env(&mut self) -> (JitMemAccess<'_>, &mut WasmEnv) { + let memory = self.data().memory.clone().unwrap(); + let (wenv, store) = self.data_and_store_mut(); + (JitMemAccess { memory, store }, wenv) + } +} + +impl<'s> JitMemAccess<'s> { + fn view(&self) -> MemoryView { + self.memory.view(&self.store) + } + + pub fn write_bytes32(&mut self, ptr: GuestPtr, val: Bytes32) { + self.write_slice(ptr, val.as_slice()) + } + + pub fn read_bytes20(&mut self, ptr: GuestPtr) -> Bytes20 { + self.read_fixed(ptr).into() + } + + pub fn read_bytes32(&mut self, ptr: GuestPtr) -> Bytes32 { + self.read_fixed(ptr).into() + } +} + +impl MemAccess for JitMemAccess<'_> { + fn read_u8(&self, ptr: GuestPtr) -> u8 { + let ptr: WasmPtr = ptr.into(); + ptr.deref(&self.view()).read().unwrap() + } + + fn read_u16(&self, ptr: GuestPtr) -> u16 { + let ptr: WasmPtr = ptr.into(); + ptr.deref(&self.view()).read().unwrap() + } + + fn read_u32(&self, ptr: GuestPtr) -> u32 { + let ptr: WasmPtr = ptr.into(); + ptr.deref(&self.view()).read().unwrap() + } + + fn read_u64(&self, ptr: GuestPtr) -> u64 { + let ptr: WasmPtr = ptr.into(); + ptr.deref(&self.view()).read().unwrap() + } + + fn write_u8(&mut self, ptr: GuestPtr, x: u8) { + let ptr: WasmPtr = ptr.into(); + ptr.deref(&self.view()).write(x).unwrap(); + } + + fn write_u16(&mut self, ptr: GuestPtr, x: u16) { + let ptr: WasmPtr = ptr.into(); + ptr.deref(&self.view()).write(x).unwrap(); + } + + fn write_u32(&mut self, ptr: GuestPtr, x: u32) { + let ptr: WasmPtr = ptr.into(); + ptr.deref(&self.view()).write(x).unwrap(); + } + + fn write_u64(&mut self, ptr: GuestPtr, x: u64) { + let ptr: WasmPtr = ptr.into(); + ptr.deref(&self.view()).write(x).unwrap(); + } + + fn read_slice(&self, ptr: GuestPtr, len: usize) -> Vec { + let mut data: Vec> = Vec::with_capacity(len); + // SAFETY: read_uninit fills all available space + unsafe { + data.set_len(len); + self.view() + .read_uninit(ptr.into(), &mut data) + .expect("bad read"); + mem::transmute(data) + } + } + + fn read_fixed(&self, ptr: GuestPtr) -> [u8; N] { + self.read_slice(ptr, N).try_into().unwrap() + } + + fn write_slice(&mut self, ptr: GuestPtr, src: &[u8]) { + self.view().write(ptr.into(), src).unwrap(); + } +} + +impl ExecEnv for JitExecEnv<'_> { + fn advance_time(&mut self, ns: u64) { + self.wenv.go_state.time += ns; + } + + fn get_time(&self) -> u64 { + self.wenv.go_state.time + } + + fn next_rand_u32(&mut self) -> u32 { + self.wenv.go_state.rng.next_u32() + } + + fn print_string(&mut self, bytes: &[u8]) { + match String::from_utf8(bytes.to_vec()) { + Ok(s) => eprintln!("JIT: WASM says: {s}"), // TODO: this adds too many newlines since go calls this in chunks + Err(e) => { + let bytes = e.as_bytes(); + eprintln!("Go string {} is not valid utf8: {e:?}", hex::encode(bytes)); + } + } + } +} + +pub struct GoRuntimeState { + /// An increasing clock used when Go asks for time, measured in nanoseconds. + pub time: u64, + /// Deterministic source of random data. + pub rng: Pcg32, +} + +impl Default for GoRuntimeState { + fn default() -> Self { + Self { + time: 0, + rng: caller_env::create_pcg(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TimeoutInfo { + pub time: u64, + pub id: u32, +} + +impl Ord for TimeoutInfo { + fn cmp(&self, other: &Self) -> Ordering { + other + .time + .cmp(&self.time) + .then_with(|| other.id.cmp(&self.id)) + } +} + +impl PartialOrd for TimeoutInfo { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +#[derive(Default, Debug)] +pub struct TimeoutState { + /// Contains tuples of (time, id) + pub times: BinaryHeap, + pub pending_ids: BTreeSet, + pub next_id: u32, +} diff --git a/arbitrator/jit/src/color.rs b/arbitrator/jit/src/color.rs deleted file mode 100644 index 05b51d73bf..0000000000 --- a/arbitrator/jit/src/color.rs +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2020-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE - -#![allow(dead_code)] - -use std::fmt; - -pub const RED: &str = "\x1b[31;1m"; -pub const BLUE: &str = "\x1b[34;1m"; -pub const YELLOW: &str = "\x1b[33;1m"; -pub const PINK: &str = "\x1b[38;5;161;1m"; -pub const MINT: &str = "\x1b[38;5;48;1m"; -pub const GREY: &str = "\x1b[90m"; -pub const RESET: &str = "\x1b[0;0m"; - -pub const LIME: &str = "\x1b[38;5;119;1m"; -pub const LAVENDER: &str = "\x1b[38;5;183;1m"; -pub const MAROON: &str = "\x1b[38;5;124;1m"; -pub const ORANGE: &str = "\x1b[38;5;202;1m"; - -pub fn color(color: &str, text: S) -> String { - format!("{}{}{}", color, text, RESET) -} - -/// Colors text red. -pub fn red(text: S) -> String { - color(RED, text) -} - -/// Colors text blue. -pub fn blue(text: S) -> String { - color(BLUE, text) -} - -/// Colors text yellow. -pub fn yellow(text: S) -> String { - color(YELLOW, text) -} - -/// Colors text pink. -pub fn pink(text: S) -> String { - color(PINK, text) -} - -/// Colors text grey. -pub fn grey(text: S) -> String { - color(GREY, text) -} - -/// Colors text lavender. -pub fn lavender(text: S) -> String { - color(LAVENDER, text) -} - -/// Colors text mint. -pub fn mint(text: S) -> String { - color(MINT, text) -} - -/// Colors text lime. -pub fn lime(text: S) -> String { - color(LIME, text) -} - -/// Colors text orange. -pub fn orange(text: S) -> String { - color(ORANGE, text) -} - -/// Colors text maroon. -pub fn maroon(text: S) -> String { - color(MAROON, text) -} - -/// Color a bool one of two colors depending on its value. -pub fn color_if(cond: bool, true_color: &str, false_color: &str) -> String { - match cond { - true => color(true_color, &format!("{cond}")), - false => color(false_color, &format!("{cond}")), - } -} - -/// Color a bool if true -pub fn when(cond: bool, text: S, when_color: &str) -> String { - match cond { - true => color(when_color, text), - false => format!("{text}"), - } -} diff --git a/arbitrator/jit/src/gostack.rs b/arbitrator/jit/src/gostack.rs deleted file mode 100644 index bf7ac47675..0000000000 --- a/arbitrator/jit/src/gostack.rs +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE - -#![allow(clippy::useless_transmute)] - -use crate::{ - machine::{WasmEnv, WasmEnvMut}, - syscall::JsValue, -}; - -use ouroboros::self_referencing; -use rand_pcg::Pcg32; -use wasmer::{AsStoreRef, Memory, MemoryView, StoreRef, WasmPtr}; - -use std::collections::{BTreeSet, BinaryHeap}; - -#[self_referencing] -struct MemoryViewContainer { - memory: Memory, - #[borrows(memory)] - #[covariant] - view: MemoryView<'this>, -} - -impl MemoryViewContainer { - fn create(env: &WasmEnvMut<'_>) -> Self { - // this func exists to properly constrain the closure's type - fn closure<'a>( - store: &'a StoreRef, - ) -> impl (for<'b> FnOnce(&'b Memory) -> MemoryView<'b>) + 'a { - move |memory: &Memory| memory.view(&store) - } - - let store = env.as_store_ref(); - let memory = env.data().memory.clone().unwrap(); - let view_builder = closure(&store); - MemoryViewContainerBuilder { - memory, - view_builder, - } - .build() - } - - fn view(&self) -> &MemoryView { - self.borrow_view() - } -} - -pub struct GoStack { - start: u32, - memory: MemoryViewContainer, -} - -#[allow(dead_code)] -impl GoStack { - pub fn new<'a, 'b: 'a>(start: u32, env: &'a mut WasmEnvMut<'b>) -> (Self, &'a mut WasmEnv) { - let memory = MemoryViewContainer::create(env); - let sp = Self { start, memory }; - (sp, env.data_mut()) - } - - pub fn simple(start: u32, env: &WasmEnvMut<'_>) -> Self { - let memory = MemoryViewContainer::create(env); - Self { start, memory } - } - - pub fn shift_start(&mut self, offset: u32) { - self.start += offset; - } - - fn view(&self) -> &MemoryView { - self.memory.view() - } - - /// Returns the memory size, in bytes. - /// note: wasmer measures memory in 65536-byte pages. - pub fn memory_size(&self) -> u64 { - self.view().size().0 as u64 * 65536 - } - - pub fn relative_offset(&self, arg: u32) -> u32 { - (arg + 1) * 8 - } - - fn offset(&self, arg: u32) -> u32 { - self.start + self.relative_offset(arg) - } - - pub fn read_u8(&self, arg: u32) -> u8 { - self.read_u8_ptr(self.offset(arg)) - } - - pub fn read_u32(&self, arg: u32) -> u32 { - self.read_u32_ptr(self.offset(arg)) - } - - pub fn read_u64(&self, arg: u32) -> u64 { - self.read_u64_ptr(self.offset(arg)) - } - - pub fn read_u8_ptr(&self, ptr: u32) -> u8 { - let ptr: WasmPtr = WasmPtr::new(ptr); - ptr.deref(self.view()).read().unwrap() - } - - pub fn read_u32_ptr(&self, ptr: u32) -> u32 { - let ptr: WasmPtr = WasmPtr::new(ptr); - ptr.deref(self.view()).read().unwrap() - } - - pub fn read_u64_ptr(&self, ptr: u32) -> u64 { - let ptr: WasmPtr = WasmPtr::new(ptr); - ptr.deref(self.view()).read().unwrap() - } - - pub fn write_u8(&self, arg: u32, x: u8) { - self.write_u8_ptr(self.offset(arg), x); - } - - pub fn write_u32(&self, arg: u32, x: u32) { - self.write_u32_ptr(self.offset(arg), x); - } - - pub fn write_u64(&self, arg: u32, x: u64) { - self.write_u64_ptr(self.offset(arg), x); - } - - pub fn write_u8_ptr(&self, ptr: u32, x: u8) { - let ptr: WasmPtr = WasmPtr::new(ptr); - ptr.deref(self.view()).write(x).unwrap(); - } - - pub fn write_u32_ptr(&self, ptr: u32, x: u32) { - let ptr: WasmPtr = WasmPtr::new(ptr); - ptr.deref(self.view()).write(x).unwrap(); - } - - pub fn write_u64_ptr(&self, ptr: u32, x: u64) { - let ptr: WasmPtr = WasmPtr::new(ptr); - ptr.deref(self.view()).write(x).unwrap(); - } - - pub fn read_slice(&self, ptr: u64, len: u64) -> Vec { - u32::try_from(ptr).expect("Go pointer not a u32"); // kept for consistency - let len = u32::try_from(len).expect("length isn't a u32") as usize; - let mut data = vec![0; len]; - self.view().read(ptr, &mut data).expect("failed to read"); - data - } - - pub fn write_slice(&self, ptr: u64, src: &[u8]) { - u32::try_from(ptr).expect("Go pointer not a u32"); - self.view().write(ptr, src).unwrap(); - } - - pub fn read_value_slice(&self, mut ptr: u64, len: u64) -> Vec { - let mut values = Vec::new(); - for _ in 0..len { - let p = u32::try_from(ptr).expect("Go pointer not a u32"); - values.push(JsValue::new(self.read_u64_ptr(p))); - ptr += 8; - } - values - } -} - -pub struct GoRuntimeState { - /// An increasing clock used when Go asks for time, measured in nanoseconds - pub time: u64, - /// The amount of time advanced each check. Currently 10 milliseconds - pub time_interval: u64, - /// The state of Go's timeouts - pub timeouts: TimeoutState, - /// Deterministic source of random data - pub rng: Pcg32, -} - -impl Default for GoRuntimeState { - fn default() -> Self { - Self { - time: 0, - time_interval: 10_000_000, - timeouts: TimeoutState::default(), - rng: Pcg32::new(0xcafef00dd15ea5e5, 0xa02bdbf7bb3c0a7), - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct TimeoutInfo { - pub time: u64, - pub id: u32, -} - -impl Ord for TimeoutInfo { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - other - .time - .cmp(&self.time) - .then_with(|| other.id.cmp(&self.id)) - } -} - -impl PartialOrd for TimeoutInfo { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -#[derive(Default, Debug)] -pub struct TimeoutState { - /// Contains tuples of (time, id) - pub times: BinaryHeap, - pub pending_ids: BTreeSet, - pub next_id: u32, -} diff --git a/arbitrator/jit/src/machine.rs b/arbitrator/jit/src/machine.rs index c9119dd16e..f51970c6dd 100644 --- a/arbitrator/jit/src/machine.rs +++ b/arbitrator/jit/src/machine.rs @@ -1,29 +1,28 @@ -// Copyright 2022, Offchain Labs, Inc. +// Copyright 2022-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE use crate::{ - arbcompress, gostack::GoRuntimeState, runtime, socket, syscall, syscall::JsRuntimeState, - wavmio, wavmio::Bytes32, Opts, + arbcompress, caller_env::GoRuntimeState, program, socket, stylus_backend::CothreadHandler, + wasip1_stub, wavmio, Opts, }; - -use arbutil::{Color, PreimageType}; -use eyre::{bail, Result, WrapErr}; +use arbutil::{Bytes32, Color, PreimageType}; +use eyre::{bail, ErrReport, Result, WrapErr}; use sha3::{Digest, Keccak256}; -use thiserror::Error; -use wasmer::{ - imports, CompilerConfig, Function, FunctionEnv, FunctionEnvMut, Instance, Memory, Module, - RuntimeError, Store, TypedFunction, -}; -use wasmer_compiler_cranelift::Cranelift; - use std::{ - collections::BTreeMap, + collections::{BTreeMap, HashMap}, fs::File, io::{self, Write}, io::{BufReader, BufWriter, ErrorKind, Read}, net::TcpStream, - time::Instant, + sync::Arc, + time::{Duration, Instant}, }; +use thiserror::Error; +use wasmer::{ + imports, CompilerConfig, Function, FunctionEnv, FunctionEnvMut, Instance, Memory, Module, + Pages, RuntimeError, Store, +}; +use wasmer_compiler_cranelift::Cranelift; pub fn create(opts: &Opts, env: WasmEnv) -> (Instance, FunctionEnv, Store) { let file = &opts.binary; @@ -60,64 +59,77 @@ pub fn create(opts: &Opts, env: WasmEnv) -> (Instance, FunctionEnv, Sto }; let func_env = FunctionEnv::new(&mut store, env); - macro_rules! native { - ($func:expr) => { - Function::new_typed(&mut store, $func) - }; - } macro_rules! func { ($func:expr) => { Function::new_typed_with_env(&mut store, &func_env, $func) }; } - let imports = imports! { - "go" => { - "debug" => native!(runtime::go_debug), - - "runtime.resetMemoryDataView" => native!(runtime::reset_memory_data_view), - "runtime.wasmExit" => func!(runtime::wasm_exit), - "runtime.wasmWrite" => func!(runtime::wasm_write), - "runtime.nanotime1" => func!(runtime::nanotime1), - "runtime.walltime" => func!(runtime::walltime), - "runtime.walltime1" => func!(runtime::walltime1), - "runtime.scheduleTimeoutEvent" => func!(runtime::schedule_timeout_event), - "runtime.clearTimeoutEvent" => func!(runtime::clear_timeout_event), - "runtime.getRandomData" => func!(runtime::get_random_data), - - "syscall/js.finalizeRef" => func!(syscall::js_finalize_ref), - "syscall/js.stringVal" => func!(syscall::js_string_val), - "syscall/js.valueGet" => func!(syscall::js_value_get), - "syscall/js.valueSet" => func!(syscall::js_value_set), - "syscall/js.valueDelete" => func!(syscall::js_value_delete), - "syscall/js.valueIndex" => func!(syscall::js_value_index), - "syscall/js.valueSetIndex" => func!(syscall::js_value_set_index), - "syscall/js.valueCall" => func!(syscall::js_value_call), - "syscall/js.valueInvoke" => func!(syscall::js_value_invoke), - "syscall/js.valueNew" => func!(syscall::js_value_new), - "syscall/js.valueLength" => func!(syscall::js_value_length), - "syscall/js.valuePrepareString" => func!(syscall::js_value_prepare_string), - "syscall/js.valueLoadString" => func!(syscall::js_value_load_string), - "syscall/js.valueInstanceOf" => func!(syscall::js_value_instance_of), - "syscall/js.copyBytesToGo" => func!(syscall::js_copy_bytes_to_go), - "syscall/js.copyBytesToJS" => func!(syscall::js_copy_bytes_to_js), - - "github.com/offchainlabs/nitro/wavmio.getGlobalStateBytes32" => func!(wavmio::get_global_state_bytes32), - "github.com/offchainlabs/nitro/wavmio.setGlobalStateBytes32" => func!(wavmio::set_global_state_bytes32), - "github.com/offchainlabs/nitro/wavmio.getGlobalStateU64" => func!(wavmio::get_global_state_u64), - "github.com/offchainlabs/nitro/wavmio.setGlobalStateU64" => func!(wavmio::set_global_state_u64), - "github.com/offchainlabs/nitro/wavmio.readInboxMessage" => func!(wavmio::read_inbox_message), - "github.com/offchainlabs/nitro/wavmio.readDelayedInboxMessage" => func!(wavmio::read_delayed_inbox_message), - "github.com/offchainlabs/nitro/wavmio.resolvePreImage" => { + "arbcompress" => { + "brotli_compress" => func!(arbcompress::brotli_compress), + "brotli_decompress" => func!(arbcompress::brotli_decompress), + }, + "wavmio" => { + "getGlobalStateBytes32" => func!(wavmio::get_global_state_bytes32), + "setGlobalStateBytes32" => func!(wavmio::set_global_state_bytes32), + "getGlobalStateU64" => func!(wavmio::get_global_state_u64), + "setGlobalStateU64" => func!(wavmio::set_global_state_u64), + "readInboxMessage" => func!(wavmio::read_inbox_message), + "readDelayedInboxMessage" => func!(wavmio::read_delayed_inbox_message), + "resolvePreImage" => { #[allow(deprecated)] // we're just keeping this around until we no longer need to validate old replay binaries { func!(wavmio::resolve_keccak_preimage) } }, - "github.com/offchainlabs/nitro/wavmio.resolveTypedPreimage" => func!(wavmio::resolve_typed_preimage), - - "github.com/offchainlabs/nitro/arbcompress.brotliCompress" => func!(arbcompress::brotli_compress), - "github.com/offchainlabs/nitro/arbcompress.brotliDecompress" => func!(arbcompress::brotli_decompress), + "resolveTypedPreimage" => func!(wavmio::resolve_typed_preimage), + }, + "wasi_snapshot_preview1" => { + "proc_exit" => func!(wasip1_stub::proc_exit), + "environ_sizes_get" => func!(wasip1_stub::environ_sizes_get), + "fd_write" => func!(wasip1_stub::fd_write), + "environ_get" => func!(wasip1_stub::environ_get), + "fd_close" => func!(wasip1_stub::fd_close), + "fd_read" => func!(wasip1_stub::fd_read), + "fd_readdir" => func!(wasip1_stub::fd_readdir), + "fd_sync" => func!(wasip1_stub::fd_sync), + "fd_seek" => func!(wasip1_stub::fd_seek), + "fd_datasync" => func!(wasip1_stub::fd_datasync), + "path_open" => func!(wasip1_stub::path_open), + "path_create_directory" => func!(wasip1_stub::path_create_directory), + "path_remove_directory" => func!(wasip1_stub::path_remove_directory), + "path_readlink" => func!(wasip1_stub::path_readlink), + "path_rename" => func!(wasip1_stub::path_rename), + "path_filestat_get" => func!(wasip1_stub::path_filestat_get), + "path_unlink_file" => func!(wasip1_stub::path_unlink_file), + "fd_prestat_get" => func!(wasip1_stub::fd_prestat_get), + "fd_prestat_dir_name" => func!(wasip1_stub::fd_prestat_dir_name), + "fd_filestat_get" => func!(wasip1_stub::fd_filestat_get), + "fd_filestat_set_size" => func!(wasip1_stub::fd_filestat_set_size), + "fd_pread" => func!(wasip1_stub::fd_pread), + "fd_pwrite" => func!(wasip1_stub::fd_pwrite), + "sock_accept" => func!(wasip1_stub::sock_accept), + "sock_shutdown" => func!(wasip1_stub::sock_shutdown), + "sched_yield" => func!(wasip1_stub::sched_yield), + "clock_time_get" => func!(wasip1_stub::clock_time_get), + "random_get" => func!(wasip1_stub::random_get), + "args_sizes_get" => func!(wasip1_stub::args_sizes_get), + "args_get" => func!(wasip1_stub::args_get), + "poll_oneoff" => func!(wasip1_stub::poll_oneoff), + "fd_fdstat_get" => func!(wasip1_stub::fd_fdstat_get), + "fd_fdstat_set_flags" => func!(wasip1_stub::fd_fdstat_set_flags), + }, + "programs" => { + "new_program" => func!(program::new_program), + "pop" => func!(program::pop), + "set_response" => func!(program::set_response), + "get_request" => func!(program::get_request), + "get_request_data" => func!(program::get_request_data), + "start_program" => func!(program::start_program), + "send_response" => func!(program::send_response), + "create_stylus_config" => func!(program::create_stylus_config), + "create_evm_data" => func!(program::create_evm_data), + "activate" => func!(program::activate), }, }; @@ -125,23 +137,13 @@ pub fn create(opts: &Opts, env: WasmEnv) -> (Instance, FunctionEnv, Sto Ok(instance) => instance, Err(err) => panic!("Failed to create instance: {}", err.red()), }; - let memory = match instance.exports.get_memory("mem") { + let memory = match instance.exports.get_memory("memory") { Ok(memory) => memory.clone(), Err(err) => panic!("Failed to get memory: {}", err.red()), }; - let resume = match instance.exports.get_typed_function(&store, "resume") { - Ok(resume) => resume, - Err(err) => panic!("Failed to get the {} func: {}", "resume".red(), err.red()), - }; - let getsp = match instance.exports.get_typed_function(&store, "getsp") { - Ok(getsp) => getsp, - Err(err) => panic!("Failed to get the {} func: {}", "getsp".red(), err.red()), - }; let env = func_env.as_mut(&mut store); env.memory = Some(memory); - env.exports.resume = Some(resume); - env.exports.get_stack_pointer = Some(getsp); (instance, func_env, store) } @@ -153,6 +155,8 @@ pub enum Escape { Failure(String), #[error("hostio failed with `{0}`")] HostIO(String), + #[error("comms with child instance failed with `{0}`")] + Child(ErrReport), #[error("hostio socket failed with `{0}`")] SocketError(#[from] io::Error), } @@ -164,13 +168,9 @@ impl Escape { Err(Self::Exit(code)) } - pub fn hostio>(message: S) -> MaybeEscape { + pub fn hostio>(message: S) -> Result { Err(Self::HostIO(message.as_ref().to_string())) } - - pub fn failure>(message: S) -> MaybeEscape { - Err(Self::Failure(message.as_ref().to_string())) - } } impl From for Escape { @@ -184,7 +184,8 @@ impl From for Escape { pub type WasmEnvMut<'a> = FunctionEnvMut<'a, WasmEnv>; pub type Inbox = BTreeMap>; -pub type Preimages = BTreeMap>>; +pub type Preimages = BTreeMap>>; +pub type ModuleAsm = Arc<[u8]>; #[derive(Default)] pub struct WasmEnv { @@ -192,22 +193,22 @@ pub struct WasmEnv { pub memory: Option, /// Go's general runtime state pub go_state: GoRuntimeState, - /// The state of Go's js runtime - pub js_state: JsRuntimeState, /// An ordered list of the 8-byte globals pub small_globals: [u64; 2], /// An ordered list of the 32-byte globals pub large_globals: [Bytes32; 2], /// An oracle allowing the prover to reverse keccak256 pub preimages: Preimages, + /// A collection of programs called during the course of execution + pub module_asms: HashMap, /// The sequencer inbox's messages pub sequencer_messages: Inbox, /// The delayed inbox's messages pub delayed_messages: Inbox, /// The purpose and connections of this process pub process: ProcessEnv, - /// The exported funcs callable in hostio - pub exports: WasmEnvFuncs, + // threads + pub threads: Vec, } impl WasmEnv { @@ -264,10 +265,10 @@ impl WasmEnv { if arg.starts_with("0x") { arg = &arg[2..]; } - let mut bytes32 = Bytes32::default(); + let mut bytes32 = [0u8; 32]; hex::decode_to_slice(arg, &mut bytes32) .wrap_err_with(|| format!("failed to parse {} contents", name))?; - Ok(bytes32) + Ok(bytes32.into()) } None => Ok(Bytes32::default()), } @@ -280,7 +281,7 @@ impl WasmEnv { Ok(env) } - pub fn send_results(&mut self, error: Option, memory_used: u64) { + pub fn send_results(&mut self, error: Option, memory_used: Pages) { let writer = match &mut self.process.socket { Some((writer, _)) => writer, None => return, @@ -307,7 +308,7 @@ impl WasmEnv { check!(socket::write_u64(writer, self.small_globals[1])); check!(socket::write_bytes32(writer, &self.large_globals[0])); check!(socket::write_bytes32(writer, &self.large_globals[1])); - check!(socket::write_u64(writer, memory_used)); + check!(socket::write_u64(writer, memory_used.bytes().0 as u64)); check!(writer.flush()); } } @@ -321,6 +322,8 @@ pub struct ProcessEnv { pub socket: Option<(BufWriter, BufReader)>, /// A timestamp that helps with printing at various moments pub timestamp: Instant, + /// How long to wait on any child threads to compute a result + pub child_timeout: Duration, /// Whether the machine has reached the first wavmio instruction pub reached_wavmio: bool, } @@ -332,15 +335,8 @@ impl Default for ProcessEnv { debug: false, socket: None, timestamp: Instant::now(), + child_timeout: Duration::from_secs(15), reached_wavmio: false, } } } - -#[derive(Default)] -pub struct WasmEnvFuncs { - /// Calls `resume` from the go runtime - pub resume: Option>, - /// Calls `getsp` from the go runtime - pub get_stack_pointer: Option>, -} diff --git a/arbitrator/jit/src/main.rs b/arbitrator/jit/src/main.rs index 968da2a978..e432dc215c 100644 --- a/arbitrator/jit/src/main.rs +++ b/arbitrator/jit/src/main.rs @@ -1,21 +1,20 @@ -// Copyright 2022, Offchain Labs, Inc. +// Copyright 2022-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE use crate::machine::{Escape, WasmEnv}; - use arbutil::{color, Color}; -use structopt::StructOpt; -use wasmer::Value; - +use eyre::Result; use std::path::PathBuf; +use structopt::StructOpt; mod arbcompress; -mod gostack; +mod caller_env; mod machine; -mod runtime; +mod program; mod socket; -mod syscall; +mod stylus_backend; mod test; +mod wasip1_stub; mod wavmio; #[derive(StructOpt)] @@ -45,37 +44,24 @@ pub struct Opts { forks: bool, #[structopt(long)] debug: bool, + #[structopt(long)] + require_success: bool, } -fn main() { +fn main() -> Result<()> { let opts = Opts::from_args(); - let env = match WasmEnv::cli(&opts) { Ok(env) => env, - Err(err) => panic!("{}", err), + Err(err) => panic!("{err}"), }; let (instance, env, mut store) = machine::create(&opts, env); - let memory = instance.exports.get_memory("mem").unwrap(); - let memory = memory.view(&store); - - // To pass in the program name argument, we need to put it in memory. - // The Go linker guarantees a section of memory starting at byte 4096 is available for this purpose. - // https://github.com/golang/go/blob/252324e879e32f948d885f787decf8af06f82be9/misc/wasm/wasm_exec.js#L520 - let free_memory_base: i32 = 4096; - let name = free_memory_base; - let argv = name + 8; - - memory.write(name as u64, b"js\0").unwrap(); // write "js\0" to the name ptr - memory.write(argv as u64, &name.to_le_bytes()).unwrap(); // write the name ptr to the argv ptr - let run_args = &[Value::I32(1), Value::I32(argv)]; // pass argv with our single name arg - - let main = instance.exports.get_function("run").unwrap(); - let outcome = main.call(&mut store, run_args); + let main = instance.exports.get_function("_start").unwrap(); + let outcome = main.call(&mut store, &[]); let escape = match outcome { Ok(outcome) => { - println!("Go returned values {:?}", outcome); + println!("Go returned values {outcome:?}"); None } Err(outcome) => { @@ -92,6 +78,13 @@ fn main() { } }; + let memory_used = instance + .exports + .get_memory("memory") + .unwrap() + .view(&store) + .size(); + let env = env.as_mut(&mut store); let user = env.process.socket.is_none(); let time = format!("{}ms", env.process.timestamp.elapsed().as_millis()); @@ -102,11 +95,12 @@ fn main() { Some(Escape::Exit(x)) => (false, format!("Failed in {time} with exit code {x}.")), Some(Escape::Failure(err)) => (false, format!("Jit failed with {err} in {time}.")), Some(Escape::HostIO(err)) => (false, format!("Hostio failed with {err} in {time}.")), + Some(Escape::Child(err)) => (false, format!("Child failed with {err} in {time}.")), Some(Escape::SocketError(err)) => (false, format!("Socket failed with {err} in {time}.")), None => (false, "Machine exited prematurely".to_owned()), }; - if opts.debug { + if opts.debug || !success { println!("{message}"); } @@ -114,9 +108,13 @@ fn main() { true => None, false => Some(message), }; - let memory_used = memory.size().0 as u64 * 65_536; env.send_results(error, memory_used); + + if !success && opts.require_success { + std::process::exit(1); + } + Ok(()) } // require a usize be at least 32 bits wide diff --git a/arbitrator/jit/src/program.rs b/arbitrator/jit/src/program.rs new file mode 100644 index 0000000000..c608a3cf85 --- /dev/null +++ b/arbitrator/jit/src/program.rs @@ -0,0 +1,265 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![allow(clippy::too_many_arguments)] + +use crate::caller_env::JitEnv; +use crate::machine::{Escape, MaybeEscape, WasmEnvMut}; +use crate::stylus_backend::exec_wasm; +use arbutil::Bytes32; +use arbutil::{evm::EvmData, format::DebugBytes, heapify}; +use caller_env::{GuestPtr, MemAccess}; +use eyre::eyre; +use prover::programs::prelude::StylusConfig; +use prover::{ + machine::Module, + programs::{config::PricingParams, prelude::*}, +}; + +/// activates a user program +pub fn activate( + mut env: WasmEnvMut, + wasm_ptr: GuestPtr, + wasm_size: u32, + pages_ptr: GuestPtr, + asm_estimate_ptr: GuestPtr, + init_cost_ptr: GuestPtr, + cached_init_cost_ptr: GuestPtr, + version: u16, + debug: u32, + codehash: GuestPtr, + module_hash_ptr: GuestPtr, + gas_ptr: GuestPtr, + err_buf: GuestPtr, + err_buf_len: u32, +) -> Result { + let (mut mem, _) = env.jit_env(); + let wasm = mem.read_slice(wasm_ptr, wasm_size as usize); + let codehash = &mem.read_bytes32(codehash); + let debug = debug != 0; + + let page_limit = mem.read_u16(pages_ptr); + let gas_left = &mut mem.read_u64(gas_ptr); + match Module::activate(&wasm, codehash, version, page_limit, debug, gas_left) { + Ok((module, data)) => { + mem.write_u64(gas_ptr, *gas_left); + mem.write_u16(pages_ptr, data.footprint); + mem.write_u32(asm_estimate_ptr, data.asm_estimate); + mem.write_u16(init_cost_ptr, data.init_cost); + mem.write_u16(cached_init_cost_ptr, data.cached_init_cost); + mem.write_bytes32(module_hash_ptr, module.hash()); + Ok(0) + } + Err(error) => { + let mut err_bytes = error.wrap_err("failed to activate").debug_bytes(); + err_bytes.truncate(err_buf_len as usize); + mem.write_slice(err_buf, &err_bytes); + mem.write_u64(gas_ptr, 0); + mem.write_u16(pages_ptr, 0); + mem.write_u32(asm_estimate_ptr, 0); + mem.write_u16(init_cost_ptr, 0); + mem.write_u16(cached_init_cost_ptr, 0); + mem.write_bytes32(module_hash_ptr, Bytes32::default()); + Ok(err_bytes.len() as u32) + } + } +} + +/// Links and creates user program (in jit starts it as well) +/// consumes both evm_data_handler and config_handler +/// returns module number +pub fn new_program( + mut env: WasmEnvMut, + compiled_hash_ptr: GuestPtr, + calldata_ptr: GuestPtr, + calldata_size: u32, + stylus_config_handler: u64, + evm_data_handler: u64, + gas: u64, +) -> Result { + let (mut mem, exec) = env.jit_env(); + let compiled_hash = mem.read_bytes32(compiled_hash_ptr); + let calldata = mem.read_slice(calldata_ptr, calldata_size as usize); + let evm_data: EvmData = unsafe { *Box::from_raw(evm_data_handler as *mut EvmData) }; + let config: JitConfig = unsafe { *Box::from_raw(stylus_config_handler as *mut JitConfig) }; + + // buy ink + let pricing = config.stylus.pricing; + let ink = pricing.gas_to_ink(gas); + + let Some(module) = exec.module_asms.get(&compiled_hash).cloned() else { + return Err(Escape::Failure(format!( + "module hash {:?} not found in {:?}", + compiled_hash, + exec.module_asms.keys() + ))); + }; + + let cothread = exec_wasm( + module, + calldata, + config.compile, + config.stylus, + evm_data, + ink, + ) + .unwrap(); + + exec.threads.push(cothread); + + Ok(exec.threads.len() as u32) +} + +/// starts the program (in jit waits for first request) +/// module MUST match last module number returned from new_program +/// returns request_id for the first request from the program +pub fn start_program(mut env: WasmEnvMut, module: u32) -> Result { + let (_, exec) = env.jit_env(); + + if exec.threads.len() as u32 != module || module == 0 { + return Escape::hostio(format!( + "got request for thread {module} but len is {}", + exec.threads.len() + )); + } + let thread = exec.threads.last_mut().unwrap(); + thread.wait_next_message()?; + let msg = thread.last_message()?; + Ok(msg.1) +} + +/// gets information about request according to id +/// request_id MUST be last request id returned from start_program or send_response +pub fn get_request(mut env: WasmEnvMut, id: u32, len_ptr: GuestPtr) -> Result { + let (mut mem, exec) = env.jit_env(); + let thread = exec.threads.last_mut().unwrap(); + let msg = thread.last_message()?; + if msg.1 != id { + return Escape::hostio("get_request id doesn't match"); + }; + mem.write_u32(len_ptr, msg.0.req_data.len() as u32); + Ok(msg.0.req_type) +} + +// gets data associated with last request. +// request_id MUST be last request receieved +// data_ptr MUST point to a buffer of at least the length returned by get_request +pub fn get_request_data(mut env: WasmEnvMut, id: u32, data_ptr: GuestPtr) -> MaybeEscape { + let (mut mem, exec) = env.jit_env(); + let thread = exec.threads.last_mut().unwrap(); + let msg = thread.last_message()?; + if msg.1 != id { + return Escape::hostio("get_request id doesn't match"); + }; + mem.write_slice(data_ptr, &msg.0.req_data); + Ok(()) +} + +/// sets response for the next request made +/// id MUST be the id of last request made +pub fn set_response( + mut env: WasmEnvMut, + id: u32, + gas: u64, + result_ptr: GuestPtr, + result_len: u32, + raw_data_ptr: GuestPtr, + raw_data_len: u32, +) -> MaybeEscape { + let (mem, exec) = env.jit_env(); + let result = mem.read_slice(result_ptr, result_len as usize); + let raw_data = mem.read_slice(raw_data_ptr, raw_data_len as usize); + + let thread = exec.threads.last_mut().unwrap(); + thread.set_response(id, result, raw_data, gas) +} + +/// sends previos response +/// MUST be called right after set_response to the same id +/// returns request_id for the next request +pub fn send_response(mut env: WasmEnvMut, req_id: u32) -> Result { + let (_, exec) = env.jit_env(); + let thread = exec.threads.last_mut().unwrap(); + let msg = thread.last_message()?; + if msg.1 != req_id { + return Escape::hostio("get_request id doesn't match"); + }; + thread.wait_next_message()?; + let msg = thread.last_message()?; + Ok(msg.1) +} + +/// removes the last created program +pub fn pop(mut env: WasmEnvMut) -> MaybeEscape { + let (_, exec) = env.jit_env(); + + match exec.threads.pop() { + None => Err(Escape::Child(eyre!("no child"))), + Some(mut thread) => thread.wait_done(), + } +} + +pub struct JitConfig { + stylus: StylusConfig, + compile: CompileConfig, +} + +/// Creates a `StylusConfig` from its component parts. +pub fn create_stylus_config( + mut _env: WasmEnvMut, + version: u16, + max_depth: u32, + ink_price: u32, + debug: u32, +) -> Result { + let stylus = StylusConfig { + version, + max_depth, + pricing: PricingParams { ink_price }, + }; + let compile = CompileConfig::version(version, debug != 0); + let res = heapify(JitConfig { stylus, compile }); + Ok(res as u64) +} + +/// Creates an `EvmData` handler from its component parts. +pub fn create_evm_data( + mut env: WasmEnvMut, + block_basefee_ptr: GuestPtr, + chainid: u64, + block_coinbase_ptr: GuestPtr, + block_gas_limit: u64, + block_number: u64, + block_timestamp: u64, + contract_address_ptr: GuestPtr, + module_hash_ptr: GuestPtr, + msg_sender_ptr: GuestPtr, + msg_value_ptr: GuestPtr, + tx_gas_price_ptr: GuestPtr, + tx_origin_ptr: GuestPtr, + cached: u32, + reentrant: u32, +) -> Result { + let (mut mem, _) = env.jit_env(); + + let evm_data = EvmData { + block_basefee: mem.read_bytes32(block_basefee_ptr), + cached: cached != 0, + chainid, + block_coinbase: mem.read_bytes20(block_coinbase_ptr), + block_gas_limit, + block_number, + block_timestamp, + contract_address: mem.read_bytes20(contract_address_ptr), + module_hash: mem.read_bytes32(module_hash_ptr), + msg_sender: mem.read_bytes20(msg_sender_ptr), + msg_value: mem.read_bytes32(msg_value_ptr), + tx_gas_price: mem.read_bytes32(tx_gas_price_ptr), + tx_origin: mem.read_bytes20(tx_origin_ptr), + reentrant, + return_data_len: 0, + tracing: false, + }; + let res = heapify(evm_data); + Ok(res as u64) +} diff --git a/arbitrator/jit/src/runtime.rs b/arbitrator/jit/src/runtime.rs deleted file mode 100644 index d547a06553..0000000000 --- a/arbitrator/jit/src/runtime.rs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE - -use crate::{ - gostack::{GoStack, TimeoutInfo}, - machine::{Escape, MaybeEscape, WasmEnvMut}, -}; - -use rand::RngCore; - -use std::io::Write; - -pub fn go_debug(x: u32) { - println!("go debug: {x}") -} - -pub fn reset_memory_data_view(_: u32) {} - -pub fn wasm_exit(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { - let (sp, _) = GoStack::new(sp, &mut env); - Escape::exit(sp.read_u32(0)) -} - -pub fn wasm_write(mut env: WasmEnvMut, sp: u32) { - let (sp, _) = GoStack::new(sp, &mut env); - let fd = sp.read_u64(0); - let ptr = sp.read_u64(1); - let len = sp.read_u32(2); - let buf = sp.read_slice(ptr, len.into()); - if fd == 2 { - let stderr = std::io::stderr(); - let mut stderr = stderr.lock(); - stderr.write_all(&buf).unwrap(); - } else { - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - stdout.write_all(&buf).unwrap(); - } -} - -pub fn nanotime1(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - env.go_state.time += env.go_state.time_interval; - sp.write_u64(0, env.go_state.time); -} - -pub fn walltime(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - env.go_state.time += env.go_state.time_interval; - sp.write_u64(0, env.go_state.time / 1_000_000_000); - sp.write_u32(1, (env.go_state.time % 1_000_000_000) as u32); -} - -pub fn walltime1(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - env.go_state.time += env.go_state.time_interval; - sp.write_u64(0, env.go_state.time / 1_000_000_000); - sp.write_u64(1, env.go_state.time % 1_000_000_000); -} - -pub fn schedule_timeout_event(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - let mut time = sp.read_u64(0); - time = time.saturating_mul(1_000_000); // milliseconds to nanoseconds - time = time.saturating_add(env.go_state.time); // add the current time to the delay - - let timeouts = &mut env.go_state.timeouts; - let id = timeouts.next_id; - timeouts.next_id += 1; - timeouts.times.push(TimeoutInfo { time, id }); - timeouts.pending_ids.insert(id); - - sp.write_u32(1, id); -} - -pub fn clear_timeout_event(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - - let id = sp.read_u32(0); - if !env.go_state.timeouts.pending_ids.remove(&id) { - eprintln!("Go attempting to clear not pending timeout event {id}"); - } -} - -pub fn get_random_data(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - - let mut ptr = u32::try_from(sp.read_u64(0)).expect("Go getRandomData pointer not a u32"); - let mut len = sp.read_u64(1); - while len >= 4 { - let next = env.go_state.rng.next_u32(); - sp.write_u32_ptr(ptr, next); - ptr += 4; - len -= 4; - } - if len > 0 { - let mut rem = env.go_state.rng.next_u32(); - for _ in 0..len { - sp.write_u8_ptr(ptr, rem as u8); - ptr += 1; - rem >>= 8; - } - } -} diff --git a/arbitrator/jit/src/socket.rs b/arbitrator/jit/src/socket.rs index 3941763a0d..004b8eb441 100644 --- a/arbitrator/jit/src/socket.rs +++ b/arbitrator/jit/src/socket.rs @@ -1,16 +1,16 @@ -// Copyright 2022, Offchain Labs, Inc. +// Copyright 2022-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE +use arbutil::Bytes32; use std::{ io, io::{BufReader, BufWriter, Read, Write}, net::TcpStream, }; -use crate::wavmio::Bytes32; - pub const SUCCESS: u8 = 0x0; pub const FAILURE: u8 = 0x1; +// pub const PREIMAGE: u8 = 0x2; // not used pub const ANOTHER: u8 = 0x3; pub const READY: u8 = 0x4; @@ -19,14 +19,19 @@ pub fn read_u8(reader: &mut BufReader) -> Result { reader.read_exact(&mut buf).map(|_| u8::from_be_bytes(buf)) } +pub fn read_u32(reader: &mut BufReader) -> Result { + let mut buf = [0; 4]; + reader.read_exact(&mut buf).map(|_| u32::from_be_bytes(buf)) +} + pub fn read_u64(reader: &mut BufReader) -> Result { let mut buf = [0; 8]; reader.read_exact(&mut buf).map(|_| u64::from_be_bytes(buf)) } pub fn read_bytes32(reader: &mut BufReader) -> Result { - let mut buf = Bytes32::default(); - reader.read_exact(&mut buf).map(|_| buf) + let mut buf = [0u8; 32]; + reader.read_exact(&mut buf).map(|_| buf.into()) } pub fn read_bytes(reader: &mut BufReader) -> Result, io::Error> { @@ -36,6 +41,10 @@ pub fn read_bytes(reader: &mut BufReader) -> Result, io::Err Ok(buf) } +pub fn read_boxed_slice(reader: &mut BufReader) -> Result, io::Error> { + Ok(Vec::into_boxed_slice(read_bytes(reader)?)) +} + pub fn write_u8(writer: &mut BufWriter, data: u8) -> Result<(), io::Error> { let buf = [data; 1]; writer.write_all(&buf) @@ -47,7 +56,7 @@ pub fn write_u64(writer: &mut BufWriter, data: u64) -> Result<(), io: } pub fn write_bytes32(writer: &mut BufWriter, data: &Bytes32) -> Result<(), io::Error> { - writer.write_all(data) + writer.write_all(data.as_slice()) } pub fn write_bytes(writer: &mut BufWriter, data: &[u8]) -> Result<(), io::Error> { diff --git a/arbitrator/jit/src/stylus_backend.rs b/arbitrator/jit/src/stylus_backend.rs new file mode 100644 index 0000000000..61dbf258d4 --- /dev/null +++ b/arbitrator/jit/src/stylus_backend.rs @@ -0,0 +1,188 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![allow(clippy::too_many_arguments)] + +use crate::machine::{Escape, MaybeEscape}; +use arbutil::evm::api::VecReader; +use arbutil::evm::{ + api::{EvmApiMethod, EVM_API_METHOD_REQ_OFFSET}, + req::EvmApiRequestor, + req::RequestHandler, + user::UserOutcome, + EvmData, +}; +use eyre::{eyre, Result}; +use prover::programs::prelude::*; +use std::thread; +use std::time::Duration; +use std::{ + sync::{ + mpsc::{self, Receiver, SyncSender}, + Arc, + }, + thread::JoinHandle, +}; +use stylus::{native::NativeInstance, run::RunProgram}; + +struct MessageToCothread { + result: Vec, + raw_data: Vec, + cost: u64, +} + +#[derive(Clone)] +pub struct MessageFromCothread { + pub req_type: u32, + pub req_data: Vec, +} + +struct CothreadRequestor { + tx: SyncSender, + rx: Receiver, +} + +impl RequestHandler for CothreadRequestor { + fn request( + &mut self, + req_type: EvmApiMethod, + req_data: impl AsRef<[u8]>, + ) -> (Vec, VecReader, u64) { + let msg = MessageFromCothread { + req_type: req_type as u32 + EVM_API_METHOD_REQ_OFFSET, + req_data: req_data.as_ref().to_vec(), + }; + + if let Err(error) = self.tx.send(msg) { + panic!("failed sending request from cothread: {error}"); + } + match self.rx.recv_timeout(Duration::from_secs(5)) { + Ok(response) => ( + response.result, + VecReader::new(response.raw_data), + response.cost, + ), + Err(_) => panic!("no response from main thread"), + } + } +} + +pub struct CothreadHandler { + tx: SyncSender, + rx: Receiver, + thread: Option>, + last_request: Option<(MessageFromCothread, u32)>, +} + +impl CothreadHandler { + pub fn wait_next_message(&mut self) -> MaybeEscape { + let msg = self.rx.recv_timeout(Duration::from_secs(10)); + let Ok(msg) = msg else { + return Escape::hostio("did not receive message"); + }; + self.last_request = Some((msg, 0x33333333)); // TODO: Ids + Ok(()) + } + + pub fn wait_done(&mut self) -> MaybeEscape { + let error = || Escape::Child(eyre!("no child")); + let status = self.thread.take().ok_or_else(error)?.join(); + match status { + Ok(res) => res, + Err(_) => Escape::hostio("failed joining child process"), + } + } + + pub fn last_message(&self) -> Result<(MessageFromCothread, u32), Escape> { + self.last_request + .clone() + .ok_or_else(|| Escape::HostIO("no message waiting".to_string())) + } + + pub fn set_response( + &mut self, + id: u32, + result: Vec, + raw_data: Vec, + cost: u64, + ) -> MaybeEscape { + let Some(msg) = self.last_request.clone() else { + return Escape::hostio("trying to set response but no message pending"); + }; + if msg.1 != id { + return Escape::hostio("trying to set response for wrong message id"); + }; + let msg = MessageToCothread { + result, + raw_data, + cost, + }; + if let Err(err) = self.tx.send(msg) { + return Escape::hostio(format!("failed to send response to stylus thread: {err:?}")); + }; + Ok(()) + } +} + +/// Executes a wasm on a new thread +pub fn exec_wasm( + module: Arc<[u8]>, + calldata: Vec, + compile: CompileConfig, + config: StylusConfig, + evm_data: EvmData, + ink: u64, +) -> Result { + let (tothread_tx, tothread_rx) = mpsc::sync_channel::(0); + let (fromthread_tx, fromthread_rx) = mpsc::sync_channel::(0); + + let cothread = CothreadRequestor { + tx: fromthread_tx, + rx: tothread_rx, + }; + + let evm_api = EvmApiRequestor::new(cothread); + + let mut instance = + unsafe { NativeInstance::deserialize(&module, compile.clone(), evm_api, evm_data) }?; + + let thread = thread::spawn(move || { + let outcome = instance.run_main(&calldata, config, ink); + + let ink_left = match outcome.as_ref() { + Ok(UserOutcome::OutOfStack) => 0, // take all ink when out of stack + _ => instance.ink_left().into(), + }; + + let outcome = match outcome { + Err(e) | Ok(UserOutcome::Failure(e)) => UserOutcome::Failure(e.wrap_err("call failed")), + Ok(outcome) => outcome, + }; + + let (out_kind, data) = outcome.into_data(); + let gas_left = config.pricing.ink_to_gas(ink_left); + + let mut output = Vec::with_capacity(8 + data.len()); + output.extend(gas_left.to_be_bytes()); + output.extend(data); + + let msg = MessageFromCothread { + req_data: output, + req_type: out_kind as u32, + }; + instance + .env_mut() + .evm_api + .request_handler() + .tx + .send(msg) + .or_else(|_| Escape::hostio("failed sending messaage to thread")) + }); + + Ok(CothreadHandler { + tx: tothread_tx, + rx: fromthread_rx, + thread: Some(thread), + last_request: None, + }) +} diff --git a/arbitrator/jit/src/syscall.rs b/arbitrator/jit/src/syscall.rs deleted file mode 100644 index 4f657eeefa..0000000000 --- a/arbitrator/jit/src/syscall.rs +++ /dev/null @@ -1,592 +0,0 @@ -// Copyright 2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE - -use crate::{ - gostack::GoStack, - machine::{Escape, MaybeEscape, WasmEnv, WasmEnvMut}, -}; - -use arbutil::Color; -use rand::RngCore; -use wasmer::AsStoreMut; - -use std::{collections::BTreeMap, io::Write}; - -const ZERO_ID: u32 = 1; -const NULL_ID: u32 = 2; -const GLOBAL_ID: u32 = 5; -const GO_ID: u32 = 6; - -const OBJECT_ID: u32 = 100; -const ARRAY_ID: u32 = 101; -const PROCESS_ID: u32 = 102; -const FS_ID: u32 = 103; -const UINT8_ARRAY_ID: u32 = 104; -const CRYPTO_ID: u32 = 105; -const DATE_ID: u32 = 106; - -const FS_CONSTANTS_ID: u32 = 200; - -const DYNAMIC_OBJECT_ID_BASE: u32 = 10000; - -#[derive(Default)] -pub struct JsRuntimeState { - /// A collection of js objects - pool: DynamicObjectPool, - /// The event Go will execute next - pub pending_event: Option, -} - -#[derive(Clone, Default, Debug)] -struct DynamicObjectPool { - objects: BTreeMap, - free_ids: Vec, -} - -impl DynamicObjectPool { - fn insert(&mut self, object: DynamicObject) -> u32 { - let id = self - .free_ids - .pop() - .unwrap_or(DYNAMIC_OBJECT_ID_BASE + self.objects.len() as u32); - self.objects.insert(id, object); - id - } - - fn get(&self, id: u32) -> Option<&DynamicObject> { - self.objects.get(&id) - } - - fn get_mut(&mut self, id: u32) -> Option<&mut DynamicObject> { - self.objects.get_mut(&id) - } - - fn remove(&mut self, id: u32) -> Option { - let res = self.objects.remove(&id); - if res.is_some() { - self.free_ids.push(id); - } - res - } -} - -#[derive(Debug, Clone)] -enum DynamicObject { - Uint8Array(Vec), - FunctionWrapper(JsValue, JsValue), - PendingEvent(PendingEvent), - ValueArray(Vec), - Date, -} - -#[derive(Clone, Debug)] -pub struct PendingEvent { - pub id: JsValue, - pub this: JsValue, - pub args: Vec, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum JsValue { - Undefined, - Number(f64), - Ref(u32), -} - -impl JsValue { - fn assume_num_or_object(self) -> GoValue { - match self { - JsValue::Undefined => GoValue::Undefined, - JsValue::Number(x) => GoValue::Number(x), - JsValue::Ref(x) => GoValue::Object(x), - } - } - - /// Creates a JS runtime value from its native 64-bit floating point representation. - /// The JS runtime stores handles to references in the NaN bits. - /// Native 0 is the value called "undefined", and actual 0 is a special-cased NaN. - /// Anything else that's not a NaN is the Number class. - pub fn new(repr: u64) -> Self { - if repr == 0 { - return Self::Undefined; - } - let float = f64::from_bits(repr); - if float.is_nan() && repr != f64::NAN.to_bits() { - let id = repr as u32; - if id == ZERO_ID { - return Self::Number(0.); - } - return Self::Ref(id); - } - Self::Number(float) - } -} - -#[derive(Clone, Copy, Debug)] -#[allow(dead_code)] -pub enum GoValue { - Undefined, - Number(f64), - Null, - Object(u32), - String(u32), - Symbol(u32), - Function(u32), -} - -impl GoValue { - fn encode(self) -> u64 { - let (ty, id): (u32, u32) = match self { - GoValue::Undefined => return 0, - GoValue::Number(mut f) => { - // Canonicalize NaNs so they don't collide with other value types - if f.is_nan() { - f = f64::NAN; - } - if f == 0. { - // Zeroes are encoded differently for some reason - (0, ZERO_ID) - } else { - return f.to_bits(); - } - } - GoValue::Null => (0, NULL_ID), - GoValue::Object(x) => (1, x), - GoValue::String(x) => (2, x), - GoValue::Symbol(x) => (3, x), - GoValue::Function(x) => (4, x), - }; - // Must not be all zeroes, otherwise it'd collide with a real NaN - assert!(ty != 0 || id != 0, "GoValue must not be empty"); - f64::NAN.to_bits() | (u64::from(ty) << 32) | u64::from(id) - } -} - -fn get_field(env: &mut WasmEnv, source: u32, field: &[u8]) -> GoValue { - use DynamicObject::*; - - if let Some(source) = env.js_state.pool.get(source) { - return match (source, field) { - (PendingEvent(event), b"id" | b"this") => event.id.assume_num_or_object(), - (PendingEvent(event), b"args") => { - let args = ValueArray(event.args.clone()); - let id = env.js_state.pool.insert(args); - GoValue::Object(id) - } - _ => { - let field = String::from_utf8_lossy(field); - eprintln!( - "Go trying to access unimplemented unknown JS value {:?} field {field}", - source - ); - GoValue::Undefined - } - }; - } - - match (source, field) { - (GLOBAL_ID, b"Object") => GoValue::Function(OBJECT_ID), - (GLOBAL_ID, b"Array") => GoValue::Function(ARRAY_ID), - (GLOBAL_ID, b"process") => GoValue::Object(PROCESS_ID), - (GLOBAL_ID, b"fs") => GoValue::Object(FS_ID), - (GLOBAL_ID, b"Uint8Array") => GoValue::Function(UINT8_ARRAY_ID), - (GLOBAL_ID, b"crypto") => GoValue::Object(CRYPTO_ID), - (GLOBAL_ID, b"Date") => GoValue::Object(DATE_ID), - (GLOBAL_ID, b"fetch") => GoValue::Undefined, // Triggers a code path in Go for a fake network impl - (FS_ID, b"constants") => GoValue::Object(FS_CONSTANTS_ID), - ( - FS_CONSTANTS_ID, - b"O_WRONLY" | b"O_RDWR" | b"O_CREAT" | b"O_TRUNC" | b"O_APPEND" | b"O_EXCL", - ) => GoValue::Number(-1.), - (GO_ID, b"_pendingEvent") => match &mut env.js_state.pending_event { - Some(event) => { - let event = PendingEvent(event.clone()); - let id = env.js_state.pool.insert(event); - GoValue::Object(id) - } - None => GoValue::Null, - }, - _ => { - let field = String::from_utf8_lossy(field); - eprintln!("Go trying to access unimplemented unknown JS value {source} field {field}"); - GoValue::Undefined - } - } -} - -pub fn js_finalize_ref(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - let pool = &mut env.js_state.pool; - - let val = JsValue::new(sp.read_u64(0)); - match val { - JsValue::Ref(x) if x < DYNAMIC_OBJECT_ID_BASE => {} - JsValue::Ref(x) => { - if pool.remove(x).is_none() { - eprintln!("Go trying to finalize unknown ref {}", x); - } - } - val => eprintln!("Go trying to finalize {:?}", val), - } -} - -pub fn js_value_get(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - let source = JsValue::new(sp.read_u64(0)); - let field_ptr = sp.read_u64(1); - let field_len = sp.read_u64(2); - let field = sp.read_slice(field_ptr, field_len); - let value = match source { - JsValue::Ref(id) => get_field(env, id, &field), - val => { - let field = String::from_utf8_lossy(&field); - eprintln!("Go trying to read field {:?} . {field}", val); - GoValue::Null - } - }; - sp.write_u64(3, value.encode()); -} - -pub fn js_value_set(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - use JsValue::*; - - let source = JsValue::new(sp.read_u64(0)); - let field_ptr = sp.read_u64(1); - let field_len = sp.read_u64(2); - let new_value = JsValue::new(sp.read_u64(3)); - let field = sp.read_slice(field_ptr, field_len); - if source == Ref(GO_ID) && &field == b"_pendingEvent" && new_value == Ref(NULL_ID) { - env.js_state.pending_event = None; - return; - } - if let Ref(id) = source { - let source = env.js_state.pool.get(id); - if let Some(DynamicObject::PendingEvent(_)) = source { - if field == b"result" { - return; - } - } - } - let field = String::from_utf8_lossy(&field); - eprintln!( - "Go attempted to set unsupported value {:?} field {field} to {:?}", - source, new_value, - ); -} - -pub fn js_value_index(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - - macro_rules! fail { - ($text:expr $(,$args:expr)*) => {{ - eprintln!($text $(,$args)*); - return sp.write_u64(2, GoValue::Null.encode()); - }}; - } - - let source = match JsValue::new(sp.read_u64(0)) { - JsValue::Ref(x) => env.js_state.pool.get(x), - val => fail!("Go attempted to index into {:?}", val), - }; - let index = match u32::try_from(sp.read_u64(1)) { - Ok(index) => index as usize, - Err(err) => fail!("{:?}", err), - }; - let value = match source { - Some(DynamicObject::Uint8Array(x)) => x.get(index).map(|x| GoValue::Number(*x as f64)), - Some(DynamicObject::ValueArray(x)) => x.get(index).cloned(), - _ => fail!("Go attempted to index into unsupported value {:?}", source), - }; - let Some(value) = value else { - fail!("Go indexing out of bounds into {:?} index {index}", source) - }; - sp.write_u64(2, value.encode()); -} - -pub fn js_value_call(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { - let Some(resume) = env.data().exports.resume.clone() else { - return Escape::failure(format!("wasmer failed to bind {}", "resume".red())); - }; - let Some(get_stack_pointer) = env.data().exports.get_stack_pointer.clone() else { - return Escape::failure(format!("wasmer failed to bind {}", "getsp".red())); - }; - let sp = GoStack::simple(sp, &env); - let data = env.data_mut(); - let rng = &mut data.go_state.rng; - let pool = &mut data.js_state.pool; - use JsValue::*; - - let object = JsValue::new(sp.read_u64(0)); - let method_name_ptr = sp.read_u64(1); - let method_name_len = sp.read_u64(2); - let method_name = sp.read_slice(method_name_ptr, method_name_len); - let args_ptr = sp.read_u64(3); - let args_len = sp.read_u64(4); - let args = sp.read_value_slice(args_ptr, args_len); - let name = String::from_utf8_lossy(&method_name); - - macro_rules! fail { - ($text:expr $(,$args:expr)*) => {{ - eprintln!($text $(,$args)*); - sp.write_u64(6, GoValue::Null.encode()); - sp.write_u8(7, 1); - return Ok(()) - }}; - } - - let value = match (object, method_name.as_slice()) { - (Ref(GO_ID), b"_makeFuncWrapper") => { - let arg = match args.first() { - Some(arg) => arg, - None => fail!( - "Go trying to call Go._makeFuncWrapper with bad args {:?}", - args - ), - }; - let ref_id = pool.insert(DynamicObject::FunctionWrapper(*arg, object)); - GoValue::Function(ref_id) - } - (Ref(FS_ID), b"write") => { - // ignore any args after the 6th, and slice no more than than the number of args we have - let args_len = std::cmp::min(6, args.len()); - - match &args.as_slice()[..args_len] { - &[Number(fd), Ref(buf_id), Number(offset), Number(length), Ref(NULL_ID), Ref(callback_id)] => - { - let buf = match pool.get(buf_id) { - Some(DynamicObject::Uint8Array(x)) => x, - x => fail!("Go trying to call fs.write with bad buffer {:?}", x), - }; - let (func_id, this) = match pool.get(callback_id) { - Some(DynamicObject::FunctionWrapper(f, t)) => (f, t), - x => fail!("Go trying to call fs.write with bad buffer {:?}", x), - }; - - let mut offset = offset as usize; - let mut length = length as usize; - if offset > buf.len() { - eprintln!( - "Go trying to call fs.write with offset {offset} >= buf.len() {length}" - ); - offset = buf.len(); - } - if offset + length > buf.len() { - eprintln!( - "Go trying to call fs.write with offset {offset} + length {length} >= buf.len() {}", - buf.len(), - ); - length = buf.len() - offset; - } - if fd == 1. { - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - stdout.write_all(&buf[offset..(offset + length)]).unwrap(); - } else if fd == 2. { - let stderr = std::io::stderr(); - let mut stderr = stderr.lock(); - stderr.write_all(&buf[offset..(offset + length)]).unwrap(); - } else { - eprintln!("Go trying to write to unknown FD {}", fd); - } - - data.js_state.pending_event = Some(PendingEvent { - id: *func_id, - this: *this, - args: vec![ - GoValue::Null, // no error - GoValue::Number(length as f64), // amount written - ], - }); - - // recursively call into wasmer - let mut store = env.as_store_mut(); - resume.call(&mut store)?; - - // the stack pointer has changed, so we'll need to write our return results elsewhere - let pointer = get_stack_pointer.call(&mut store)? as u32; - sp.write_u64_ptr(pointer + sp.relative_offset(6), GoValue::Null.encode()); - sp.write_u8_ptr(pointer + sp.relative_offset(7), 1); - return Ok(()); - } - _ => fail!("Go trying to call fs.write with bad args {:?}", args), - } - } - (Ref(CRYPTO_ID), b"getRandomValues") => { - let name = "crypto.getRandomValues"; - - let id = match args.first() { - Some(Ref(x)) => x, - _ => fail!("Go trying to call {name} with bad args {:?}", args), - }; - - let buf = match pool.get_mut(*id) { - Some(DynamicObject::Uint8Array(buf)) => buf, - Some(x) => fail!("Go trying to call {name} on bad object {:?}", x), - None => fail!("Go trying to call {name} on unknown reference {id}"), - }; - - rng.fill_bytes(buf.as_mut_slice()); - GoValue::Undefined - } - (Ref(obj_id), _) => { - let value = match pool.get(obj_id) { - Some(value) => value, - None => fail!("Go trying to call method {name} for unknown object - id {obj_id}"), - }; - match value { - DynamicObject::Date => GoValue::Number(0.0), - _ => fail!("Go trying to call unknown method {name} for date object"), - } - } - _ => fail!("Go trying to call unknown method {:?} . {name}", object), - }; - - sp.write_u64(6, value.encode()); - sp.write_u8(7, 1); - Ok(()) -} - -pub fn js_value_new(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - let pool = &mut env.js_state.pool; - - let class = sp.read_u32(0); - let args_ptr = sp.read_u64(1); - let args_len = sp.read_u64(2); - let args = sp.read_value_slice(args_ptr, args_len); - match class { - UINT8_ARRAY_ID => match args.first() { - Some(JsValue::Number(size)) => { - let id = pool.insert(DynamicObject::Uint8Array(vec![0; *size as usize])); - sp.write_u64(4, GoValue::Object(id).encode()); - sp.write_u8(5, 1); - return; - } - _ => eprintln!( - "Go attempted to construct Uint8Array with bad args: {:?}", - args, - ), - }, - DATE_ID => { - let id = pool.insert(DynamicObject::Date); - sp.write_u64(4, GoValue::Object(id).encode()); - sp.write_u8(5, 1); - return; - } - _ => eprintln!("Go trying to construct unimplemented JS value {class}"), - } - sp.write_u64(4, GoValue::Null.encode()); - sp.write_u8(5, 0); -} - -pub fn js_value_length(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - - let source = match JsValue::new(sp.read_u64(0)) { - JsValue::Ref(x) => env.js_state.pool.get(x), - _ => None, - }; - let length = match source { - Some(DynamicObject::Uint8Array(x)) => x.len(), - Some(DynamicObject::ValueArray(x)) => x.len(), - _ => { - eprintln!( - "Go attempted to get length of unsupported value {:?}", - source, - ); - 0 - } - }; - sp.write_u64(1, length as u64); -} - -pub fn js_copy_bytes_to_go(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - let dest_ptr = sp.read_u64(0); - let dest_len = sp.read_u64(1); - let src_val = JsValue::new(sp.read_u64(3)); - - match src_val { - JsValue::Ref(src_id) => match env.js_state.pool.get_mut(src_id) { - Some(DynamicObject::Uint8Array(buf)) => { - let src_len = buf.len() as u64; - if src_len != dest_len { - eprintln!( - "Go copying bytes from JS source length {src_len} to Go dest length {dest_len}", - ); - } - let len = std::cmp::min(src_len, dest_len) as usize; - sp.write_slice(dest_ptr, &buf[..len]); - sp.write_u64(4, GoValue::Number(len as f64).encode()); - sp.write_u8(5, 1); - return; - } - source => { - eprintln!( - "Go trying to copy bytes from unsupported source {:?}", - source, - ); - } - }, - _ => eprintln!("Go trying to copy bytes from {:?}", src_val), - } - - sp.write_u8(5, 0); -} - -pub fn js_copy_bytes_to_js(mut env: WasmEnvMut, sp: u32) { - let (sp, env) = GoStack::new(sp, &mut env); - - match JsValue::new(sp.read_u64(0)) { - JsValue::Ref(dest_id) => { - let src_ptr = sp.read_u64(1); - let src_len = sp.read_u64(2); - - match env.js_state.pool.get_mut(dest_id) { - Some(DynamicObject::Uint8Array(buf)) => { - let dest_len = buf.len() as u64; - if buf.len() as u64 != src_len { - eprintln!( - "Go copying bytes from Go source length {src_len} to JS dest length {dest_len}", - ); - } - let len = std::cmp::min(src_len, dest_len) as usize; - - // Slightly inefficient as this allocates a new temporary buffer - let data = sp.read_slice(src_ptr, len as u64); - buf[..len].copy_from_slice(&data); - sp.write_u64(4, GoValue::Number(len as f64).encode()); - sp.write_u8(5, 1); - return; - } - dest => eprintln!("Go trying to copy bytes into unsupported target {:?}", dest), - } - } - value => eprintln!("Go trying to copy bytes into {:?}", value), - } - - sp.write_u64(4, GoValue::Null.encode()); - sp.write_u8(5, 0); -} - -macro_rules! unimpl_js { - ($($f:ident),* $(,)?) => { - $( - #[no_mangle] - pub fn $f(_: WasmEnvMut, _: u32) { - unimplemented!("Go JS interface {} not supported", stringify!($f)); - } - )* - } -} - -unimpl_js!( - js_string_val, - js_value_set_index, - js_value_prepare_string, - js_value_load_string, - js_value_delete, - js_value_invoke, - js_value_instance_of, -); diff --git a/arbitrator/jit/src/test.rs b/arbitrator/jit/src/test.rs index 517c8596c0..621b47125c 100644 --- a/arbitrator/jit/src/test.rs +++ b/arbitrator/jit/src/test.rs @@ -3,10 +3,11 @@ #![cfg(test)] +use eyre::Result; use wasmer::{imports, Instance, Module, Store, Value}; #[test] -fn test_crate() -> eyre::Result<()> { +fn test_crate() -> Result<()> { // Adapted from https://docs.rs/wasmer/3.1.0/wasmer/index.html let source = std::fs::read("programs/pure/main.wat")?; diff --git a/arbitrator/jit/src/wasip1_stub.rs b/arbitrator/jit/src/wasip1_stub.rs new file mode 100644 index 0000000000..0b525e6a97 --- /dev/null +++ b/arbitrator/jit/src/wasip1_stub.rs @@ -0,0 +1,162 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +#![allow(clippy::too_many_arguments)] + +use crate::caller_env::{JitEnv, JitExecEnv}; +use crate::machine::{Escape, WasmEnvMut}; +use caller_env::{self, wasip1_stub::Errno, GuestPtr}; + +pub fn proc_exit(mut _env: WasmEnvMut, code: u32) -> Result<(), Escape> { + Err(Escape::Exit(code)) +} + +macro_rules! wrap { + ($(fn $func_name:ident ($($arg_name:ident : $arg_type:ty),* ) -> $return_type:ty);*) => { + $( + pub fn $func_name(mut src: WasmEnvMut, $($arg_name : $arg_type),*) -> Result<$return_type, Escape> { + let (mut mem, wenv) = src.jit_env(); + + Ok(caller_env::wasip1_stub::$func_name(&mut mem, &mut JitExecEnv { wenv }, $($arg_name),*)) + } + )* + }; +} + +wrap! { + fn clock_time_get( + clock_id: u32, + precision: u64, + time_ptr: GuestPtr + ) -> Errno; + + fn random_get(buf: GuestPtr, len: u32) -> Errno; + + fn environ_get(a: GuestPtr, b: GuestPtr) -> Errno; + fn environ_sizes_get(length_ptr: GuestPtr, data_size_ptr: GuestPtr) -> Errno; + + fn fd_read(a: u32, b: u32, c: u32, d: u32) -> Errno; + fn fd_close(fd: u32) -> Errno; + fn fd_write( + fd: u32, + iovecs_ptr: GuestPtr, + iovecs_len: u32, + ret_ptr: GuestPtr + ) -> Errno; + + fn fd_readdir( + fd: u32, + a: u32, + b: u32, + c: u64, + d: u32 + ) -> Errno; + + fn fd_sync(a: u32) -> Errno; + + fn fd_seek( + fd: u32, + offset: u64, + whence: u8, + filesize: u32 + ) -> Errno; + + fn fd_datasync(_fd: u32) -> Errno; + + fn path_open( + a: u32, + b: u32, + c: u32, + d: u32, + e: u32, + f: u64, + g: u64, + h: u32, + i: u32 + ) -> Errno; + + fn path_create_directory( + a: u32, + b: u32, + c: u32 + ) -> Errno; + + fn path_remove_directory( + a: u32, + b: u32, + c: u32 + ) -> Errno; + + fn path_readlink( + a: u32, + b: u32, + c: u32, + d: u32, + e: u32, + f: u32 + ) -> Errno; + + fn path_rename( + a: u32, + b: u32, + c: u32, + d: u32, + e: u32, + f: u32 + ) -> Errno; + + fn path_filestat_get( + a: u32, + b: u32, + c: u32, + d: u32, + e: u32 + ) -> Errno; + + fn path_unlink_file(a: u32, b: u32, c: u32) -> Errno; + + fn fd_prestat_get(a: u32, b: u32) -> Errno; + fn fd_prestat_dir_name(a: u32, b: u32, c: u32) -> Errno; + + fn fd_filestat_get(fd: u32, _filestat: u32) -> Errno; + fn fd_filestat_set_size(fd: u32, size: u64) -> Errno; + + fn fd_pread( + fd: u32, + a: u32, + b: u32, + c: u64, + d: u32 + ) -> Errno; + + fn fd_pwrite( + fd: u32, + a: u32, + b: u32, + c: u64, + d: u32 + ) -> Errno; + + fn sock_accept(_fd: u32, a: u32, b: u32) -> Errno; + fn sock_shutdown(a: u32, b: u32) -> Errno; + + fn sched_yield() -> Errno; + + fn args_sizes_get( + length_ptr: GuestPtr, + data_size_ptr: GuestPtr + ) -> Errno; + + fn args_get(argv_buf: GuestPtr, data_buf: GuestPtr) -> Errno; + + fn fd_fdstat_get(a: u32, b: u32) -> Errno; + fn fd_fdstat_set_flags(a: u32, b: u32) -> Errno; + + // we always simulate a timeout + fn poll_oneoff( + in_subs: GuestPtr, + out_evt: GuestPtr, + nsubscriptions: u32, + nevents_ptr: GuestPtr + ) -> Errno +} diff --git a/arbitrator/jit/src/wavmio.rs b/arbitrator/jit/src/wavmio.rs index dfc7f21779..062d18d8e9 100644 --- a/arbitrator/jit/src/wavmio.rs +++ b/arbitrator/jit/src/wavmio.rs @@ -1,13 +1,15 @@ -// Copyright 2022, Offchain Labs, Inc. +// Copyright 2022-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE use crate::{ - gostack::GoStack, - machine::{Escape, Inbox, MaybeEscape, WasmEnv, WasmEnvMut}, + caller_env::JitEnv, + machine::{Escape, MaybeEscape, WasmEnv, WasmEnvMut}, socket, }; - use arbutil::{Color, PreimageType}; +use caller_env::{GuestPtr, MemAccess}; +use sha2::Sha256; +use sha3::{Digest, Keccak256}; use std::{ io, io::{BufReader, BufWriter, ErrorKind}, @@ -15,166 +17,139 @@ use std::{ time::Instant, }; -pub type Bytes32 = [u8; 32]; - -pub fn get_global_state_bytes32(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { - let (sp, env) = GoStack::new(sp, &mut env); - ready_hostio(env)?; - - let global = sp.read_u64(0) as u32 as usize; - let out_ptr = sp.read_u64(1); - let mut out_len = sp.read_u64(2) as usize; - if out_len < 32 { - eprintln!("Go trying to read block hash into {out_len} bytes long buffer"); - } else { - out_len = 32; - } +/// Reads 32-bytes of global state. +pub fn get_global_state_bytes32(mut env: WasmEnvMut, idx: u32, out_ptr: GuestPtr) -> MaybeEscape { + let (mut mem, exec) = env.jit_env(); + ready_hostio(exec)?; - let global = match env.large_globals.get(global) { - Some(global) => global, - None => return Escape::hostio("global read out of bounds in wavmio.getGlobalStateBytes32"), + let Some(global) = exec.large_globals.get(idx as usize) else { + return Escape::hostio("global read out of bounds in wavmio.getGlobalStateBytes32"); }; - sp.write_slice(out_ptr, &global[..out_len]); + mem.write_slice(out_ptr, &global[..32]); Ok(()) } -pub fn set_global_state_bytes32(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { - let (sp, env) = GoStack::new(sp, &mut env); - ready_hostio(env)?; +/// Writes 32-bytes of global state. +pub fn set_global_state_bytes32(mut env: WasmEnvMut, idx: u32, src_ptr: GuestPtr) -> MaybeEscape { + let (mem, exec) = env.jit_env(); + ready_hostio(exec)?; - let global = sp.read_u64(0) as u32 as usize; - let src_ptr = sp.read_u64(1); - let src_len = sp.read_u64(2); - if src_len != 32 { - eprintln!("Go trying to set 32-byte global with a {src_len} bytes long buffer"); - return Ok(()); - } - - let slice = sp.read_slice(src_ptr, src_len); + let slice = mem.read_slice(src_ptr, 32); let slice = &slice.try_into().unwrap(); - match env.large_globals.get_mut(global) { + match exec.large_globals.get_mut(idx as usize) { Some(global) => *global = *slice, - None => { - return Escape::hostio("global write out of bounds in wavmio.setGlobalStateBytes32") - } - } + None => return Escape::hostio("global write oob in wavmio.setGlobalStateBytes32"), + }; Ok(()) } -pub fn get_global_state_u64(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { - let (sp, env) = GoStack::new(sp, &mut env); - ready_hostio(env)?; +/// Reads 8-bytes of global state +pub fn get_global_state_u64(mut env: WasmEnvMut, idx: u32) -> Result { + let (_, exec) = env.jit_env(); + ready_hostio(exec)?; - let global = sp.read_u64(0) as u32 as usize; - match env.small_globals.get(global) { - Some(global) => sp.write_u64(1, *global), - None => return Escape::hostio("global read out of bounds in wavmio.getGlobalStateU64"), + match exec.small_globals.get(idx as usize) { + Some(global) => Ok(*global), + None => Escape::hostio("global read out of bounds in wavmio.getGlobalStateU64"), } - Ok(()) } -pub fn set_global_state_u64(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { - let (sp, env) = GoStack::new(sp, &mut env); - ready_hostio(env)?; +/// Writes 8-bytes of global state +pub fn set_global_state_u64(mut env: WasmEnvMut, idx: u32, val: u64) -> MaybeEscape { + let (_, exec) = env.jit_env(); + ready_hostio(exec)?; - let global = sp.read_u64(0) as u32 as usize; - match env.small_globals.get_mut(global) { - Some(global) => *global = sp.read_u64(1), + match exec.small_globals.get_mut(idx as usize) { + Some(global) => *global = val, None => return Escape::hostio("global write out of bounds in wavmio.setGlobalStateU64"), } Ok(()) } -pub fn read_inbox_message(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { - let (sp, env) = GoStack::new(sp, &mut env); - ready_hostio(env)?; - - let inbox = &env.sequencer_messages; - inbox_message_impl(&sp, inbox, "wavmio.readInboxMessage") -} - -pub fn read_delayed_inbox_message(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { - let (sp, env) = GoStack::new(sp, &mut env); - ready_hostio(env)?; - - let inbox = &env.delayed_messages; - inbox_message_impl(&sp, inbox, "wavmio.readDelayedInboxMessage") -} - -/// Reads an inbox message -/// note: the order of the checks is very important. -fn inbox_message_impl(sp: &GoStack, inbox: &Inbox, name: &str) -> MaybeEscape { - let msg_num = sp.read_u64(0); - let offset = sp.read_u64(1); - let out_ptr = sp.read_u64(2); - let out_len = sp.read_u64(3); - if out_len != 32 { - eprintln!("Go trying to read inbox message with out len {out_len} in {name}"); - sp.write_u64(5, 0); - return Ok(()); - } - - macro_rules! error { - ($text:expr $(,$args:expr)*) => {{ - let text = format!($text $(,$args)*); - return Escape::hostio(&text) - }}; - } - - let message = match inbox.get(&msg_num) { +/// Reads an inbox message. +pub fn read_inbox_message( + mut env: WasmEnvMut, + msg_num: u64, + offset: u32, + out_ptr: GuestPtr, +) -> Result { + let (mut mem, exec) = env.jit_env(); + ready_hostio(exec)?; + + let message = match exec.sequencer_messages.get(&msg_num) { Some(message) => message, - None => error!("missing inbox message {msg_num} in {name}"), + None => return Escape::hostio(format!("missing sequencer inbox message {msg_num}")), }; + let offset = offset as usize; + let len = std::cmp::min(32, message.len().saturating_sub(offset)); + let read = message.get(offset..(offset + len)).unwrap_or_default(); + mem.write_slice(out_ptr, read); + Ok(read.len() as u32) +} - if out_ptr + 32 > sp.memory_size() { - error!("unknown message type in {name}"); - } - let offset = match u32::try_from(offset) { - Ok(offset) => offset as usize, - Err(_) => error!("bad offset {offset} in {name}"), +/// Reads a delayed inbox message. +pub fn read_delayed_inbox_message( + mut env: WasmEnvMut, + msg_num: u64, + offset: u32, + out_ptr: GuestPtr, +) -> Result { + let (mut mem, exec) = env.jit_env(); + ready_hostio(exec)?; + + let message = match exec.delayed_messages.get(&msg_num) { + Some(message) => message, + None => return Escape::hostio(format!("missing delayed inbox message {msg_num}")), }; - + let offset = offset as usize; let len = std::cmp::min(32, message.len().saturating_sub(offset)); let read = message.get(offset..(offset + len)).unwrap_or_default(); - sp.write_slice(out_ptr, read); - sp.write_u64(5, read.len() as u64); - Ok(()) + mem.write_slice(out_ptr, read); + Ok(read.len() as u32) } +/// Retrieves the preimage of the given hash. #[deprecated] // we're just keeping this around until we no longer need to validate old replay binaries -pub fn resolve_keccak_preimage(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { - let (sp, env) = GoStack::new(sp, &mut env); - resolve_preimage_impl(env, sp, 0, "wavmio.ResolvePreImage") +pub fn resolve_keccak_preimage( + env: WasmEnvMut, + hash_ptr: GuestPtr, + offset: u32, + out_ptr: GuestPtr, +) -> Result { + resolve_preimage_impl(env, 0, hash_ptr, offset, out_ptr, "wavmio.ResolvePreImage") } -pub fn resolve_typed_preimage(mut env: WasmEnvMut, sp: u32) -> MaybeEscape { - let (mut sp, env) = GoStack::new(sp, &mut env); - let preimage_type = sp.read_u8(0); - sp.shift_start(8); // to account for the preimage type being the first slot - resolve_preimage_impl(env, sp, preimage_type, "wavmio.ResolveTypedPreimage") +pub fn resolve_typed_preimage( + env: WasmEnvMut, + preimage_type: u8, + hash_ptr: GuestPtr, + offset: u32, + out_ptr: GuestPtr, +) -> Result { + resolve_preimage_impl( + env, + preimage_type, + hash_ptr, + offset, + out_ptr, + "wavmio.ResolveTypedPreimage", + ) } pub fn resolve_preimage_impl( - env: &mut WasmEnv, - sp: GoStack, + mut env: WasmEnvMut, preimage_type: u8, + hash_ptr: GuestPtr, + offset: u32, + out_ptr: GuestPtr, name: &str, -) -> MaybeEscape { - let hash_ptr = sp.read_u64(0); - let hash_len = sp.read_u64(1); - let offset = sp.read_u64(3); - let out_ptr = sp.read_u64(4); - let out_len = sp.read_u64(5); - if hash_len != 32 || out_len != 32 { - eprintln!("Go trying to resolve pre image with hash len {hash_len} and out len {out_len}"); - sp.write_u64(7, 0); - return Ok(()); - } +) -> Result { + let (mut mem, exec) = env.jit_env(); + let offset = offset as usize; let Ok(preimage_type) = preimage_type.try_into() else { eprintln!("Go trying to resolve pre image with unknown type {preimage_type}"); - sp.write_u64(7, 0); - return Ok(()); + return Ok(0); }; macro_rules! error { @@ -184,24 +159,40 @@ pub fn resolve_preimage_impl( }}; } - let hash = sp.read_slice(hash_ptr, hash_len); - let hash: &[u8; 32] = &hash.try_into().unwrap(); - let hash_hex = hex::encode(hash); + let hash = mem.read_bytes32(hash_ptr); - let Some(preimage) = env.preimages.get(&preimage_type).and_then(|m| m.get(hash)) else { - error!("Missing requested preimage for preimage type {preimage_type:?} hash {hash_hex} in {name}"); + let Some(preimage) = exec + .preimages + .get(&preimage_type) + .and_then(|m| m.get(&hash)) + else { + let hash_hex = hex::encode(hash); + error!("Missing requested preimage for hash {hash_hex} in {name}") }; - let offset = match u32::try_from(offset) { - Ok(offset) if offset % 32 == 0 => offset as usize, - _ => error!("bad offset {offset} in {name}"), + // Check if preimage rehashes to the provided hash. Exclude blob preimages + let calculated_hash: [u8; 32] = match preimage_type { + PreimageType::Keccak256 => Keccak256::digest(preimage).into(), + PreimageType::Sha2_256 => Sha256::digest(preimage).into(), + PreimageType::EthVersionedHash => *hash, + }; + if calculated_hash != *hash { + error!( + "Calculated hash {} of preimage {} does not match provided hash {}", + hex::encode(calculated_hash), + hex::encode(preimage), + hex::encode(*hash) + ); + } + + if offset % 32 != 0 { + error!("bad offset {offset} in {name}") }; let len = std::cmp::min(32, preimage.len().saturating_sub(offset)); let read = preimage.get(offset..(offset + len)).unwrap_or_default(); - sp.write_slice(out_ptr, read); - sp.write_u64(7, read.len() as u64); - Ok(()) + mem.write_slice(out_ptr, read); + Ok(read.len() as u32) } fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { @@ -237,7 +228,7 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { address.pop(); // pop the newline if address.is_empty() { - return Ok(()); + return Escape::exit(0); } if debug { println!("Child will connect to {address}"); @@ -281,12 +272,12 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { env.delayed_messages.insert(position, message); } - let preimage_types = socket::read_u64(stream)?; + let preimage_types = socket::read_u32(stream)?; for _ in 0..preimage_types { let preimage_ty = PreimageType::try_from(socket::read_u8(stream)?) .map_err(|e| Escape::Failure(e.to_string()))?; let map = env.preimages.entry(preimage_ty).or_default(); - let preimage_count = socket::read_u64(stream)?; + let preimage_count = socket::read_u32(stream)?; for _ in 0..preimage_count { let hash = socket::read_bytes32(stream)?; let preimage = socket::read_bytes(stream)?; @@ -294,6 +285,13 @@ fn ready_hostio(env: &mut WasmEnv) -> MaybeEscape { } } + let programs_count = socket::read_u32(stream)?; + for _ in 0..programs_count { + let module_hash = socket::read_bytes32(stream)?; + let module_asm = socket::read_boxed_slice(stream)?; + env.module_asms.insert(module_hash, module_asm.into()); + } + if socket::read_u8(stream)? != socket::READY { return Escape::hostio("failed to parse global state"); } diff --git a/arbitrator/langs/bf b/arbitrator/langs/bf new file mode 160000 index 0000000000..062b87bad1 --- /dev/null +++ b/arbitrator/langs/bf @@ -0,0 +1 @@ +Subproject commit 062b87bad1ec00d42b9cc2b5ee41e63cd6ff1cbb diff --git a/arbitrator/langs/c b/arbitrator/langs/c new file mode 160000 index 0000000000..29fe05d686 --- /dev/null +++ b/arbitrator/langs/c @@ -0,0 +1 @@ +Subproject commit 29fe05d68672797572080084b0f5f0a282e298ef diff --git a/arbitrator/langs/rust b/arbitrator/langs/rust new file mode 160000 index 0000000000..7bb07e556d --- /dev/null +++ b/arbitrator/langs/rust @@ -0,0 +1 @@ +Subproject commit 7bb07e556d2da4e623f13bfb099a99f9d85cc297 diff --git a/arbitrator/prover/Cargo.toml b/arbitrator/prover/Cargo.toml index 51cbe84399..0b42013808 100644 --- a/arbitrator/prover/Cargo.toml +++ b/arbitrator/prover/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "prover" version = "0.1.0" -edition = "2018" +edition = "2021" publish = false [dependencies] bincode = "1.3.3" -brotli2 = "0.3.2" +derivative = "2.2.0" digest = "0.9.0" eyre = "0.6.5" fnv = "1.0.7" @@ -15,22 +15,38 @@ libc = "0.2.108" nom = "7.0.0" nom-leb128 = "0.2.0" num = "0.4" -rayon = "1.5.1" rustc-demangle = "0.1.21" serde = { version = "1.0.130", features = ["derive", "rc"] } serde_json = "1.0.67" sha3 = "0.9.1" static_assertions = "1.1.0" structopt = "0.3.23" -wasmparser = "0.84.0" -wat = "1.0.56" serde_with = "1.12.1" -lazy_static = "1.4.0" +parking_lot = "0.12.1" +lazy_static.workspace = true +itertools = "0.10.5" +wat = "1.0.56" smallvec = { version = "1.10.0", features = ["serde"] } +rayon = { version = "1.5.1", optional = true } arbutil = { path = "../arbutil/" } -c-kzg = "0.4.0" # TODO: look into switching to rust-kzg (no crates.io release or hosted rustdoc yet) +brotli = { path = "../brotli/" } +wasmer = { path = "../tools/wasmer/lib/api", optional = true } +wasmer-types = { path = "../tools/wasmer/lib/types" } +wasmer-compiler-singlepass = { path = "../tools/wasmer/lib/compiler-singlepass", optional = true, default-features = false, features = ["std", "unwind", "avx"] } +wasmparser.workspace = true +num-derive = "0.4.1" +num-traits = "0.2.17" +c-kzg = { version = "0.4.0", optional = true } # TODO: look into switching to rust-kzg (no crates.io release or hosted rustdoc yet) sha2 = "0.9.9" +lru = "0.12.3" +once_cell = "1.19.0" [lib] name = "prover" -crate-type = ["staticlib","lib"] +crate-type = ["staticlib", "lib"] + +[features] +default = ["native", "rayon", "singlepass_rayon"] +native = ["dep:wasmer", "dep:wasmer-compiler-singlepass", "brotli/wasmer_traits", "dep:c-kzg"] +singlepass_rayon = ["wasmer-compiler-singlepass?/rayon"] +rayon = ["dep:rayon"] diff --git a/arbitrator/prover/src/binary.rs b/arbitrator/prover/src/binary.rs index 3aa857449e..f6c3e9fe8f 100644 --- a/arbitrator/prover/src/binary.rs +++ b/arbitrator/prover/src/binary.rs @@ -1,9 +1,17 @@ -// Copyright 2021-2023, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE -use crate::value::{ArbValueType, FunctionType, IntegerValType, Value as LirValue}; -use eyre::{bail, ensure, Result}; -use fnv::FnvHashMap as HashMap; +use crate::{ + programs::{ + config::CompileConfig, counter::Counter, depth::DepthChecker, dynamic::DynamicMeter, + heap::HeapBound, meter::Meter, start::StartMover, FuncMiddleware, Middleware, ModuleMod, + StylusData, STYLUS_ENTRY_POINT, + }, + value::{ArbValueType, FunctionType, IntegerValType, Value}, +}; +use arbutil::{math::SaturatingSum, Bytes32, Color, DebugColor}; +use eyre::{bail, ensure, eyre, Result, WrapErr}; +use fnv::{FnvHashMap as HashMap, FnvHashSet as HashSet}; use nom::{ branch::alt, bytes::complete::tag, @@ -11,10 +19,11 @@ use nom::{ sequence::{preceded, tuple}, }; use serde::{Deserialize, Serialize}; -use std::{convert::TryInto, hash::Hash, str::FromStr}; +use std::{convert::TryInto, fmt::Debug, hash::Hash, mem, path::Path, str::FromStr}; +use wasmer_types::{entity::EntityRef, ExportIndex, FunctionIndex, LocalFunctionIndex}; use wasmparser::{ - Data, Element, Export, Global, Import, MemoryType, Name, NameSectionReader, Naming, Operator, - Parser, Payload, TableType, TypeDef, + Data, Element, ExternalKind, MemoryType, Name, NameSectionReader, Naming, Operator, Parser, + Payload, TableType, TypeRef, ValType, Validator, WasmFeatures, }; #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -70,22 +79,16 @@ pub enum FloatInstruction { impl FloatInstruction { pub fn signature(&self) -> FunctionType { match *self { - FloatInstruction::UnOp(t, _) => FunctionType::new(vec![t.into()], vec![t.into()]), - FloatInstruction::BinOp(t, _) => FunctionType::new(vec![t.into(); 2], vec![t.into()]), - FloatInstruction::RelOp(t, _) => { - FunctionType::new(vec![t.into(); 2], vec![ArbValueType::I32]) - } - FloatInstruction::TruncIntOp(i, f, ..) => { - FunctionType::new(vec![f.into()], vec![i.into()]) - } - FloatInstruction::ConvertIntOp(f, i, _) => { - FunctionType::new(vec![i.into()], vec![f.into()]) - } + FloatInstruction::UnOp(t, _) => FunctionType::new([t.into()], [t.into()]), + FloatInstruction::BinOp(t, _) => FunctionType::new([t.into(); 2], [t.into()]), + FloatInstruction::RelOp(t, _) => FunctionType::new([t.into(); 2], [ArbValueType::I32]), + FloatInstruction::TruncIntOp(i, f, ..) => FunctionType::new([f.into()], [i.into()]), + FloatInstruction::ConvertIntOp(f, i, _) => FunctionType::new([i.into()], [f.into()]), FloatInstruction::F32DemoteF64 => { - FunctionType::new(vec![ArbValueType::F64], vec![ArbValueType::F32]) + FunctionType::new([ArbValueType::F64], [ArbValueType::F32]) } FloatInstruction::F64PromoteF32 => { - FunctionType::new(vec![ArbValueType::F32], vec![ArbValueType::F64]) + FunctionType::new([ArbValueType::F32], [ArbValueType::F64]) } } } @@ -202,16 +205,58 @@ impl FromStr for FloatInstruction { } } -pub fn op_as_const(op: Operator) -> Result { +pub fn op_as_const(op: Operator) -> Result { match op { - Operator::I32Const { value } => Ok(LirValue::I32(value as u32)), - Operator::I64Const { value } => Ok(LirValue::I64(value as u64)), - Operator::F32Const { value } => Ok(LirValue::F32(f32::from_bits(value.bits()))), - Operator::F64Const { value } => Ok(LirValue::F64(f64::from_bits(value.bits()))), + Operator::I32Const { value } => Ok(Value::I32(value as u32)), + Operator::I64Const { value } => Ok(Value::I64(value as u64)), + Operator::F32Const { value } => Ok(Value::F32(f32::from_bits(value.bits()))), + Operator::F64Const { value } => Ok(Value::F64(f64::from_bits(value.bits()))), _ => bail!("Opcode is not a constant"), } } +#[derive(Clone, Debug, Default)] +pub struct FuncImport<'a> { + pub offset: u32, + pub module: &'a str, + pub name: &'a str, +} + +/// This enum primarily exists because wasmer's ExternalKind doesn't impl these derived functions +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)] +pub enum ExportKind { + Func, + Table, + Memory, + Global, + Tag, +} + +impl From for ExportKind { + fn from(kind: ExternalKind) -> Self { + use ExternalKind as E; + match kind { + E::Func => Self::Func, + E::Table => Self::Table, + E::Memory => Self::Memory, + E::Global => Self::Global, + E::Tag => Self::Tag, + } + } +} + +impl From for ExportKind { + fn from(value: ExportIndex) -> Self { + use ExportIndex as E; + match value { + E::Function(_) => Self::Func, + E::Table(_) => Self::Table, + E::Memory(_) => Self::Memory, + E::Global(_) => Self::Global, + } + } +} + #[derive(Clone, Debug, Default)] pub struct Code<'a> { pub locals: Vec, @@ -230,24 +275,31 @@ pub struct NameCustomSection { pub functions: HashMap, } +pub type ExportMap = HashMap; + #[derive(Clone, Default)] pub struct WasmBinary<'a> { pub types: Vec, - pub imports: Vec>, + pub imports: Vec>, + /// Maps *local* function indices to global type signatures. pub functions: Vec, pub tables: Vec, pub memories: Vec, - pub globals: Vec>, - pub exports: Vec>, + pub globals: Vec, + pub exports: ExportMap, pub start: Option, pub elements: Vec>, pub codes: Vec>, pub datas: Vec>, pub names: NameCustomSection, + /// The source wasm, if known. + pub wasm: Option<&'a [u8]>, + /// Consensus data used to make module hashes unique. + pub extra_data: Vec, } -pub fn parse(input: &[u8]) -> eyre::Result> { - let features = wasmparser::WasmFeatures { +pub fn parse<'a>(input: &'a [u8], path: &'_ Path) -> Result> { + let features = WasmFeatures { mutable_global: true, saturating_float_to_int: true, sign_extension: true, @@ -258,42 +310,49 @@ pub fn parse(input: &[u8]) -> eyre::Result> { relaxed_simd: false, threads: false, tail_call: false, - deterministic_only: false, + floats: true, multi_memory: false, exceptions: false, memory64: false, extended_const: false, component_model: false, + function_references: false, + memory_control: false, + gc: false, + component_model_values: false, + component_model_nested_names: false, }; - wasmparser::Validator::new_with_features(features).validate_all(input)?; - let sections: Vec<_> = Parser::new(0).parse_all(input).collect::>()?; + Validator::new_with_features(features) + .validate_all(input) + .wrap_err_with(|| eyre!("failed to validate {}", path.to_string_lossy().red()))?; - let mut binary = WasmBinary::default(); + let mut binary = WasmBinary { + wasm: Some(input), + ..Default::default() + }; + let sections: Vec<_> = Parser::new(0).parse_all(input).collect::>()?; - for mut section in sections { + for section in sections { use Payload::*; macro_rules! process { ($dest:expr, $source:expr) => {{ - for _ in 0..$source.get_count() { - let item = $source.read()?; - $dest.push(item.into()) + for item in $source.into_iter() { + $dest.push(item?.into()) } }}; } - match &mut section { + match section { TypeSection(type_section) => { - for _ in 0..type_section.get_count() { - let TypeDef::Func(ty) = type_section.read()?; - binary.types.push(ty.try_into()?); + for func in type_section.into_iter_err_on_gc_types() { + binary.types.push(func?.try_into()?); } } CodeSectionEntry(codes) => { let mut code = Code::default(); let mut locals = codes.get_locals_reader()?; let mut ops = codes.get_operators_reader()?; - let mut index = 0; for _ in 0..locals.get_count() { @@ -312,35 +371,70 @@ pub fn parse(input: &[u8]) -> eyre::Result> { binary.codes.push(code); } - ImportSection(imports) => process!(binary.imports, imports), + GlobalSection(globals) => { + for global in globals { + let mut init = global?.init_expr.get_operators_reader(); + + let value = match (init.read()?, init.read()?, init.eof()) { + (op, Operator::End, true) => op_as_const(op)?, + _ => bail!("Non-constant global initializer"), + }; + binary.globals.push(value); + } + } + ImportSection(imports) => { + for import in imports { + let import = import?; + let TypeRef::Func(offset) = import.ty else { + bail!("unsupported import kind {:?}", import) + }; + let import = FuncImport { + offset, + module: import.module, + name: import.name, + }; + binary.imports.push(import); + } + } + ExportSection(exports) => { + use ExternalKind as E; + for export in exports { + let export = export?; + let name = export.name.to_owned(); + let kind = export.kind; + if let E::Func = kind { + let index = export.index; + let name = || name.clone(); + binary.names.functions.entry(index).or_insert_with(name); + } + binary.exports.insert(name, (export.index, kind.into())); + } + } FunctionSection(functions) => process!(binary.functions, functions), - TableSection(tables) => process!(binary.tables, tables), + TableSection(tables) => { + for table in tables { + binary.tables.push(table?.ty); + } + } MemorySection(memories) => process!(binary.memories, memories), - GlobalSection(globals) => process!(binary.globals, globals), - ExportSection(exports) => process!(binary.exports, exports), - StartSection { func, .. } => binary.start = Some(*func), + StartSection { func, .. } => binary.start = Some(func), ElementSection(elements) => process!(binary.elements, elements), DataSection(datas) => process!(binary.datas, datas), CodeSectionStart { .. } => {} - CustomSection { - name, - data_offset, - data, - .. - } => { - if *name != "name" { + CustomSection(reader) => { + if reader.name() != "name" { continue; } - let mut name_reader = NameSectionReader::new(data, *data_offset)?; + // CHECK: maybe reader.data_offset() + let name_reader = NameSectionReader::new(reader.data(), 0); - while !name_reader.eof() { - match name_reader.read()? { - Name::Module(name) => binary.names.module = name.get_name()?.to_owned(), + for name in name_reader { + match name? { + Name::Module { name, .. } => binary.names.module = name.to_owned(), Name::Function(namemap) => { - let mut map_reader = namemap.get_map()?; - for _ in 0..map_reader.get_count() { - let Naming { index, name } = map_reader.read()?; + for naming in namemap { + let Naming { index, name } = naming?; binary.names.functions.insert(index, name.to_owned()); } } @@ -348,12 +442,281 @@ pub fn parse(input: &[u8]) -> eyre::Result> { } } } - Version { num, .. } => ensure!(*num == 1, "wasm format version not supported {}", num), - UnknownSection { id, .. } => bail!("unsupported unknown section type {}", id), - End(_offset) => {} + Version { num, .. } => ensure!(num == 1, "wasm format version not supported {num}"), + UnknownSection { id, .. } => bail!("unsupported unknown section type {id}"), + End(_) => {} x => bail!("unsupported section type {:?}", x), } } + // reject the module if it imports the same func with inconsistent signatures + let mut imports = HashMap::default(); + for import in &binary.imports { + let offset = import.offset; + let module = import.module; + let name = import.name; + + let key = (module, name); + if let Some(prior) = imports.insert(key, offset) { + if prior != offset { + let name = name.debug_red(); + bail!("inconsistent imports for {} {name}", module.red()); + } + } + } + + // reject the module if it re-exports an import with the same name + let mut exports = HashSet::default(); + for export in binary.exports.keys() { + let export = export.rsplit("__").take(1); + exports.extend(export); + } + for import in &binary.imports { + let name = import.name; + if exports.contains(name) { + bail!("binary exports an import with the same name {}", name.red()); + } + } + + // reject the module if it imports or exports reserved symbols + let reserved = |x: &&str| x.starts_with("stylus"); + if let Some(name) = exports.into_iter().find(reserved) { + bail!("binary exports reserved symbol {}", name.red()) + } + if let Some(name) = binary.imports.iter().map(|x| x.name).find(reserved) { + bail!("binary imports reserved symbol {}", name.red()) + } + + // if no module name was given, make a best-effort guess with the file path + if binary.names.module.is_empty() { + binary.names.module = match path.file_name() { + Some(os_str) => os_str.to_string_lossy().into(), + None => path.to_string_lossy().into(), + }; + } Ok(binary) } + +impl<'a> Debug for WasmBinary<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("WasmBinary") + .field("types", &self.types) + .field("imports", &self.imports) + .field("functions", &self.functions) + .field("tables", &self.tables) + .field("memories", &self.memories) + .field("globals", &self.globals) + .field("exports", &self.exports) + .field("start", &self.start) + .field("elements", &format!("<{} elements>", self.elements.len())) + .field("codes", &self.codes) + .field("datas", &self.datas) + .field("names", &self.names) + .finish() + } +} + +impl<'a> WasmBinary<'a> { + /// Instruments a user wasm, producing a version bounded via configurable instrumentation. + pub fn instrument( + &mut self, + compile: &CompileConfig, + codehash: &Bytes32, + ) -> Result { + let start = StartMover::new(compile.debug.debug_info); + let meter = Meter::new(&compile.pricing); + let dygas = DynamicMeter::new(&compile.pricing); + let depth = DepthChecker::new(compile.bounds); + let bound = HeapBound::new(compile.bounds); + + start.update_module(self)?; + meter.update_module(self)?; + dygas.update_module(self)?; + depth.update_module(self)?; + bound.update_module(self)?; + + let count = compile.debug.count_ops.then(Counter::new); + if let Some(count) = &count { + count.update_module(self)?; + } + + for (index, code) in self.codes.iter_mut().enumerate() { + let index = LocalFunctionIndex::from_u32(index as u32); + let locals: Vec = code.locals.iter().map(|x| x.value.into()).collect(); + + let mut build = mem::take(&mut code.expr); + let mut input = Vec::with_capacity(build.len()); + + /// this macro exists since middlewares aren't sized (can't use a vec without boxes) + macro_rules! apply { + ($middleware:expr) => { + let mut mid = Middleware::::instrument(&$middleware, index)?; + mid.locals_info(&locals); + + mem::swap(&mut build, &mut input); + + for op in input.drain(..) { + mid.feed(op, &mut build) + .wrap_err_with(|| format!("{} failure", mid.name()))? + } + }; + } + + // add the instrumentation in the order of application + // note: this must be consistent with native execution + apply!(start); + apply!(meter); + apply!(dygas); + apply!(depth); + apply!(bound); + + if let Some(count) = &count { + apply!(*count); + } + + code.expr = build; + } + + let wasm = self.wasm.take().unwrap(); + self.extra_data.extend(*codehash); + self.extra_data.extend(compile.version.to_be_bytes()); + + // 4GB maximum implies `footprint` fits in a u16 + let footprint = self.memory_info()?.min.0 as u16; + + // check the entrypoint + let ty = FunctionType::new([ArbValueType::I32], [ArbValueType::I32]); + let user_main = self.check_func(STYLUS_ENTRY_POINT, ty)?; + + // predict costs + let funcs = self.codes.len() as u64; + let globals = self.globals.len() as u64; + let wasm_len = wasm.len() as u64; + + let data_len: u64 = self.datas.iter().map(|x| x.range.len() as u64).sum(); + let elem_len: u64 = self.elements.iter().map(|x| x.range.len() as u64).sum(); + let data_len = data_len + elem_len; + + let mut type_len = 0; + for index in &self.functions { + let ty = &self.types[*index as usize]; + type_len += (ty.inputs.len() + ty.outputs.len()) as u64; + } + + let mut asm_estimate: u64 = 512000; + asm_estimate = asm_estimate.saturating_add(funcs.saturating_mul(996829) / 1000); + asm_estimate = asm_estimate.saturating_add(type_len.saturating_mul(11416) / 1000); + asm_estimate = asm_estimate.saturating_add(wasm_len.saturating_mul(62628) / 10000); + + let mut cached_init: u64 = 0; + cached_init = cached_init.saturating_add(funcs.saturating_mul(13420) / 100_000); + cached_init = cached_init.saturating_add(type_len.saturating_mul(89) / 100_000); + cached_init = cached_init.saturating_add(wasm_len.saturating_mul(122) / 100_000); + cached_init = cached_init.saturating_add(globals.saturating_mul(1628) / 1000); + cached_init = cached_init.saturating_add(data_len.saturating_mul(75244) / 100_000); + cached_init = cached_init.saturating_add(footprint as u64 * 5); + + let mut init = cached_init; + init = init.saturating_add(funcs.saturating_mul(8252) / 1000); + init = init.saturating_add(type_len.saturating_mul(1059) / 1000); + init = init.saturating_add(wasm_len.saturating_mul(1286) / 10_000); + + let [ink_left, ink_status] = meter.globals(); + let depth_left = depth.globals(); + Ok(StylusData { + ink_left: ink_left.as_u32(), + ink_status: ink_status.as_u32(), + depth_left: depth_left.as_u32(), + init_cost: init.try_into().wrap_err("init cost too high")?, + cached_init_cost: cached_init.try_into().wrap_err("cached cost too high")?, + asm_estimate: asm_estimate.try_into().wrap_err("asm estimate too large")?, + footprint, + user_main, + }) + } + + /// Parses and instruments a user wasm + pub fn parse_user( + wasm: &'a [u8], + page_limit: u16, + compile: &CompileConfig, + codehash: &Bytes32, + ) -> Result<(WasmBinary<'a>, StylusData)> { + let mut bin = parse(wasm, Path::new("user"))?; + let stylus_data = bin.instrument(compile, codehash)?; + + let Some(memory) = bin.memories.first() else { + bail!("missing memory with export name \"memory\"") + }; + let pages = memory.initial; + + // ensure the wasm fits within the remaining amount of memory + if pages > page_limit.into() { + let limit = page_limit.red(); + bail!("memory exceeds limit: {} > {limit}", pages.red()); + } + + // not strictly necessary, but anti-DoS limits and extra checks in case of bugs + macro_rules! limit { + ($limit:expr, $count:expr, $name:expr) => { + if $count > $limit { + bail!("too many wasm {}: {} > {}", $name, $count, $limit); + } + }; + } + limit!(1, bin.memories.len(), "memories"); + limit!(128, bin.datas.len(), "datas"); + limit!(128, bin.elements.len(), "elements"); + limit!(1024, bin.exports.len(), "exports"); + limit!(4096, bin.codes.len(), "functions"); + limit!(32768, bin.globals.len(), "globals"); + for code in &bin.codes { + limit!(348, code.locals.len(), "locals"); + limit!(65536, code.expr.len(), "opcodes in func body"); + } + + let table_entries = bin.tables.iter().map(|x| x.initial).saturating_sum(); + limit!(4096, table_entries, "table entries"); + + let elem_entries = bin.elements.iter().map(|x| x.range.len()).saturating_sum(); + limit!(4096, elem_entries, "element entries"); + + let max_len = 512; + macro_rules! too_long { + ($name:expr, $len:expr) => { + bail!( + "wasm {} too long: {} > {}", + $name.red(), + $len.red(), + max_len.red() + ) + }; + } + if let Some((name, _)) = bin.exports.iter().find(|(name, _)| name.len() > max_len) { + too_long!("name", name.len()) + } + if bin.names.module.len() > max_len { + too_long!("module name", bin.names.module.len()) + } + if bin.start.is_some() { + bail!("wasm start functions not allowed"); + } + Ok((bin, stylus_data)) + } + + /// Ensures a func exists and has the right type. + fn check_func(&self, name: &str, ty: FunctionType) -> Result { + let Some(&(func, kind)) = self.exports.get(name) else { + bail!("missing export with name {}", name.red()); + }; + if kind != ExportKind::Func { + let kind = kind.debug_red(); + bail!("export {} must be a function but is a {kind}", name.red()); + } + let func_ty = self.get_function(FunctionIndex::new(func.try_into()?))?; + if func_ty != ty { + bail!("wrong type for {}: {}", name.red(), func_ty.red()); + } + Ok(func) + } +} diff --git a/arbitrator/prover/src/bulk_memory.wat b/arbitrator/prover/src/bulk_memory.wat index 080290f158..bc0d128809 100644 --- a/arbitrator/prover/src/bulk_memory.wat +++ b/arbitrator/prover/src/bulk_memory.wat @@ -5,7 +5,7 @@ ;; https://github.com/WebAssembly/bulk-memory-operations/blob/master/proposals/bulk-memory-operations/Overview.md (module - (memory 0) + (memory (export "memory") 0 0) (func $memory_fill (param $dest i32) (param $value i32) (param $size i32) (local $value64 i64) ;; the bounds check happens before any data is written according to the spec diff --git a/arbitrator/prover/src/host.rs b/arbitrator/prover/src/host.rs index cb8b222ca7..1d0fe658ec 100644 --- a/arbitrator/prover/src/host.rs +++ b/arbitrator/prover/src/host.rs @@ -1,22 +1,24 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -#![allow(clippy::vec_init_then_push)] +#![allow(clippy::vec_init_then_push, clippy::redundant_closure)] use crate::{ binary, host, machine::{Function, InboxIdentifier}, + programs::StylusData, utils, value::{ArbValueType, FunctionType}, wavm::{wasm_to_wavm, Instruction, Opcode}, }; -use arbutil::{Color, PreimageType}; +use arbutil::{evm::user::UserOutcomeKind, Color, PreimageType}; use eyre::{bail, ErrReport, Result}; use lazy_static::lazy_static; -use std::{collections::HashMap, str::FromStr}; +use num_derive::FromPrimitive; +use std::{collections::HashMap, path::Path, str::FromStr}; /// Represents the internal hostio functions a module may have. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug, FromPrimitive)] #[repr(u64)] pub enum InternalFunc { WavmCallerLoad8, @@ -25,17 +27,38 @@ pub enum InternalFunc { WavmCallerStore32, MemoryFill, MemoryCopy, + UserInkLeft, + UserInkStatus, + UserSetInk, + UserStackLeft, + UserSetStack, + UserMemorySize, + CallMain, } impl InternalFunc { pub fn ty(&self) -> FunctionType { use ArbValueType::*; use InternalFunc::*; - match self { - WavmCallerLoad8 | WavmCallerLoad32 => FunctionType::new(vec![I32], vec![I32]), - WavmCallerStore8 | WavmCallerStore32 => FunctionType::new(vec![I32, I32], vec![]), - MemoryFill | MemoryCopy => FunctionType::new(vec![I32, I32, I32], vec![]), + macro_rules! func { + ([$($args:expr),*], [$($outs:expr),*]) => { + FunctionType::new(vec![$($args),*], vec![$($outs),*]) + }; } + #[rustfmt::skip] + let ty = match self { + WavmCallerLoad8 | WavmCallerLoad32 => func!([I32], [I32]), + WavmCallerStore8 | WavmCallerStore32 => func!([I32, I32], []), + MemoryFill | MemoryCopy => func!([I32, I32, I32], []), + UserInkLeft => func!([], [I64]), // λ() → ink_left + UserInkStatus => func!([], [I32]), // λ() → ink_status + UserSetInk => func!([I64, I32], []), // λ(ink_left, ink_status) + UserStackLeft => func!([], [I32]), // λ() → stack_left + UserSetStack => func!([I32], []), // λ(stack_left) + UserMemorySize => func!([], [I32]), // λ() → memory_size + CallMain => func!([I32], [I32]), // λ(args_len) → status + }; + ty } } @@ -55,6 +78,29 @@ pub enum Hostio { WavmReadInboxMessage, WavmReadDelayedInboxMessage, WavmHaltAndSetFinished, + WavmLinkModule, + WavmUnlinkModule, + ProgramInkLeft, + ProgramInkStatus, + ProgramSetInk, + ProgramStackLeft, + ProgramSetStack, + ProgramMemorySize, + ProgramCallMain, + ProgramRequest, + ProgramContinue, + ConsoleLogTxt, + ConsoleLogI32, + ConsoleLogI64, + ConsoleLogF32, + ConsoleLogF64, + ConsoleTeeI32, + ConsoleTeeI64, + ConsoleTeeF32, + ConsoleTeeF64, + UserInkLeft, + UserInkStatus, + UserSetInk, } impl FromStr for Hostio { @@ -79,6 +125,29 @@ impl FromStr for Hostio { ("env", "wavm_read_inbox_message") => WavmReadInboxMessage, ("env", "wavm_read_delayed_inbox_message") => WavmReadDelayedInboxMessage, ("env", "wavm_halt_and_set_finished") => WavmHaltAndSetFinished, + ("hostio", "wavm_link_module") => WavmLinkModule, + ("hostio", "wavm_unlink_module") => WavmUnlinkModule, + ("hostio", "program_ink_left") => ProgramInkLeft, + ("hostio", "program_ink_status") => ProgramInkStatus, + ("hostio", "program_set_ink") => ProgramSetInk, + ("hostio", "program_stack_left") => ProgramStackLeft, + ("hostio", "program_set_stack") => ProgramSetStack, + ("hostio", "program_memory_size") => ProgramMemorySize, + ("hostio", "program_call_main") => ProgramCallMain, + ("hostio", "program_request") => ProgramRequest, + ("hostio", "program_continue") => ProgramContinue, + ("hostio", "user_ink_left") => UserInkLeft, + ("hostio", "user_ink_status") => UserInkStatus, + ("hostio", "user_set_ink") => UserSetInk, + ("console", "log_txt") => ConsoleLogTxt, + ("console", "log_i32") => ConsoleLogI32, + ("console", "log_i64") => ConsoleLogI64, + ("console", "log_f32") => ConsoleLogF32, + ("console", "log_f64") => ConsoleLogF64, + ("console", "tee_i32") => ConsoleTeeI32, + ("console", "tee_i64") => ConsoleTeeI64, + ("console", "tee_f32") => ConsoleTeeF32, + ("console", "tee_f64") => ConsoleTeeF64, _ => bail!("no such hostio {} in {}", name.red(), module.red()), }) } @@ -117,11 +186,34 @@ impl Hostio { WavmReadInboxMessage => func!([I64, I32, I32], [I32]), WavmReadDelayedInboxMessage => func!([I64, I32, I32], [I32]), WavmHaltAndSetFinished => func!(), + WavmLinkModule => func!([I32], [I32]), // λ(module_hash) → module + WavmUnlinkModule => func!(), // λ() + ProgramInkLeft => func!([I32], [I64]), // λ(module) → ink_left + ProgramInkStatus => func!([I32], [I32]), // λ(module) → ink_status + ProgramSetInk => func!([I32, I64]), // λ(module, ink_left) + ProgramStackLeft => func!([I32], [I32]), // λ(module) → stack_left + ProgramSetStack => func!([I32, I32]), // λ(module, stack_left) + ProgramMemorySize => func!([I32], [I32]), // λ(module) → memory_size + ProgramCallMain => func!([I32, I32], [I32]), // λ(module, args_len) → status + ProgramRequest => func!([I32], [I32]), // λ(status) → response + ProgramContinue => func!([I32], [I32]), // λ(response) → status + ConsoleLogTxt => func!([I32, I32]), // λ(text, len) + ConsoleLogI32 => func!([I32]), // λ(value) + ConsoleLogI64 => func!([I64]), // λ(value) + ConsoleLogF32 => func!([F32]), // λ(value) + ConsoleLogF64 => func!([F64]), // λ(value) + ConsoleTeeI32 => func!([I32], [I32]), // λ(value) → value + ConsoleTeeI64 => func!([I64], [I64]), // λ(value) → value + ConsoleTeeF32 => func!([F32], [F32]), // λ(value) → value + ConsoleTeeF64 => func!([F64], [F64]), // λ(value) → value + UserInkLeft => InternalFunc::UserInkLeft.ty(), + UserInkStatus => InternalFunc::UserInkStatus.ty(), + UserSetInk => InternalFunc::UserSetInk.ty(), }; ty } - pub fn body(&self) -> Vec { + pub fn body(&self, _prior: usize) -> Vec { let mut body = vec![]; macro_rules! opcode { @@ -132,27 +224,38 @@ impl Hostio { body.push(Instruction::with_data($opcode, $value as u64)) }; } + macro_rules! cross_internal { + ($func:ident) => { + opcode!(LocalGet, 0); // module + opcode!(CrossModuleInternalCall, InternalFunc::$func); // consumes module + }; + } + macro_rules! intern { + ($func:ident) => { + opcode!(CallerModuleInternalCall, InternalFunc::$func); + }; + } use Hostio::*; use Opcode::*; match self { WavmCallerLoad8 => { opcode!(LocalGet, 0); - opcode!(CallerModuleInternalCall, InternalFunc::WavmCallerLoad8); + intern!(WavmCallerLoad8); } WavmCallerLoad32 => { opcode!(LocalGet, 0); - opcode!(CallerModuleInternalCall, InternalFunc::WavmCallerLoad32); + intern!(WavmCallerLoad32); } WavmCallerStore8 => { opcode!(LocalGet, 0); opcode!(LocalGet, 1); - opcode!(CallerModuleInternalCall, InternalFunc::WavmCallerStore8); + intern!(WavmCallerStore8); } WavmCallerStore32 => { opcode!(LocalGet, 0); opcode!(LocalGet, 1); - opcode!(CallerModuleInternalCall, InternalFunc::WavmCallerStore32); + intern!(WavmCallerStore32); } WavmGetGlobalStateBytes32 => { opcode!(LocalGet, 0); @@ -203,37 +306,134 @@ impl Hostio { WavmHaltAndSetFinished => { opcode!(HaltAndSetFinished); } + WavmLinkModule => { + // λ(module_hash) → module + opcode!(LocalGet, 0); + opcode!(LinkModule); + opcode!(NewCoThread); + } + WavmUnlinkModule => { + // λ() + opcode!(UnlinkModule); + opcode!(PopCoThread); + } + ProgramInkLeft => { + // λ(module) → ink_left + cross_internal!(UserInkLeft); + } + ProgramInkStatus => { + // λ(module) → ink_status + cross_internal!(UserInkStatus); + } + ProgramSetInk => { + // λ(module, ink_left) + opcode!(LocalGet, 1); // ink_left + opcode!(I32Const, 0); // ink_status + cross_internal!(UserSetInk); + } + ProgramStackLeft => { + // λ(module) → stack_left + cross_internal!(UserStackLeft); + } + ProgramSetStack => { + // λ(module, stack_left) + opcode!(LocalGet, 1); // stack_left + cross_internal!(UserSetStack); + } + ProgramMemorySize => { + // λ(module) → memory_size + cross_internal!(UserMemorySize); + } + ProgramCallMain => { + // caller sees: λ(module, args_len) → status + opcode!(LocalGet, 1); // args_len + opcode!(LocalGet, 0); // module + opcode!(MoveFromStackToInternal); + opcode!(MoveFromStackToInternal); + opcode!(SwitchThread, 8); + opcode!(MoveFromInternalToStack); + opcode!(MoveFromInternalToStack); + opcode!(CrossModuleInternalCall, InternalFunc::CallMain); // consumes module + opcode!(MoveFromStackToInternal); + opcode!(SwitchThread, 0); + opcode!(MoveFromInternalToStack); + opcode!(Return); + + // jumps here if errored while in thread 1 + opcode!(I32Const, UserOutcomeKind::Failure as u32) + } + ProgramContinue => { + // caller sees: λ(return_data) → status (returns to caller of ProgramRequest) + // code returns return_data to caller of ProgramRequest + opcode!(LocalGet, 0); // return_data + opcode!(MoveFromStackToInternal); + opcode!(SwitchThread, 3); + opcode!(MoveFromInternalToStack); + opcode!(Return); + + // jumps here if errored while in cothread + opcode!(I32Const, UserOutcomeKind::Failure as u32) + } + ProgramRequest => { + // caller sees: λ(status) → response + // code returns status of either ProgramContinue or ProgramCallMain + opcode!(LocalGet, 0); // return_data + opcode!(MoveFromStackToInternal); + opcode!(SwitchThread, 0); + opcode!(MoveFromInternalToStack); + } + UserInkLeft => { + // λ() → ink_left + intern!(UserInkLeft); + } + UserInkStatus => { + // λ() → ink_status + intern!(UserInkStatus); + } + UserSetInk => { + // λ(ink_left, ink_status) + opcode!(LocalGet, 0); + opcode!(LocalGet, 1); + intern!(UserSetInk); + } + ConsoleLogTxt | ConsoleLogI32 | ConsoleLogI64 | ConsoleLogF32 | ConsoleLogF64 => {} + ConsoleTeeI32 | ConsoleTeeI64 | ConsoleTeeF32 | ConsoleTeeF64 => { + opcode!(LocalGet, 0); + } } body } } -pub fn get_impl(module: &str, name: &str) -> Result { +pub fn get_impl(module: &str, name: &str) -> Result<(Function, bool)> { let hostio: Hostio = format!("{module}__{name}").parse()?; let append = |code: &mut Vec| { - code.extend(hostio.body()); + let len = code.len(); + code.extend(hostio.body(len)); Ok(()) }; - Function::new(&[], append, hostio.ty(), &[]) + + let debug = module == "console" || module == "debug"; + Function::new(&[], append, hostio.ty(), &[]).map(|x| (x, debug)) } /// Adds internal functions to a module. /// Note: the order of the functions must match that of the `InternalFunc` enum -pub fn new_internal_funcs() -> Vec { +pub fn new_internal_funcs(stylus_data: Option) -> Vec { use ArbValueType::*; use InternalFunc::*; use Opcode::*; - fn code_func(code: Vec, ty: FunctionType) -> Function { + fn code_func(code: &[Instruction], func: InternalFunc) -> Function { let mut wavm = vec![Instruction::simple(InitFrame)]; wavm.extend(code); wavm.push(Instruction::simple(Return)); - Function::new_from_wavm(wavm, ty, vec![]) + Function::new_from_wavm(wavm, func.ty(), vec![]) } fn op_func(opcode: Opcode, func: InternalFunc) -> Function { - code_func(vec![Instruction::simple(opcode)], func.ty()) + code_func(&[Instruction::simple(opcode)], func) } let mut funcs = vec![]; @@ -266,6 +466,30 @@ pub fn new_internal_funcs() -> Vec { let [memory_fill, memory_copy] = (*BULK_MEMORY_FUNCS).clone(); add_func(memory_fill, MemoryFill); add_func(memory_copy, MemoryCopy); + + let mut add_func = |code: &[_], internal| add_func(code_func(code, internal), internal); + + if let Some(info) = stylus_data { + let (gas, status, depth) = info.global_offsets(); + let main = info.user_main.into(); + let inst = |op, data| Instruction::with_data(op, data); + + add_func(&[inst(GlobalGet, gas)], UserInkLeft); + add_func(&[inst(GlobalGet, status)], UserInkStatus); + add_func(&[inst(GlobalSet, status), inst(GlobalSet, gas)], UserSetInk); + add_func(&[inst(GlobalGet, depth)], UserStackLeft); + add_func(&[inst(GlobalSet, depth)], UserSetStack); + add_func(&[inst(MemorySize, 0)], UserMemorySize); + add_func( + &[ + inst(Call, main), + // force return value to boolean + Instruction::simple(I32Eqz), + Instruction::simple(I32Eqz), + ], + CallMain, + ); + } funcs } @@ -275,7 +499,7 @@ lazy_static! { let data = include_bytes!("bulk_memory.wat"); let wasm = wat::parse_bytes(data).expect("failed to parse bulk_memory.wat"); - let bin = binary::parse(&wasm).expect("failed to parse bulk_memory.wasm"); + let bin = binary::parse(&wasm, Path::new("internal")).expect("failed to parse bulk_memory.wasm"); let types = [MemoryFill.ty(), MemoryCopy.ty()]; let names = ["memory_fill", "memory_copy"]; @@ -296,6 +520,7 @@ lazy_static! { &[ty.clone()], // only type needed is the func itself 0, // ----------------------------------- 0, // impls don't use other internals + &bin.names.module, ), ty.clone(), &[] // impls don't make calls diff --git a/arbitrator/prover/src/kzg.rs b/arbitrator/prover/src/kzg.rs index 66a71c52b3..b3348b9040 100644 --- a/arbitrator/prover/src/kzg.rs +++ b/arbitrator/prover/src/kzg.rs @@ -1,7 +1,7 @@ // Copyright 2022-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE -use crate::utils::Bytes32; +use arbutil::Bytes32; use c_kzg::{ KzgSettings, BYTES_PER_BLOB, BYTES_PER_G1_POINT, BYTES_PER_G2_POINT, FIELD_ELEMENTS_PER_BLOB, }; diff --git a/arbitrator/prover/src/lib.rs b/arbitrator/prover/src/lib.rs index c7610ab31f..c8649a4b3d 100644 --- a/arbitrator/prover/src/lib.rs +++ b/arbitrator/prover/src/lib.rs @@ -1,35 +1,53 @@ -// Copyright 2021-2023, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE #![allow(clippy::missing_safety_doc, clippy::too_many_arguments)] pub mod binary; mod host; +#[cfg(feature = "native")] mod kzg; pub mod machine; /// cbindgen:ignore mod memory; mod merkle; +mod print; +pub mod programs; mod reinterpret; pub mod utils; pub mod value; pub mod wavm; -use crate::machine::{argument_data_to_inbox, Machine}; -use arbutil::PreimageType; -use eyre::Result; -use machine::{get_empty_preimage_resolver, GlobalState, MachineStatus, PreimageResolver}; +#[cfg(test)] +mod test; + +pub use machine::Machine; + +use arbutil::{Bytes32, PreimageType}; +use eyre::{Report, Result}; +use lru::LruCache; +use machine::{ + argument_data_to_inbox, get_empty_preimage_resolver, GlobalState, MachineStatus, + PreimageResolver, +}; +use once_cell::sync::OnceCell; use static_assertions::const_assert_eq; use std::{ ffi::CStr, + num::NonZeroUsize, os::raw::{c_char, c_int}, path::Path, + ptr, slice, sync::{ atomic::{self, AtomicU8}, - Arc, + Arc, Mutex, }, }; -use utils::{Bytes32, CBytes}; +use utils::CBytes; + +lazy_static::lazy_static! { + static ref BLOBHASH_PREIMAGE_CACHE: Mutex>>> = Mutex::new(LruCache::new(NonZeroUsize::new(12).unwrap())); +} #[repr(C)] #[derive(Clone, Copy)] @@ -51,12 +69,15 @@ pub unsafe extern "C" fn arbitrator_load_machine( binary_path: *const c_char, library_paths: *const *const c_char, library_paths_size: isize, + debug_chain: usize, ) -> *mut Machine { - match arbitrator_load_machine_impl(binary_path, library_paths, library_paths_size) { + let debug_chain = debug_chain != 0; + match arbitrator_load_machine_impl(binary_path, library_paths, library_paths_size, debug_chain) + { Ok(mach) => mach, Err(err) => { - eprintln!("Error loading binary: {}", err); - std::ptr::null_mut() + eprintln!("Error loading binary: {:?}", err); + ptr::null_mut() } } } @@ -65,6 +86,7 @@ unsafe fn arbitrator_load_machine_impl( binary_path: *const c_char, library_paths: *const *const c_char, library_paths_size: isize, + debug_chain: bool, ) -> Result<*mut Machine> { let binary_path = cstr_to_string(binary_path); let binary_path = Path::new(&binary_path); @@ -81,6 +103,8 @@ unsafe fn arbitrator_load_machine_impl( true, false, false, + debug_chain, + debug_chain, Default::default(), Default::default(), get_empty_preimage_resolver(), @@ -89,14 +113,15 @@ unsafe fn arbitrator_load_machine_impl( } #[no_mangle] +#[cfg(feature = "native")] pub unsafe extern "C" fn arbitrator_load_wavm_binary(binary_path: *const c_char) -> *mut Machine { let binary_path = cstr_to_string(binary_path); let binary_path = Path::new(&binary_path); match Machine::new_from_wavm(binary_path) { Ok(mach) => Box::into_raw(Box::new(mach)), Err(err) => { - eprintln!("Error loading binary: {}", err); - std::ptr::null_mut() + eprintln!("Error loading binary: {err}"); + ptr::null_mut() } } } @@ -105,6 +130,23 @@ unsafe fn cstr_to_string(c_str: *const c_char) -> String { CStr::from_ptr(c_str).to_string_lossy().into_owned() } +pub fn err_to_c_string(err: Report) -> *mut libc::c_char { + str_to_c_string(&format!("{err:?}")) +} + +/// Copies the str-data into a libc free-able C string +pub fn str_to_c_string(text: &str) -> *mut libc::c_char { + unsafe { + let buf = libc::malloc(text.len() + 1); // includes null-terminating byte + if buf.is_null() { + panic!("Failed to allocate memory for error string"); + } + ptr::copy_nonoverlapping(text.as_ptr(), buf as *mut u8, text.len()); + *(buf as *mut u8).add(text.len()) = 0; + buf as *mut libc::c_char + } +} + #[no_mangle] pub unsafe extern "C" fn arbitrator_free_machine(mach: *mut Machine) { drop(Box::from_raw(mach)); @@ -122,22 +164,10 @@ pub unsafe extern "C" fn atomic_u8_store(ptr: *mut u8, contents: u8) { (*(ptr as *mut AtomicU8)).store(contents, atomic::Ordering::Relaxed); } -fn err_to_c_string(err: eyre::Report) -> *mut libc::c_char { - let err = format!("{:#}", err); - unsafe { - let buf = libc::malloc(err.len() + 1); - if buf.is_null() { - panic!("Failed to allocate memory for error string"); - } - std::ptr::copy_nonoverlapping(err.as_ptr(), buf as *mut u8, err.len()); - *(buf.add(err.len()) as *mut u8) = 0; - buf as *mut libc::c_char - } -} - /// Runs the machine while the condition variable is zero. May return early if num_steps is hit. /// Returns a c string error (freeable with libc's free) on error, or nullptr on success. #[no_mangle] +#[cfg(feature = "native")] pub unsafe extern "C" fn arbitrator_step( mach: *mut Machine, num_steps: u64, @@ -157,7 +187,7 @@ pub unsafe extern "C" fn arbitrator_step( } remaining_steps -= stepping; } - std::ptr::null_mut() + ptr::null_mut() } #[no_mangle] @@ -169,7 +199,7 @@ pub unsafe extern "C" fn arbitrator_add_inbox_message( ) -> c_int { let mach = &mut *mach; if let Some(identifier) = argument_data_to_inbox(inbox_identifier) { - let slice = std::slice::from_raw_parts(data.ptr, data.len); + let slice = slice::from_raw_parts(data.ptr, data.len); let data = slice.to_vec(); mach.add_inbox_msg(identifier, index, data); 0 @@ -178,9 +208,22 @@ pub unsafe extern "C" fn arbitrator_add_inbox_message( } } +/// Adds a user program to the machine's known set of wasms. +#[no_mangle] +pub unsafe extern "C" fn arbitrator_add_user_wasm( + mach: *mut Machine, + module: *const u8, + module_len: usize, + module_hash: *const Bytes32, +) { + let module = slice::from_raw_parts(module, module_len); + (*mach).add_stylus_module(*module_hash, module.to_owned()); +} + /// Like arbitrator_step, but stops early if it hits a host io operation. /// Returns a c string error (freeable with libc's free) on error, or nullptr on success. #[no_mangle] +#[cfg(feature = "native")] pub unsafe extern "C" fn arbitrator_step_until_host_io( mach: *mut Machine, condition: *const u8, @@ -190,10 +233,10 @@ pub unsafe extern "C" fn arbitrator_step_until_host_io( while condition.load(atomic::Ordering::Relaxed) == 0 { for _ in 0..1_000_000 { if mach.is_halted() { - return std::ptr::null_mut(); + return ptr::null_mut(); } if mach.next_instruction_is_host_io() { - return std::ptr::null_mut(); + return ptr::null_mut(); } match mach.step_n(1) { Ok(()) => {} @@ -201,7 +244,7 @@ pub unsafe extern "C" fn arbitrator_step_until_host_io( } } } - std::ptr::null_mut() + ptr::null_mut() } #[no_mangle] @@ -212,7 +255,7 @@ pub unsafe extern "C" fn arbitrator_serialize_state( let mach = &*mach; let res = CStr::from_ptr(path) .to_str() - .map_err(eyre::Report::from) + .map_err(Report::from) .and_then(|path| mach.serialize_state(path)); if let Err(err) = res { eprintln!("Failed to serialize machine state: {}", err); @@ -230,7 +273,7 @@ pub unsafe extern "C" fn arbitrator_deserialize_and_replace_state( let mach = &mut *mach; let res = CStr::from_ptr(path) .to_str() - .map_err(eyre::Report::from) + .map_err(Report::from) .and_then(|path| mach.deserialize_and_replace_state(path)); if let Err(err) = res { eprintln!("Failed to deserialize machine state: {}", err); @@ -290,32 +333,55 @@ pub struct ResolvedPreimage { pub len: isize, // negative if not found } +#[cfg(feature = "native")] +unsafe fn handle_preimage_resolution( + context: u64, + ty: PreimageType, + hash: Bytes32, + resolver: unsafe extern "C" fn(u64, u8, *const u8) -> ResolvedPreimage, +) -> Option { + let res = resolver(context, ty.into(), hash.as_ptr()); + if res.len < 0 { + return None; + } + let data = CBytes::from_raw_parts(res.ptr, res.len as usize); + // Check if preimage rehashes to the provided hash + match crate::utils::hash_preimage(&data, ty) { + Ok(have_hash) if have_hash.as_slice() == *hash => {} + Ok(got_hash) => panic!( + "Resolved incorrect data for hash {} (rehashed to {})", + hash, + Bytes32(got_hash), + ), + Err(err) => panic!( + "Failed to hash preimage from resolver (expecting hash {}): {}", + hash, err, + ), + } + Some(data) +} + #[no_mangle] +#[cfg(feature = "native")] pub unsafe extern "C" fn arbitrator_set_preimage_resolver( mach: *mut Machine, resolver: unsafe extern "C" fn(u64, u8, *const u8) -> ResolvedPreimage, ) { (*mach).set_preimage_resolver(Arc::new( move |context: u64, ty: PreimageType, hash: Bytes32| -> Option { - let res = resolver(context, ty.into(), hash.as_ptr()); - if res.len < 0 { - return None; - } - let data = CBytes::from_raw_parts(res.ptr, res.len as usize); - #[cfg(debug_assertions)] - match crate::utils::hash_preimage(&data, ty) { - Ok(have_hash) if have_hash.as_slice() == *hash => {} - Ok(got_hash) => panic!( - "Resolved incorrect data for hash {} (rehashed to {})", - hash, - Bytes32(got_hash), - ), - Err(err) => panic!( - "Failed to hash preimage from resolver (expecting hash {}): {}", - hash, err, - ), + if ty == PreimageType::EthVersionedHash { + let cache: Arc> = { + let mut locked = BLOBHASH_PREIMAGE_CACHE.lock().unwrap(); + locked.get_or_insert(hash, Default::default).clone() + }; + return cache + .get_or_try_init(|| { + handle_preimage_resolution(context, ty, hash, resolver).ok_or(()) + }) + .ok() + .cloned(); } - Some(data) + handle_preimage_resolution(context, ty, hash, resolver) }, ) as PreimageResolver); } @@ -326,16 +392,17 @@ pub unsafe extern "C" fn arbitrator_set_context(mach: *mut Machine, context: u64 } #[no_mangle] -pub unsafe extern "C" fn arbitrator_hash(mach: *mut Machine) -> utils::Bytes32 { +pub unsafe extern "C" fn arbitrator_hash(mach: *mut Machine) -> Bytes32 { (*mach).hash() } #[no_mangle] -pub unsafe extern "C" fn arbitrator_module_root(mach: *mut Machine) -> utils::Bytes32 { +pub unsafe extern "C" fn arbitrator_module_root(mach: *mut Machine) -> Bytes32 { (*mach).get_modules_root() } #[no_mangle] +#[cfg(feature = "native")] pub unsafe extern "C" fn arbitrator_gen_proof(mach: *mut Machine) -> RustByteArray { let mut proof = (*mach).serialize_proof(); let ret = RustByteArray { diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 85ea14e101..5466c7f790 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -1,27 +1,33 @@ -// Copyright 2021-2023, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE +#[cfg(feature = "native")] +use crate::kzg::prove_kzg_preimage; use crate::{ - binary::{parse, FloatInstruction, Local, NameCustomSection, WasmBinary}, + binary::{ + self, parse, ExportKind, ExportMap, FloatInstruction, Local, NameCustomSection, WasmBinary, + }, host, - kzg::prove_kzg_preimage, memory::Memory, merkle::{Merkle, MerkleType}, + programs::{config::CompileConfig, meter::MeteredMachine, ModuleMod, StylusData}, reinterpret::{ReinterpretAsSigned, ReinterpretAsUnsigned}, - utils::{file_bytes, Bytes32, CBytes, RemoteTableType}, + utils::{file_bytes, CBytes, RemoteTableType}, value::{ArbValueType, FunctionType, IntegerValType, ProgramCounter, Value}, wavm::{ - pack_cross_module_call, unpack_cross_module_call, wasm_to_wavm, FloatingPointImpls, + self, pack_cross_module_call, unpack_cross_module_call, wasm_to_wavm, FloatingPointImpls, IBinOpType, IRelOpType, IUnOpType, Instruction, Opcode, }, }; -use arbutil::{Color, PreimageType}; +use arbutil::{crypto, math, Bytes32, Color, DebugColor, PreimageType}; +use brotli::Dictionary; +#[cfg(feature = "native")] use c_kzg::BYTES_PER_BLOB; use digest::Digest; use eyre::{bail, ensure, eyre, Result, WrapErr}; use fnv::FnvHashMap as HashMap; +use lazy_static::lazy_static; use num::{traits::PrimInt, Zero}; -use rayon::prelude::*; use serde::{Deserialize, Serialize}; use serde_with::serde_as; use sha3::Keccak256; @@ -31,12 +37,18 @@ use std::{ convert::{TryFrom, TryInto}, fmt::{self, Display}, fs::File, + hash::Hash, io::{BufReader, BufWriter, Write}, num::Wrapping, + ops::Add, path::{Path, PathBuf}, sync::Arc, }; -use wasmparser::{DataKind, ElementItem, ElementKind, ExternalKind, Operator, TableType, TypeRef}; +use wasmer_types::FunctionIndex; +use wasmparser::{DataKind, ElementItems, ElementKind, Operator, RefType, TableType}; + +#[cfg(feature = "rayon")] +use rayon::prelude::*; fn hash_call_indirect_data(table: u32, ty: &FunctionType) -> Bytes32 { let mut h = Keccak256::new(); @@ -62,11 +74,11 @@ pub fn argument_data_to_inbox(argument_data: u64) -> Option { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Function { - code: Vec, - ty: FunctionType, + pub code: Vec, + pub ty: FunctionType, #[serde(skip)] code_merkle: Merkle, - local_types: Vec, + pub local_types: Vec, } impl Function { @@ -106,7 +118,7 @@ impl Function { // Insert missing proving argument data for inst in insts.iter_mut() { if inst.opcode == Opcode::CallIndirect { - let (table, ty) = crate::wavm::unpack_call_indirect(inst.argument_data); + let (table, ty) = wavm::unpack_call_indirect(inst.argument_data); let ty = &module_types[usize::try_from(ty).unwrap()]; inst.proving_argument_data = Some(hash_call_indirect_data(table, ty)); } @@ -124,17 +136,36 @@ impl Function { u32::try_from(code.len()).is_ok(), "Function instruction count doesn't fit in a u32", ); - let code_merkle = Merkle::new( - MerkleType::Instruction, - code.par_iter().map(|i| i.hash()).collect(), - ); - - Function { + let mut func = Function { code, ty, - code_merkle, + code_merkle: Merkle::default(), // TODO: make an option local_types, - } + }; + func.set_code_merkle(); + func + } + + const CHUNK_SIZE: usize = 64; + + fn set_code_merkle(&mut self) { + let code = &self.code; + let chunks = math::div_ceil::<64>(code.len()); + let crunch = |x: usize| Instruction::hash(&code[64 * x..(64 * (x + 1)).min(code.len())]); + + #[cfg(feature = "rayon")] + let code_hashes = (0..chunks).into_par_iter().map(crunch).collect(); + + #[cfg(not(feature = "rayon"))] + let code_hashes = (0..chunks).map(crunch).collect(); + + self.code_merkle = Merkle::new(MerkleType::Instruction, code_hashes); + } + + fn serialize_body_for_proof(&self, pc: ProgramCounter) -> Vec { + let start = pc.inst() / 64 * 64; + let end = (start + 64).min(self.code.len()); + Instruction::serialize_for_proof(&self.code[start..end]) } fn hash(&self) -> Bytes32 { @@ -187,9 +218,9 @@ impl StackFrame { } #[derive(Clone, Debug, Serialize, Deserialize)] -struct TableElement { +pub(crate) struct TableElement { func_ty: FunctionType, - val: Value, + pub val: Value, } impl Default for TableElement { @@ -213,10 +244,10 @@ impl TableElement { #[serde_as] #[derive(Clone, Debug, Serialize, Deserialize)] -struct Table { +pub(crate) struct Table { #[serde(with = "RemoteTableType")] - ty: TableType, - elems: Vec, + pub ty: TableType, + pub elems: Vec, #[serde(skip)] elems_merkle: Merkle, } @@ -246,82 +277,137 @@ struct AvailableImport { func: u32, } +impl AvailableImport { + pub fn new(ty: FunctionType, module: u32, func: u32) -> Self { + Self { ty, module, func } + } +} + #[derive(Clone, Debug, Default, Serialize, Deserialize)] -struct Module { - globals: Vec, - memory: Memory, - tables: Vec, +pub struct Module { + pub(crate) globals: Vec, + pub(crate) memory: Memory, + pub(crate) tables: Vec
, #[serde(skip)] - tables_merkle: Merkle, - funcs: Arc>, + pub(crate) tables_merkle: Merkle, + pub(crate) funcs: Arc>, #[serde(skip)] - funcs_merkle: Arc, - types: Arc>, - internals_offset: u32, - names: Arc, - host_call_hooks: Arc>>, - start_function: Option, - func_types: Arc>, - exports: Arc>, + pub(crate) funcs_merkle: Arc, + pub(crate) types: Arc>, + pub(crate) internals_offset: u32, + pub(crate) names: Arc, + pub(crate) host_call_hooks: Arc>>, + pub(crate) start_function: Option, + pub(crate) func_types: Arc>, + /// Old modules use this format. + /// TODO: remove this after the jump to stylus. + #[serde(alias = "exports")] + pub(crate) func_exports: Arc>, + #[serde(default)] + pub(crate) all_exports: Arc, + /// Used to make modules unique. + pub(crate) extra_hash: Arc, +} + +lazy_static! { + static ref USER_IMPORTS: HashMap = { + let mut imports = HashMap::default(); + + let forward = include_bytes!("../../../target/machines/latest/forward_stub.wasm"); + let forward = binary::parse(forward, Path::new("forward")).unwrap(); + + for (name, &(export, kind)) in &forward.exports { + if kind == ExportKind::Func { + let ty = match forward.get_function(FunctionIndex::from_u32(export)) { + Ok(ty) => ty, + Err(error) => panic!("failed to read export {name}: {error:?}"), + }; + let import = AvailableImport::new(ty, 1, export); + imports.insert(name.to_owned(), import); + } + } + imports + }; } impl Module { + const FORWARDING_PREFIX: &'static str = "arbitrator_forward__"; + fn from_binary( bin: &WasmBinary, available_imports: &HashMap, floating_point_impls: &FloatingPointImpls, allow_hostapi: bool, + debug_funcs: bool, + stylus_data: Option, ) -> Result { let mut code = Vec::new(); let mut func_type_idxs: Vec = Vec::new(); let mut memory = Memory::default(); - let mut exports = HashMap::default(); let mut tables = Vec::new(); let mut host_call_hooks = Vec::new(); + let bin_name = &bin.names.module; for import in &bin.imports { - if let TypeRef::Func(ty) = import.ty { - let mut qualified_name = format!("{}__{}", import.module, import.name); - qualified_name = qualified_name.replace(&['/', '.'] as &[char], "_"); - let have_ty = &bin.types[ty as usize]; - let func; - if let Some(import) = available_imports.get(&qualified_name) { - ensure!( - &import.ty == have_ty, - "Import has different function signature than host function. Expected {:?} but got {:?}", - import.ty, have_ty, - ); - let wavm = vec![ - Instruction::simple(Opcode::InitFrame), - Instruction::with_data( - Opcode::CrossModuleCall, - pack_cross_module_call(import.module, import.func), - ), - Instruction::simple(Opcode::Return), - ]; - func = Function::new_from_wavm(wavm, import.ty.clone(), Vec::new()); - } else { - func = host::get_impl(import.module, import.name)?; - ensure!( - &func.ty == have_ty, - "Import has different function signature than host function. Expected {:?} but got {:?}", - func.ty, have_ty, - ); - ensure!( - allow_hostapi, - "Calling hostapi directly is not allowed. Function {}", - import.name, - ); - } - func_type_idxs.push(ty); - code.push(func); - host_call_hooks.push(Some((import.module.into(), import.name.into()))); + let module = import.module; + let have_ty = &bin.types[import.offset as usize]; + let (forward, import_name) = match import.name.strip_prefix(Module::FORWARDING_PREFIX) { + Some(name) => (true, name), + None => (false, import.name), + }; + + let mut qualified_name = format!("{module}__{import_name}"); + qualified_name = qualified_name.replace(&['/', '.', '-'] as &[char], "_"); + + let func = if let Some(import) = available_imports.get(&qualified_name) { + let call = match forward { + true => Opcode::CrossModuleForward, + false => Opcode::CrossModuleCall, + }; + let wavm = vec![ + Instruction::simple(Opcode::InitFrame), + Instruction::with_data( + call, + pack_cross_module_call(import.module, import.func), + ), + Instruction::simple(Opcode::Return), + ]; + Function::new_from_wavm(wavm, import.ty.clone(), vec![]) + } else if let Ok((hostio, debug)) = host::get_impl(import.module, import_name) { + ensure!( + (debug && debug_funcs) || (!debug && allow_hostapi), + "Host func {} in {} not enabled debug_funcs={debug_funcs} hostapi={allow_hostapi} debug={debug}", + import_name.red(), + import.module.red(), + ); + hostio } else { - bail!("Unsupport import kind {:?}", import); - } + bail!( + "No such import {} in {} for {}", + import_name.red(), + import.module.red(), + bin_name.red() + ) + }; + ensure!( + &func.ty == have_ty, + "Import {} for {} has different function signature than export.\nexpected {} in {}\nbut have {}", + import_name.red(), bin_name.red(), func.ty.red(), module.red(), have_ty.red(), + ); + + func_type_idxs.push(import.offset); + code.push(func); + host_call_hooks.push(Some((import.module.into(), import_name.into()))); } func_type_idxs.extend(bin.functions.iter()); - let internals = host::new_internal_funcs(); + let func_exports: HashMap = bin + .exports + .iter() + .filter(|(_, (_, kind))| kind == &ExportKind::Func) + .map(|(name, (offset, _))| (name.to_owned(), *offset)) + .collect(); + + let internals = host::new_internal_funcs(stylus_data); let internals_offset = (code.len() + bin.codes.len()) as u32; let internals_types = internals.iter().map(|f| f.ty.clone()); @@ -348,6 +434,7 @@ impl Module { &types, func_type_idxs[idx], internals_offset, + bin_name, ) }, func_ty.clone(), @@ -376,8 +463,8 @@ impl Module { if initial > max_size { bail!( "Memory inits to a size larger than its max: {} vs {}", - limits.initial, - max_size + limits.initial.red(), + max_size.red() ); } let size = initial * page_size; @@ -385,29 +472,12 @@ impl Module { memory = Memory::new(size as usize, max_size); } - let mut globals = vec![]; - for global in &bin.globals { - let mut init = global.init_expr.get_operators_reader(); - - let value = match (init.read()?, init.read()?, init.eof()) { - (op, Operator::End, true) => crate::binary::op_as_const(op)?, - _ => bail!("Non-constant global initializer"), - }; - globals.push(value); - } - - for export in &bin.exports { - if let ExternalKind::Func = export.kind { - exports.insert(export.name.to_owned(), export.index); - } - } - for data in &bin.datas { let (memory_index, mut init) = match data.kind { DataKind::Active { memory_index, - init_expr, - } => (memory_index, init_expr.get_operators_reader()), + offset_expr, + } => (memory_index, offset_expr.get_operators_reader()), _ => continue, }; ensure!( @@ -417,7 +487,7 @@ impl Module { let offset = match (init.read()?, init.read()?, init.eof()) { (Operator::I32Const { value }, Operator::End, true) => value as usize, - x => bail!("Non-constant element segment offset expression {:?}", x), + x => bail!("Non-constant element segment offset expression {x:?}"), }; if !matches!( offset.checked_add(data.data.len()), @@ -425,14 +495,19 @@ impl Module { ) { bail!( "Out-of-bounds data memory init with offset {} and size {}", - offset, - data.data.len(), + offset.red(), + data.data.len().red(), ); } - memory.set_range(offset, data.data); + memory.set_range(offset, data.data)?; } for table in &bin.tables { + let element_type = table.element_type; + ensure!( + element_type == RefType::FUNCREF, + "unsupported table type {element_type}" + ); tables.push(Table { elems: vec![TableElement::default(); usize::try_from(table.initial).unwrap()], ty: *table, @@ -444,36 +519,27 @@ impl Module { let (t, mut init) = match elem.kind { ElementKind::Active { table_index, - init_expr, - } => (table_index, init_expr.get_operators_reader()), - _ => continue, + offset_expr, + } => ( + table_index.unwrap_or_default() as usize, + offset_expr.get_operators_reader(), + ), + _ => continue, // we don't support the ops that use these }; let offset = match (init.read()?, init.read()?, init.eof()) { (Operator::I32Const { value }, Operator::End, true) => value as usize, - x => bail!("Non-constant element segment offset expression {:?}", x), + x => bail!("Non-constant element segment offset expression {x:?}"), }; - let table = match tables.get_mut(t as usize) { - Some(t) => t, - None => bail!("Element segment for non-exsistent table {}", t), + let Some(table) = tables.get_mut(t) else { + bail!("Element segment for non-exsistent table {}", t.red()) }; - let expected_ty = table.ty.element_type; - ensure!( - expected_ty == elem.ty, - "Element type expected to be of table type {:?} but of type {:?}", - expected_ty, - elem.ty - ); let mut contents = vec![]; - let mut item_reader = elem.items.get_items_reader()?; - for _ in 0..item_reader.get_count() { - let item = item_reader.read()?; - let index = match item { - ElementItem::Func(index) => index, - ElementItem::Expr(_) => { - bail!("Non-constant element initializers are not supported") - } - }; + let ElementItems::Functions(item_reader) = elem.items.clone() else { + bail!("Non-constant element initializers are not supported"); + }; + for func in item_reader.into_iter() { + let index = func?; let func_ty = func_types[index as usize].clone(); contents.push(TableElement { val: Value::FuncRef(index), @@ -484,19 +550,22 @@ impl Module { let len = contents.len(); ensure!( offset.saturating_add(len) <= table.elems.len(), - "Out of bounds element segment at offset {} and length {} for table of length {}", - offset, - len, + "Out of bounds element segment at offset {offset} and length {len} for table of length {}", table.elems.len(), ); table.elems[offset..][..len].clone_from_slice(&contents); } + ensure!( + code.len() < (1usize << 31), + "Module function count must be under 2^31", + ); + ensure!(!code.is_empty(), "Module has no code"); let tables_hashes: Result<_, _> = tables.iter().map(Table::hash).collect(); Ok(Module { memory, - globals, + globals: bin.globals.clone(), tables_merkle: Merkle::new(MerkleType::Table, tables_hashes?), tables, funcs_merkle: Arc::new(Merkle::new( @@ -510,11 +579,39 @@ impl Module { host_call_hooks: Arc::new(host_call_hooks), start_function: bin.start, func_types: Arc::new(func_types), - exports: Arc::new(exports), + func_exports: Arc::new(func_exports), + all_exports: Arc::new(bin.exports.clone()), + extra_hash: Arc::new(crypto::keccak(&bin.extra_data).into()), }) } - fn hash(&self) -> Bytes32 { + pub fn from_user_binary( + bin: &WasmBinary, + debug_funcs: bool, + stylus_data: Option, + ) -> Result { + Self::from_binary( + bin, + &USER_IMPORTS, + &HashMap::default(), + false, + debug_funcs, + stylus_data, + ) + } + + pub fn name(&self) -> &str { + &self.names.module + } + + fn find_func(&self, name: &str) -> Result { + let Some(func) = self.func_exports.iter().find(|x| x.0 == name) else { + bail!("func {} not found in {}", name.red(), self.name().red()) + }; + Ok(*func.1) + } + + pub fn hash(&self) -> Bytes32 { let mut h = Keccak256::new(); h.update("Module:"); h.update( @@ -527,6 +624,7 @@ impl Module { h.update(self.memory.hash()); h.update(self.tables_merkle.root()); h.update(self.funcs_merkle.root()); + h.update(*self.extra_hash); h.update(self.internals_offset.to_be_bytes()); h.finalize().into() } @@ -548,11 +646,131 @@ impl Module { data.extend(self.tables_merkle.root()); data.extend(self.funcs_merkle.root()); - + data.extend(*self.extra_hash); data.extend(self.internals_offset.to_be_bytes()); - data } + + /// Serializes the `Module` into bytes that can be stored in the db. + /// The format employed is forward-compatible with future brotli dictionary and caching policies. + pub fn into_bytes(&self) -> Vec { + let data = bincode::serialize::(&self.into()).unwrap(); + let header = vec![1 + Into::::into(Dictionary::Empty)]; + brotli::compress_into(&data, header, 0, 22, Dictionary::Empty).expect("failed to compress") + } + + /// Deserializes a `Module` from db bytes. + /// + /// # Safety + /// + /// The bytes must have been produced by `into_bytes` and represent a valid `Module`. + pub unsafe fn from_bytes(data: &[u8]) -> Self { + let module = if data[0] > 0 { + let dict = Dictionary::try_from(data[0] - 1).expect("unknown dictionary"); + let data = brotli::decompress(&data[1..], dict).expect("failed to inflate"); + bincode::deserialize::(&data) + } else { + bincode::deserialize::(&data[1..]) + }; + module.unwrap().into() + } +} + +/// This type exists to provide a serde option for serializing all the fields of a `Module`. +#[derive(Clone, Debug, Default, Serialize, Deserialize)] +pub struct ModuleSerdeAll { + globals: Vec, + memory: Memory, + tables: Vec
, + tables_merkle: Merkle, + funcs: Vec, + funcs_merkle: Arc, + types: Arc>, + internals_offset: u32, + names: Arc, + host_call_hooks: Arc>>, + start_function: Option, + func_types: Arc>, + func_exports: Arc>, + all_exports: Arc, + extra_hash: Arc, +} + +impl From for Module { + fn from(module: ModuleSerdeAll) -> Self { + let funcs = module.funcs.into_iter().map(Function::from).collect(); + Self { + globals: module.globals, + memory: module.memory, + tables: module.tables, + tables_merkle: module.tables_merkle, + funcs: Arc::new(funcs), + funcs_merkle: module.funcs_merkle, + types: module.types, + internals_offset: module.internals_offset, + names: module.names, + host_call_hooks: module.host_call_hooks, + start_function: module.start_function, + func_types: module.func_types, + func_exports: module.func_exports, + all_exports: module.all_exports, + extra_hash: module.extra_hash, + } + } +} + +impl From<&Module> for ModuleSerdeAll { + fn from(module: &Module) -> Self { + let funcs = Vec::clone(&module.funcs); + Self { + globals: module.globals.clone(), + memory: module.memory.clone(), + tables: module.tables.clone(), + tables_merkle: module.tables_merkle.clone(), + funcs: funcs.into_iter().map(FunctionSerdeAll::from).collect(), + funcs_merkle: module.funcs_merkle.clone(), + types: module.types.clone(), + internals_offset: module.internals_offset, + names: module.names.clone(), + host_call_hooks: module.host_call_hooks.clone(), + start_function: module.start_function, + func_types: module.func_types.clone(), + func_exports: module.func_exports.clone(), + all_exports: module.all_exports.clone(), + extra_hash: module.extra_hash.clone(), + } + } +} + +#[serde_as] +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct FunctionSerdeAll { + code: Vec, + ty: FunctionType, + code_merkle: Merkle, + local_types: Vec, +} + +impl From for Function { + fn from(func: FunctionSerdeAll) -> Self { + Self { + code: func.code, + ty: func.ty, + code_merkle: func.code_merkle, + local_types: func.local_types, + } + } +} + +impl From for FunctionSerdeAll { + fn from(func: Function) -> Self { + Self { + code: func.code, + ty: func.ty, + code_merkle: func.code_merkle, + local_types: func.local_types, + } + } } // Globalstate holds: @@ -639,13 +857,39 @@ pub struct ModuleState<'a> { memory: Cow<'a, Memory>, } +/// Represents if the machine can recover and where to jump back if so. +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub enum ThreadState { + /// Execution is in the main thread. Errors are fatal. + Main, + /// Execution is in a cothread. Errors recover to the associated pc with the main thread. + CoThread(ProgramCounter), +} + +impl ThreadState { + fn is_cothread(&self) -> bool { + match self { + ThreadState::Main => false, + ThreadState::CoThread(_) => true, + } + } + + fn serialize(&self) -> Bytes32 { + match self { + ThreadState::Main => Bytes32([0xff; 32]), + ThreadState::CoThread(pc) => (*pc).serialize(), + } + } +} + #[derive(Serialize, Deserialize)] pub struct MachineState<'a> { steps: u64, // Not part of machine hash + thread_state: ThreadState, status: MachineStatus, - value_stack: Cow<'a, Vec>, + value_stacks: Cow<'a, Vec>>, internal_stack: Cow<'a, Vec>, - frame_stack: Cow<'a, Vec>, + frame_stacks: Cow<'a, Vec>>, modules: Vec>, global_state: GlobalState, pc: ProgramCounter, @@ -677,6 +921,7 @@ impl PreimageResolverWrapper { } } + #[cfg(feature = "native")] pub fn get(&mut self, context: u64, ty: PreimageType, hash: Bytes32) -> Option<&[u8]> { // TODO: this is unnecessarily complicated by the rust borrow checker. // This will probably be simplifiable when Polonius is shipped. @@ -692,6 +937,7 @@ impl PreimageResolverWrapper { } } + #[cfg(feature = "native")] pub fn get_const(&self, context: u64, ty: PreimageType, hash: Bytes32) -> Option { if let Some(resolved) = &self.last_resolved { if resolved.0 == hash { @@ -705,10 +951,11 @@ impl PreimageResolverWrapper { #[derive(Clone, Debug)] pub struct Machine { steps: u64, // Not part of machine hash + thread_state: ThreadState, status: MachineStatus, - value_stack: Vec, + value_stacks: Vec>, internal_stack: Vec, - frame_stack: Vec, + frame_stacks: Vec>, modules: Vec, modules_merkle: Option, global_state: GlobalState, @@ -717,35 +964,79 @@ pub struct Machine { inbox_contents: HashMap<(InboxIdentifier, u64), Vec>, first_too_far: u64, // Not part of machine hash preimage_resolver: PreimageResolverWrapper, + /// Linkable Stylus modules in compressed form. Not part of the machine hash. + stylus_modules: HashMap>, initial_hash: Bytes32, context: u64, + debug_info: bool, // Not part of machine hash } -fn hash_stack(stack: I, prefix: &str) -> Bytes32 +type FrameStackHash = Bytes32; +type ValueStackHash = Bytes32; +type MultiStackHash = Bytes32; +type InterStackHash = Bytes32; + +pub(crate) fn hash_stack(stack: I, prefix: &str) -> Bytes32 where I: IntoIterator, D: AsRef<[u8]>, { + hash_stack_with_heights(stack, &[], prefix).0 +} + +/// Hashes a stack of n elements, returning the values at various heights along the way in O(n). +fn hash_stack_with_heights( + stack: I, + mut heights: &[usize], + prefix: &str, +) -> (Bytes32, Vec) +where + I: IntoIterator, + D: AsRef<[u8]>, +{ + let mut parts = vec![]; let mut hash = Bytes32::default(); + let mut count = 0; for item in stack.into_iter() { - let mut h = Keccak256::new(); - h.update(prefix); - h.update(item.as_ref()); - h.update(hash); - hash = h.finalize().into(); + while heights.first() == Some(&count) { + parts.push(hash); + heights = &heights[1..]; + } + + hash = Keccak256::new() + .chain(prefix) + .chain(item.as_ref()) + .chain(hash) + .finalize() + .into(); + + count += 1; } - hash + while !heights.is_empty() { + assert_eq!(heights[0], count); + parts.push(hash); + heights = &heights[1..]; + } + (hash, parts) } -fn hash_value_stack(stack: &[Value]) -> Bytes32 { +fn hash_value_stack(stack: &[Value]) -> ValueStackHash { hash_stack(stack.iter().map(|v| v.hash()), "Value stack:") } -fn hash_stack_frame_stack(frames: &[StackFrame]) -> Bytes32 { +fn hash_stack_frame_stack(frames: &[StackFrame]) -> FrameStackHash { hash_stack(frames.iter().map(|f| f.hash()), "Stack frame stack:") } +fn hash_multistack(multistack: &[&[T]], stack_hasher: F) -> MultiStackHash +where + F: Fn(&[T]) -> Bytes32, +{ + hash_stack(multistack.iter().map(|v| stack_hasher(v)), "cothread:") +} + #[must_use] +#[cfg(feature = "native")] fn prove_window(items: &[T], stack_hasher: F, encoder: G) -> Vec where F: Fn(&[T]) -> Bytes32, @@ -766,6 +1057,7 @@ where } #[must_use] +#[cfg(feature = "native")] fn prove_stack( items: &[T], proving_depth: usize, @@ -787,7 +1079,52 @@ where data } +// prove_multistacks encodes proof for multistacks: +// - Proof of first(main) if not cothread otherwise last +// - Hash of first if cothread, otherwise last +// - Recursive hash of the rest +// If length is < 1, hash of last element is assumed 0xff..f, same for hash +// of in-between stacks ([2nd..last)). +// Accepts prover function so that it can work both for proving stack and window. +#[must_use] +#[cfg(feature = "native")] +fn prove_multistack( + cothread: bool, + items: Vec<&[T]>, + stack_hasher: F, + multistack_hasher: MF, + prover: fn(&[T]) -> Vec, +) -> Vec +where + F: Fn(&[T]) -> Bytes32, + MF: Fn(&[&[T]], F) -> Bytes32, +{ + let mut data = Vec::with_capacity(33); + + if cothread { + data.extend(prover(items.last().unwrap())); + data.extend(stack_hasher(items.first().unwrap())) + } else { + data.extend(prover(items.first().unwrap())); + + let last_hash = if items.len() > 1 { + stack_hasher(items.last().unwrap()) + } else { + Machine::NO_STACK_HASH + }; + data.extend(last_hash); + } + let hash: Bytes32 = if items.len() > 2 { + multistack_hasher(&items[1..items.len() - 1], stack_hasher) + } else { + Bytes32::default() + }; + data.extend(hash); + data +} + #[must_use] +#[cfg(feature = "native")] fn exec_ibin_op(a: T, b: T, op: IBinOpType) -> Option where Wrapping: ReinterpretAsSigned, @@ -823,6 +1160,7 @@ where } #[must_use] +#[cfg(feature = "native")] fn exec_iun_op(a: T, op: IUnOpType) -> u32 where T: PrimInt, @@ -834,6 +1172,7 @@ where } } +#[cfg(feature = "native")] fn exec_irel_op(a: T, b: T, op: IRelOpType) -> Value where T: Ord, @@ -855,6 +1194,7 @@ pub fn get_empty_preimage_resolver() -> PreimageResolver { impl Machine { pub const MAX_STEPS: u64 = 1 << 43; + pub const NO_STACK_HASH: Bytes32 = Bytes32([255_u8; 32]); pub fn from_paths( library_paths: &[PathBuf], @@ -862,21 +1202,23 @@ impl Machine { language_support: bool, always_merkleize: bool, allow_hostapi_from_main: bool, + debug_funcs: bool, + debug_info: bool, global_state: GlobalState, inbox_contents: HashMap<(InboxIdentifier, u64), Vec>, preimage_resolver: PreimageResolver, ) -> Result { let bin_source = file_bytes(binary_path)?; - let bin = parse(&bin_source) + let bin = parse(&bin_source, binary_path) .wrap_err_with(|| format!("failed to validate WASM binary at {:?}", binary_path))?; let mut libraries = vec![]; let mut lib_sources = vec![]; for path in library_paths { let error_message = format!("failed to validate WASM binary at {:?}", path); - lib_sources.push((file_bytes(path)?, error_message)); + lib_sources.push((file_bytes(path)?, path, error_message)); } - for (source, error_message) in &lib_sources { - let library = parse(source).wrap_err_with(|| error_message.clone())?; + for (source, path, error_message) in &lib_sources { + let library = parse(source, path).wrap_err_with(|| error_message.clone())?; libraries.push(library); } Self::from_binaries( @@ -885,21 +1227,90 @@ impl Machine { language_support, always_merkleize, allow_hostapi_from_main, + debug_funcs, + debug_info, global_state, inbox_contents, preimage_resolver, + None, ) } + /// Creates an instrumented user Machine from the wasm or wat at the given `path`. + #[cfg(feature = "native")] + pub fn from_user_path(path: &Path, compile: &CompileConfig) -> Result { + let data = std::fs::read(path)?; + let wasm = wasmer::wat2wasm(&data)?; + let mut bin = binary::parse(&wasm, Path::new("user"))?; + let stylus_data = bin.instrument(compile, &Bytes32::default())?; + + let user_test = std::fs::read("../../target/machines/latest/user_test.wasm")?; + let user_test = parse(&user_test, Path::new("user_test"))?; + let wasi_stub = std::fs::read("../../target/machines/latest/wasi_stub.wasm")?; + let wasi_stub = parse(&wasi_stub, Path::new("wasi_stub"))?; + let soft_float = std::fs::read("../../target/machines/latest/soft-float.wasm")?; + let soft_float = parse(&soft_float, Path::new("soft-float"))?; + + let mut machine = Self::from_binaries( + &[soft_float, wasi_stub, user_test], + bin, + false, + false, + false, + compile.debug.debug_funcs, + true, + GlobalState::default(), + HashMap::default(), + Arc::new(|_, _, _| panic!("tried to read preimage")), + Some(stylus_data), + )?; + + let footprint: u32 = stylus_data.footprint.into(); + machine.call_function("user_test", "set_pages", vec![footprint.into()])?; + Ok(machine) + } + + /// Adds a user program to the machine's known set of wasms, compiling it into a link-able module. + /// Note that the module produced will need to be configured before execution via hostio calls. + pub fn add_program( + &mut self, + wasm: &[u8], + codehash: &Bytes32, + version: u16, + debug_funcs: bool, + ) -> Result { + let mut bin = binary::parse(wasm, Path::new("user"))?; + let config = CompileConfig::version(version, debug_funcs); + let stylus_data = bin.instrument(&config, codehash)?; + + // enable debug mode if debug funcs are available + if debug_funcs { + self.debug_info = true; + } + + let module = Module::from_user_binary(&bin, debug_funcs, Some(stylus_data))?; + let hash = module.hash(); + self.add_stylus_module(hash, module.into_bytes()); + Ok(hash) + } + + /// Adds a pre-built program to the machine's known set of wasms. + pub fn add_stylus_module(&mut self, hash: Bytes32, module: Vec) { + self.stylus_modules.insert(hash, module); + } + pub fn from_binaries( libraries: &[WasmBinary<'_>], bin: WasmBinary<'_>, runtime_support: bool, always_merkleize: bool, allow_hostapi_from_main: bool, + debug_funcs: bool, + debug_info: bool, global_state: GlobalState, inbox_contents: HashMap<(InboxIdentifier, u64), Vec>, preimage_resolver: PreimageResolver, + stylus_data: Option, ) -> Result { use ArbValueType::*; @@ -907,40 +1318,49 @@ impl Machine { let mut modules = vec![Module::default()]; let mut available_imports = HashMap::default(); let mut floating_point_impls = HashMap::default(); - - for export in &bin.exports { - if let ExternalKind::Func = export.kind { - if let Some(ty_idx) = usize::try_from(export.index) - .unwrap() - .checked_sub(bin.imports.len()) - { - let ty = bin.functions[ty_idx]; - let ty = &bin.types[usize::try_from(ty).unwrap()]; - let module = u32::try_from(modules.len() + libraries.len()).unwrap(); + let main_module_index = u32::try_from(modules.len() + libraries.len())?; + + // make the main module's exports available to libraries + for (name, &(export, kind)) in &bin.exports { + if kind == ExportKind::Func { + let index: usize = export.try_into()?; + if let Some(index) = index.checked_sub(bin.imports.len()) { + let ty: usize = bin.functions[index].try_into()?; + let ty = bin.types[ty].clone(); available_imports.insert( - format!("env__wavm_guest_call__{}", export.name), - AvailableImport { - ty: ty.clone(), - module, - func: export.index, - }, + format!("env__wavm_guest_call__{name}"), + AvailableImport::new(ty, main_module_index, export), ); } } } + // collect all the library exports in advance so they can use each other's + for (index, lib) in libraries.iter().enumerate() { + let module = 1 + index as u32; // off by one due to the entry point + for (name, &(export, kind)) in &lib.exports { + if kind == ExportKind::Func { + let ty = match lib.get_function(FunctionIndex::from_u32(export)) { + Ok(ty) => ty, + Err(error) => bail!("failed to read export {name}: {error}"), + }; + let import = AvailableImport::new(ty, module, export); + available_imports.insert(name.to_owned(), import); + } + } + } + for lib in libraries { - let module = Module::from_binary(lib, &available_imports, &floating_point_impls, true)?; - for (name, &func) in &*module.exports { + let module = Module::from_binary( + lib, + &available_imports, + &floating_point_impls, + true, + debug_funcs, + None, + )?; + for (name, &func) in &*module.func_exports { let ty = module.func_types[func as usize].clone(); - available_imports.insert( - name.clone(), - AvailableImport { - module: modules.len() as u32, - func, - ty: ty.clone(), - }, - ); if let Ok(op) = name.parse::() { let mut sig = op.signature(); // wavm codegen takes care of effecting this type change at callsites @@ -953,10 +1373,10 @@ impl Machine { } ensure!( ty == sig, - "Wrong type for floating point impl {:?} expecting {:?} but got {:?}", - name, - sig, - ty + "Wrong type for floating point impl {} expecting {} but got {}", + name.red(), + sig.red(), + ty.red() ); floating_point_impls.insert(op, (modules.len() as u32, func)); } @@ -964,13 +1384,15 @@ impl Machine { modules.push(module); } - // Shouldn't be necessary, but to safe, don't allow the main binary to import its own guest calls + // Shouldn't be necessary, but to be safe, don't allow the main binary to import its own guest calls available_imports.retain(|_, i| i.module as usize != modules.len()); modules.push(Module::from_binary( &bin, &available_imports, &floating_point_impls, allow_hostapi_from_main, + debug_funcs, + stylus_data, )?); // Build the entrypoint module @@ -1003,11 +1425,12 @@ impl Machine { } let main_module_idx = modules.len() - 1; let main_module = &modules[main_module_idx]; + let main_exports = &main_module.func_exports; // Rust support let rust_fn = "__main_void"; - if let Some(&f) = main_module.exports.get(rust_fn).filter(|_| runtime_support) { - let expected_type = FunctionType::new(vec![], vec![I32]); + if let Some(&f) = main_exports.get(rust_fn).filter(|_| runtime_support) { + let expected_type = FunctionType::new([], [I32]); ensure!( main_module.func_types[f as usize] == expected_type, "Main function doesn't match expected signature of [] -> [ret]", @@ -1017,59 +1440,15 @@ impl Machine { entry!(HaltAndSetFinished); } - // Go support - if let Some(&f) = main_module.exports.get("run").filter(|_| runtime_support) { - let mut expected_type = FunctionType::default(); - expected_type.inputs.push(I32); // argc - expected_type.inputs.push(I32); // argv + // Go/wasi support + if let Some(&f) = main_exports.get("_start").filter(|_| runtime_support) { + let expected_type = FunctionType::new([], []); ensure!( main_module.func_types[f as usize] == expected_type, - "Run function doesn't match expected signature of [argc, argv]", - ); - // Go's flags library panics if the argument list is empty. - // To pass in the program name argument, we need to put it in memory. - // The Go linker guarantees a section of memory starting at byte 4096 is available for this purpose. - // https://github.com/golang/go/blob/252324e879e32f948d885f787decf8af06f82be9/misc/wasm/wasm_exec.js#L520 - // These memory stores also assume that the Go module's memory is large enough to begin with. - // That's also handled by the Go compiler. Go 1.17.5 in the compilation of the arbitrator go test case - // initializes its memory to 272 pages long (about 18MB), much larger than the required space. - let free_memory_base = 4096; - let name_str_ptr = free_memory_base; - let argv_ptr = name_str_ptr + 8; - ensure!( - main_module.internals_offset != 0, - "Main module doesn't have internals" + "Main function doesn't match expected signature of [] -> []", ); - let main_module_idx = u32::try_from(main_module_idx).unwrap(); - let main_module_store32 = main_module.internals_offset + 3; - - // Write "js\0" to name_str_ptr, to match what the actual JS environment does - entry!(I32Const, name_str_ptr); - entry!(I32Const, 0x736a); // b"js\0" - entry!(@cross, main_module_idx, main_module_store32); - entry!(I32Const, name_str_ptr + 4); - entry!(I32Const, 0); - entry!(@cross, main_module_idx, main_module_store32); - - // Write name_str_ptr to argv_ptr - entry!(I32Const, argv_ptr); - entry!(I32Const, name_str_ptr); - entry!(@cross, main_module_idx, main_module_store32); - entry!(I32Const, argv_ptr + 4); - entry!(I32Const, 0); - entry!(@cross, main_module_idx, main_module_store32); - - // Launch main with an argument count of 1 and argv_ptr - entry!(I32Const, 1); - entry!(I32Const, argv_ptr); - entry!(@cross, main_module_idx, f); - if let Some(i) = available_imports.get("wavm__go_after_run") { - ensure!( - i.ty == FunctionType::default(), - "Resume function has non-empty function signature", - ); - entry!(@cross, i.module, i.func); - } + entry!(@cross, u32::try_from(main_module_idx).unwrap(), f); + entry!(HaltAndSetFinished); } let entrypoint_types = vec![FunctionType::default()]; @@ -1102,10 +1481,12 @@ impl Machine { types: Arc::new(entrypoint_types), names: Arc::new(entrypoint_names), internals_offset: 0, - host_call_hooks: Arc::new(Vec::new()), + host_call_hooks: Default::default(), start_function: None, func_types: Arc::new(vec![FunctionType::default()]), - exports: Arc::new(HashMap::default()), + func_exports: Default::default(), + all_exports: Default::default(), + extra_hash: Default::default(), }; modules[0] = entrypoint; @@ -1148,10 +1529,11 @@ impl Machine { let mut mach = Machine { status: MachineStatus::Running, + thread_state: ThreadState::Main, steps: 0, - value_stack: vec![Value::RefNull, Value::I32(0), Value::I32(0)], + value_stacks: vec![vec![Value::RefNull, Value::I32(0), Value::I32(0)]], internal_stack: Vec::new(), - frame_stack: Vec::new(), + frame_stacks: vec![Vec::new()], modules, modules_merkle, global_state, @@ -1160,17 +1542,24 @@ impl Machine { inbox_contents, first_too_far, preimage_resolver: PreimageResolverWrapper::new(preimage_resolver), + stylus_modules: HashMap::default(), initial_hash: Bytes32::default(), context: 0, + debug_info, }; mach.initial_hash = mach.hash(); Ok(mach) } pub fn new_from_wavm(wavm_binary: &Path) -> Result { - let f = BufReader::new(File::open(wavm_binary)?); - let decompressor = brotli2::read::BrotliDecoder::new(f); - let mut modules: Vec = bincode::deserialize_from(decompressor)?; + let mut modules: Vec = { + let compressed = std::fs::read(wavm_binary)?; + let Ok(modules) = brotli::decompress(&compressed, Dictionary::Empty) else { + bail!("failed to decompress wavm binary"); + }; + bincode::deserialize(&modules)? + }; + for module in modules.iter_mut() { for table in module.tables.iter_mut() { table.elems_merkle = Merkle::new( @@ -1181,14 +1570,9 @@ impl Machine { let tables: Result<_> = module.tables.iter().map(Table::hash).collect(); module.tables_merkle = Merkle::new(MerkleType::Table, tables?); - let funcs = - Arc::get_mut(&mut module.funcs).expect("Multiple copies of module functions"); - for func in funcs.iter_mut() { - func.code_merkle = Merkle::new( - MerkleType::Instruction, - func.code.par_iter().map(|i| i.hash()).collect(), - ); - } + let funcs = Arc::get_mut(&mut module.funcs).expect("Multiple copies of module funcs"); + funcs.iter_mut().for_each(Function::set_code_merkle); + module.funcs_merkle = Arc::new(Merkle::new( MerkleType::Function, module.funcs.iter().map(Function::hash).collect(), @@ -1196,10 +1580,11 @@ impl Machine { } let mut mach = Machine { status: MachineStatus::Running, + thread_state: ThreadState::Main, steps: 0, - value_stack: vec![Value::RefNull, Value::I32(0), Value::I32(0)], + value_stacks: vec![vec![Value::RefNull, Value::I32(0), Value::I32(0)]], internal_stack: Vec::new(), - frame_stack: Vec::new(), + frame_stacks: vec![Vec::new()], modules, modules_merkle: None, global_state: Default::default(), @@ -1208,8 +1593,10 @@ impl Machine { inbox_contents: Default::default(), first_too_far: 0, preimage_resolver: PreimageResolverWrapper::new(get_empty_preimage_resolver()), + stylus_modules: HashMap::default(), initial_hash: Bytes32::default(), context: 0, + debug_info: false, }; mach.initial_hash = mach.hash(); Ok(mach) @@ -1220,12 +1607,14 @@ impl Machine { self.hash() == self.initial_hash, "serialize_binary can only be called on initial machine", ); - let mut f = File::create(path)?; - let mut compressor = brotli2::write::BrotliEncoder::new(BufWriter::new(&mut f), 9); - bincode::serialize_into(&mut compressor, &self.modules)?; - compressor.flush()?; - drop(compressor); - f.sync_data()?; + let modules = bincode::serialize(&self.modules)?; + let window = brotli::DEFAULT_WINDOW_SIZE; + let Ok(output) = brotli::compress(&modules, 9, window, Dictionary::Empty) else { + bail!("failed to compress binary"); + }; + + let mut file = File::create(path)?; + file.write_all(&output)?; Ok(()) } @@ -1242,10 +1631,11 @@ impl Machine { .collect(); let state = MachineState { steps: self.steps, + thread_state: self.thread_state, status: self.status, - value_stack: Cow::Borrowed(&self.value_stack), + value_stacks: Cow::Borrowed(&self.value_stacks), internal_stack: Cow::Borrowed(&self.internal_stack), - frame_stack: Cow::Borrowed(&self.frame_stack), + frame_stacks: Cow::Borrowed(&self.frame_stacks), modules, global_state: self.global_state.clone(), pc: self.pc, @@ -1279,9 +1669,9 @@ impl Machine { } self.steps = new_state.steps; self.status = new_state.status; - self.value_stack = new_state.value_stack.into_owned(); + self.value_stacks = new_state.value_stacks.into_owned(); self.internal_stack = new_state.internal_stack.into_owned(); - self.frame_stack = new_state.frame_stack.into_owned(); + self.frame_stacks = new_state.frame_stacks.into_owned(); self.global_state = new_state.global_state; self.pc = new_state.pc; self.stdio_output = new_state.stdio_output.into_owned(); @@ -1305,37 +1695,154 @@ impl Machine { } } - pub fn jump_into_function(&mut self, func: &str, mut args: Vec) { + pub fn main_module_name(&self) -> String { + self.modules.last().expect("no module").name().to_owned() + } + + pub fn main_module_memory(&self) -> &Memory { + &self.modules.last().expect("no module").memory + } + + pub fn main_module_hash(&self) -> Bytes32 { + self.modules.last().expect("no module").hash() + } + + /// finds the first module with the given name + pub fn find_module(&self, name: &str) -> Result { + let Some(module) = self.modules.iter().position(|m| m.name() == name) else { + let names: Vec<_> = self.modules.iter().map(|m| m.name()).collect(); + let names = names.join(", "); + bail!("module {} not found among: {names}", name.red()) + }; + Ok(module as u32) + } + + pub fn find_module_func(&self, module: &str, func: &str) -> Result<(u32, u32)> { + let qualified = format!("{module}__{func}"); + let offset = self.find_module(module)?; + let module = &self.modules[offset as usize]; + let func = module + .find_func(func) + .or_else(|_| module.find_func(&qualified))?; + Ok((offset, func)) + } + + pub fn jump_into_func(&mut self, module: u32, func: u32, mut args: Vec) -> Result<()> { + let Some(source_module) = self.modules.get(module as usize) else { + bail!("no module at offset {}", module.red()) + }; + let Some(source_func) = source_module.funcs.get(func as usize) else { + bail!( + "no func at offset {} in module {}", + func.red(), + source_module.name().red() + ) + }; + let ty = &source_func.ty; + if ty.inputs.len() != args.len() { + let name = source_module.names.functions.get(&func).unwrap(); + bail!( + "func {} has type {} but received args {:?}", + name.red(), + ty.red(), + args.debug_red(), + ) + } + let frame_args = [Value::RefNull, Value::I32(0), Value::I32(0)]; args.extend(frame_args); - self.value_stack = args; + self.value_stacks[0] = args; - let module = self.modules.last().expect("no module"); - let export = module.exports.iter().find(|x| x.0 == func); - let export = export - .unwrap_or_else(|| panic!("func {} not found", func)) - .1; - - self.frame_stack.clear(); + self.frame_stacks[0].clear(); self.internal_stack.clear(); self.pc = ProgramCounter { - module: (self.modules.len() - 1).try_into().unwrap(), - func: *export, + module, + func, inst: 0, }; self.status = MachineStatus::Running; self.steps = 0; + Ok(()) } pub fn get_final_result(&self) -> Result> { - if !self.frame_stack.is_empty() { + if self.thread_state.is_cothread() { + bail!("machine in cothread when expecting final result") + } + if !self.frame_stacks[0].is_empty() { bail!( - "machine has not successfully computed a final result {:?}", - self.status + "machine has not successfully computed a final result {}", + self.status.red() ) } - Ok(self.value_stack.clone()) + Ok(self.value_stacks[0].clone()) + } + + #[cfg(feature = "native")] + pub fn call_function( + &mut self, + module: &str, + func: &str, + args: Vec, + ) -> Result> { + let (module, func) = self.find_module_func(module, func)?; + self.jump_into_func(module, func, args)?; + self.step_n(Machine::MAX_STEPS)?; + self.get_final_result() + } + + #[cfg(feature = "native")] + pub fn call_user_func(&mut self, func: &str, args: Vec, ink: u64) -> Result> { + self.set_ink(ink); + self.call_function("user", func, args) + } + + /// Gets the *last* global with the given name, if one exists + /// Note: two globals may have the same name, so use carefully! + pub fn get_global(&self, name: &str) -> Result { + for module in self.modules.iter().rev() { + if let Some((global, ExportKind::Global)) = module.all_exports.get(name) { + return Ok(module.globals[*global as usize]); + } + } + bail!("global {} not found", name.red()) + } + + /// Sets the *last* global with the given name, if one exists + /// Note: two globals may have the same name, so use carefully! + pub fn set_global(&mut self, name: &str, value: Value) -> Result<()> { + for module in self.modules.iter_mut().rev() { + if let Some((global, ExportKind::Global)) = module.all_exports.get(name) { + module.globals[*global as usize] = value; + return Ok(()); + } + } + bail!("global {} not found", name.red()) + } + + pub fn read_memory(&self, module: u32, ptr: u32, len: u32) -> Result<&[u8]> { + let Some(module) = &self.modules.get(module as usize) else { + bail!("no module at offset {}", module.red()) + }; + let memory = module.memory.get_range(ptr as usize, len as usize); + let error = || format!("failed memory read of {} bytes @ {}", len.red(), ptr.red()); + memory.ok_or_else(|| eyre!(error())) + } + + pub fn write_memory(&mut self, module: u32, ptr: u32, data: &[u8]) -> Result<()> { + let Some(module) = &mut self.modules.get_mut(module as usize) else { + bail!("no module at offset {}", module.red()) + }; + if let Err(err) = module.memory.set_range(ptr as usize, data) { + let msg = eyre!( + "failed to write {} bytes to memory @ {}", + data.len().red(), + ptr.red() + ); + bail!(err.wrap_err(msg)); + } + Ok(()) } pub fn get_next_instruction(&self) -> Option { @@ -1361,21 +1868,44 @@ impl Machine { Some(self.pc) } + #[cfg(feature = "native")] fn test_next_instruction(func: &Function, pc: &ProgramCounter) { - debug_assert!(func.code.len() > pc.inst.try_into().unwrap()); + let inst: usize = pc.inst.try_into().unwrap(); + debug_assert!(func.code.len() > inst); } pub fn get_steps(&self) -> u64 { self.steps } + #[cfg(feature = "native")] pub fn step_n(&mut self, n: u64) -> Result<()> { if self.is_halted() { return Ok(()); } + let (mut value_stack, mut frame_stack) = match self.thread_state { + ThreadState::Main => (&mut self.value_stacks[0], &mut self.frame_stacks[0]), + ThreadState::CoThread(_) => ( + self.value_stacks.last_mut().unwrap(), + self.frame_stacks.last_mut().unwrap(), + ), + }; let mut module = &mut self.modules[self.pc.module()]; let mut func = &module.funcs[self.pc.func()]; + macro_rules! reset_refs { + () => { + (value_stack, frame_stack) = match self.thread_state { + ThreadState::Main => (&mut self.value_stacks[0], &mut self.frame_stacks[0]), + ThreadState::CoThread(_) => ( + self.value_stacks.last_mut().unwrap(), + self.frame_stacks.last_mut().unwrap(), + ), + }; + module = &mut self.modules[self.pc.module()]; + func = &module.funcs[self.pc.func()]; + }; + } macro_rules! flush_module { () => { if let Some(merkle) = self.modules_merkle.as_mut() { @@ -1384,8 +1914,31 @@ impl Machine { }; } macro_rules! error { - () => {{ + () => { + error!("") + }; + ($format:expr $(, $message:expr)*) => {{ + flush_module!(); + + if self.debug_info { + println!("\n{} {}", "error on line".grey(), line!().pink()); + println!($format, $($message.pink()),*); + println!("{}", "backtrace:".grey()); + self.print_backtrace(true); + } + + if let ThreadState::CoThread(recovery_pc) = self.thread_state { + self.thread_state = ThreadState::Main; + self.pc = recovery_pc; + reset_refs!(); + if self.debug_info { + println!("\n{}", "switching to main thread".grey()); + println!("\n{} {:?}", "next opcode: ".grey(), func.code[self.pc.inst()]); + } + continue; + } self.status = MachineStatus::Errored; + module = &mut self.modules[self.pc.module()]; break; }}; } @@ -1393,18 +1946,23 @@ impl Machine { for _ in 0..n { self.steps += 1; if self.steps == Self::MAX_STEPS { - error!(); + println!("\n{}", "Machine out of steps".red()); + self.status = MachineStatus::Errored; + self.print_backtrace(true); + module = &mut self.modules[self.pc.module()]; + break; } + let inst = func.code[self.pc.inst()]; self.pc.inst += 1; match inst.opcode { - Opcode::Unreachable => error!(), + Opcode::Unreachable => error!("unreachable"), Opcode::Nop => {} Opcode::InitFrame => { - let caller_module_internals = self.value_stack.pop().unwrap().assume_u32(); - let caller_module = self.value_stack.pop().unwrap().assume_u32(); - let return_ref = self.value_stack.pop().unwrap(); - self.frame_stack.push(StackFrame { + let caller_module_internals = value_stack.pop().unwrap().assume_u32(); + let caller_module = value_stack.pop().unwrap().assume_u32(); + let return_ref = value_stack.pop().unwrap(); + frame_stack.push(StackFrame { return_ref, locals: func .local_types @@ -1421,15 +1979,15 @@ impl Machine { .and_then(|h| h.as_ref()) { if let Err(err) = Self::host_call_hook( - &self.value_stack, + value_stack, module, &mut self.stdio_output, &hook.0, &hook.1, ) { eprintln!( - "Failed to process host call hook for host call {:?} {:?}: {}", - hook.0, hook.1, err, + "Failed to process host call hook for host call {:?} {:?}: {err}", + hook.0, hook.1, ); } } @@ -1439,14 +1997,14 @@ impl Machine { Machine::test_next_instruction(func, &self.pc); } Opcode::ArbitraryJumpIf => { - let x = self.value_stack.pop().unwrap(); + let x = value_stack.pop().unwrap(); if !x.is_i32_zero() { self.pc.inst = inst.argument_data as u32; Machine::test_next_instruction(func, &self.pc); } } Opcode::Return => { - let frame = self.frame_stack.pop().unwrap(); + let frame = frame_stack.pop().unwrap(); match frame.return_ref { Value::RefNull => error!(), Value::InternalRef(pc) => { @@ -1464,34 +2022,56 @@ impl Machine { } } Opcode::Call => { - let current_frame = self.frame_stack.last().unwrap(); - self.value_stack.push(Value::InternalRef(self.pc)); - self.value_stack - .push(Value::I32(current_frame.caller_module)); - self.value_stack - .push(Value::I32(current_frame.caller_module_internals)); + let frame = frame_stack.last().unwrap(); + value_stack.push(Value::InternalRef(self.pc)); + value_stack.push(frame.caller_module.into()); + value_stack.push(frame.caller_module_internals.into()); self.pc.func = inst.argument_data as u32; self.pc.inst = 0; func = &module.funcs[self.pc.func()]; } Opcode::CrossModuleCall => { flush_module!(); - self.value_stack.push(Value::InternalRef(self.pc)); - self.value_stack.push(Value::I32(self.pc.module)); - self.value_stack.push(Value::I32(module.internals_offset)); + value_stack.push(Value::InternalRef(self.pc)); + value_stack.push(self.pc.module.into()); + value_stack.push(module.internals_offset.into()); let (call_module, call_func) = unpack_cross_module_call(inst.argument_data); self.pc.module = call_module; self.pc.func = call_func; self.pc.inst = 0; - module = &mut self.modules[self.pc.module()]; - func = &module.funcs[self.pc.func()]; + reset_refs!(); + } + Opcode::CrossModuleForward => { + flush_module!(); + let frame = frame_stack.last().unwrap(); + value_stack.push(Value::InternalRef(self.pc)); + value_stack.push(frame.caller_module.into()); + value_stack.push(frame.caller_module_internals.into()); + let (call_module, call_func) = unpack_cross_module_call(inst.argument_data); + self.pc.module = call_module; + self.pc.func = call_func; + self.pc.inst = 0; + reset_refs!(); + } + Opcode::CrossModuleInternalCall => { + flush_module!(); + let call_internal = inst.argument_data as u32; + let call_module = value_stack.pop().unwrap().assume_u32(); + value_stack.push(Value::InternalRef(self.pc)); + value_stack.push(self.pc.module.into()); + value_stack.push(module.internals_offset.into()); + module = &mut self.modules[call_module as usize]; + self.pc.module = call_module; + self.pc.func = module.internals_offset + call_internal; + self.pc.inst = 0; + reset_refs!(); } Opcode::CallerModuleInternalCall => { - self.value_stack.push(Value::InternalRef(self.pc)); - self.value_stack.push(Value::I32(self.pc.module)); - self.value_stack.push(Value::I32(module.internals_offset)); + value_stack.push(Value::InternalRef(self.pc)); + value_stack.push(self.pc.module.into()); + value_stack.push(module.internals_offset.into()); - let current_frame = self.frame_stack.last().unwrap(); + let current_frame = frame_stack.last().unwrap(); if current_frame.caller_module_internals > 0 { let func_idx = u32::try_from(inst.argument_data) .ok() @@ -1501,8 +2081,7 @@ impl Machine { self.pc.module = current_frame.caller_module; self.pc.func = func_idx; self.pc.inst = 0; - module = &mut self.modules[self.pc.module()]; - func = &module.funcs[self.pc.func()]; + reset_refs!(); } else { // The caller module has no internals error!(); @@ -1510,7 +2089,7 @@ impl Machine { } Opcode::CallIndirect => { let (table, ty) = crate::wavm::unpack_call_indirect(inst.argument_data); - let idx = match self.value_stack.pop() { + let idx = match value_stack.pop() { Some(Value::I32(i)) => usize::try_from(i).unwrap(), x => bail!( "WASM validation failed: top of stack before call_indirect is {:?}", @@ -1519,63 +2098,60 @@ impl Machine { }; let ty = &module.types[usize::try_from(ty).unwrap()]; let elems = &module.tables[usize::try_from(table).unwrap()].elems; - if let Some(elem) = elems.get(idx).filter(|e| &e.func_ty == ty) { - match elem.val { - Value::FuncRef(call_func) => { - let current_frame = self.frame_stack.last().unwrap(); - self.value_stack.push(Value::InternalRef(self.pc)); - self.value_stack - .push(Value::I32(current_frame.caller_module)); - self.value_stack - .push(Value::I32(current_frame.caller_module_internals)); - self.pc.func = call_func; - self.pc.inst = 0; - func = &module.funcs[self.pc.func()]; - } - Value::RefNull => error!(), - v => bail!("invalid table element value {:?}", v), + let Some(elem) = elems.get(idx).filter(|e| &e.func_ty == ty) else { + error!() + }; + match elem.val { + Value::FuncRef(call_func) => { + let frame = frame_stack.last().unwrap(); + value_stack.push(Value::InternalRef(self.pc)); + value_stack.push(frame.caller_module.into()); + value_stack.push(frame.caller_module_internals.into()); + self.pc.func = call_func; + self.pc.inst = 0; + func = &module.funcs[self.pc.func()]; } - } else { - error!(); + Value::RefNull => error!(), + v => bail!("invalid table element value {:?}", v), } } Opcode::LocalGet => { - let val = self.frame_stack.last().unwrap().locals[inst.argument_data as usize]; - self.value_stack.push(val); + let val = frame_stack.last().unwrap().locals[inst.argument_data as usize]; + value_stack.push(val); } Opcode::LocalSet => { - let val = self.value_stack.pop().unwrap(); - self.frame_stack.last_mut().unwrap().locals[inst.argument_data as usize] = val; + let val = value_stack.pop().unwrap(); + let locals = &mut frame_stack.last_mut().unwrap().locals; + if locals.len() <= inst.argument_data as usize { + error!("not enough locals") + } + locals[inst.argument_data as usize] = val; } Opcode::GlobalGet => { - self.value_stack - .push(module.globals[inst.argument_data as usize]); + value_stack.push(module.globals[inst.argument_data as usize]); } Opcode::GlobalSet => { - let val = self.value_stack.pop().unwrap(); + let val = value_stack.pop().unwrap(); module.globals[inst.argument_data as usize] = val; } Opcode::MemoryLoad { ty, bytes, signed } => { - let base = match self.value_stack.pop() { + let base = match value_stack.pop() { Some(Value::I32(x)) => x, x => bail!( "WASM validation failed: top of stack before memory load is {:?}", x, ), }; - if let Some(idx) = inst.argument_data.checked_add(base.into()) { - let val = module.memory.get_value(idx, ty, bytes, signed); - if let Some(val) = val { - self.value_stack.push(val); - } else { - error!(); - } - } else { - error!(); - } + let Some(index) = inst.argument_data.checked_add(base.into()) else { + error!() + }; + let Some(value) = module.memory.get_value(index, ty, bytes, signed) else { + error!("failed to read offset {}", index) + }; + value_stack.push(value); } Opcode::MemoryStore { ty: _, bytes } => { - let val = match self.value_stack.pop() { + let val = match value_stack.pop() { Some(Value::I32(x)) => x.into(), Some(Value::I64(x)) => x, Some(Value::F32(x)) => x.to_bits().into(), @@ -1585,53 +2161,50 @@ impl Machine { x, ), }; - let base = match self.value_stack.pop() { + let base = match value_stack.pop() { Some(Value::I32(x)) => x, x => bail!( "WASM validation failed: attempted to memory store with index type {:?}", x, ), }; - if let Some(idx) = inst.argument_data.checked_add(base.into()) { - if !module.memory.store_value(idx, val, bytes) { - error!(); - } - } else { + let Some(idx) = inst.argument_data.checked_add(base.into()) else { + error!() + }; + if !module.memory.store_value(idx, val, bytes) { error!(); } } Opcode::I32Const => { - self.value_stack.push(Value::I32(inst.argument_data as u32)); + value_stack.push(Value::I32(inst.argument_data as u32)); } Opcode::I64Const => { - self.value_stack.push(Value::I64(inst.argument_data)); + value_stack.push(Value::I64(inst.argument_data)); } Opcode::F32Const => { - self.value_stack - .push(Value::F32(f32::from_bits(inst.argument_data as u32))); + value_stack.push(f32::from_bits(inst.argument_data as u32).into()); } Opcode::F64Const => { - self.value_stack - .push(Value::F64(f64::from_bits(inst.argument_data))); + value_stack.push(f64::from_bits(inst.argument_data).into()); } Opcode::I32Eqz => { - let val = self.value_stack.pop().unwrap(); - self.value_stack.push(Value::I32(val.is_i32_zero() as u32)); + let val = value_stack.pop().unwrap(); + value_stack.push(Value::I32(val.is_i32_zero() as u32)); } Opcode::I64Eqz => { - let val = self.value_stack.pop().unwrap(); - self.value_stack.push(Value::I32(val.is_i64_zero() as u32)); + let val = value_stack.pop().unwrap(); + value_stack.push(Value::I32(val.is_i64_zero() as u32)); } Opcode::IRelOp(t, op, signed) => { - let vb = self.value_stack.pop(); - let va = self.value_stack.pop(); + let vb = value_stack.pop(); + let va = value_stack.pop(); match t { IntegerValType::I32 => { if let (Some(Value::I32(a)), Some(Value::I32(b))) = (va, vb) { if signed { - self.value_stack.push(exec_irel_op(a as i32, b as i32, op)); + value_stack.push(exec_irel_op(a as i32, b as i32, op)); } else { - self.value_stack.push(exec_irel_op(a, b, op)); + value_stack.push(exec_irel_op(a, b, op)); } } else { bail!("WASM validation failed: wrong types for i32relop"); @@ -1640,9 +2213,9 @@ impl Machine { IntegerValType::I64 => { if let (Some(Value::I64(a)), Some(Value::I64(b))) = (va, vb) { if signed { - self.value_stack.push(exec_irel_op(a as i64, b as i64, op)); + value_stack.push(exec_irel_op(a as i64, b as i64, op)); } else { - self.value_stack.push(exec_irel_op(a, b, op)); + value_stack.push(exec_irel_op(a, b, op)); } } else { bail!("WASM validation failed: wrong types for i64relop"); @@ -1651,26 +2224,26 @@ impl Machine { } } Opcode::Drop => { - self.value_stack.pop().unwrap(); + value_stack.pop().unwrap(); } Opcode::Select => { - let selector_zero = self.value_stack.pop().unwrap().is_i32_zero(); - let val2 = self.value_stack.pop().unwrap(); - let val1 = self.value_stack.pop().unwrap(); + let selector_zero = value_stack.pop().unwrap().is_i32_zero(); + let val2 = value_stack.pop().unwrap(); + let val1 = value_stack.pop().unwrap(); if selector_zero { - self.value_stack.push(val2); + value_stack.push(val2); } else { - self.value_stack.push(val1); + value_stack.push(val1); } } Opcode::MemorySize => { let pages = u32::try_from(module.memory.size() / Memory::PAGE_SIZE) .expect("Memory pages grew past a u32"); - self.value_stack.push(Value::I32(pages)); + value_stack.push(pages.into()); } Opcode::MemoryGrow => { let old_size = module.memory.size(); - let adding_pages = match self.value_stack.pop() { + let adding_pages = match value_stack.pop() { Some(Value::I32(x)) => x, v => bail!("WASM validation failed: bad value for memory.grow {:?}", v), }; @@ -1690,142 +2263,132 @@ impl Machine { module.memory.resize(usize::try_from(new_size).unwrap()); // Push the old number of pages let old_pages = u32::try_from(old_size / page_size).unwrap(); - self.value_stack.push(Value::I32(old_pages)); + value_stack.push(old_pages.into()); } else { // Push -1 - self.value_stack.push(Value::I32(u32::MAX)); + value_stack.push(u32::MAX.into()); } } Opcode::IUnOp(w, op) => { - let va = self.value_stack.pop(); + let va = value_stack.pop(); match w { IntegerValType::I32 => { - if let Some(Value::I32(a)) = va { - self.value_stack.push(Value::I32(exec_iun_op(a, op))); - } else { + let Some(Value::I32(value)) = va else { bail!("WASM validation failed: wrong types for i32unop"); - } + }; + value_stack.push(exec_iun_op(value, op).into()); } IntegerValType::I64 => { - if let Some(Value::I64(a)) = va { - self.value_stack.push(Value::I64(exec_iun_op(a, op) as u64)); - } else { + let Some(Value::I64(value)) = va else { bail!("WASM validation failed: wrong types for i64unop"); - } + }; + value_stack.push(Value::I64(exec_iun_op(value, op) as u64)); } } } Opcode::IBinOp(w, op) => { - let vb = self.value_stack.pop(); - let va = self.value_stack.pop(); + let vb = value_stack.pop(); + let va = value_stack.pop(); match w { IntegerValType::I32 => { - if let (Some(Value::I32(a)), Some(Value::I32(b))) = (va, vb) { - if op == IBinOpType::DivS - && (a as i32) == i32::MIN - && (b as i32) == -1 - { - error!(); - } - let value = match exec_ibin_op(a, b, op) { - Some(value) => value, - None => error!(), - }; - self.value_stack.push(Value::I32(value)) - } else { - bail!("WASM validation failed: wrong types for i32binop"); + let (Some(Value::I32(a)), Some(Value::I32(b))) = (va, vb) else { + bail!("WASM validation failed: wrong types for i32binop") + }; + if op == IBinOpType::DivS && (a as i32) == i32::MIN && (b as i32) == -1 + { + error!() } + let Some(value) = exec_ibin_op(a, b, op) else { + error!() + }; + value_stack.push(value.into()); } IntegerValType::I64 => { - if let (Some(Value::I64(a)), Some(Value::I64(b))) = (va, vb) { - if op == IBinOpType::DivS - && (a as i64) == i64::MIN - && (b as i64) == -1 - { - error!(); - } - let value = match exec_ibin_op(a, b, op) { - Some(value) => value, - None => error!(), - }; - self.value_stack.push(Value::I64(value)) - } else { - bail!("WASM validation failed: wrong types for i64binop"); + let (Some(Value::I64(a)), Some(Value::I64(b))) = (va, vb) else { + bail!("WASM validation failed: wrong types for i64binop") + }; + if op == IBinOpType::DivS && (a as i64) == i64::MIN && (b as i64) == -1 + { + error!(); } + let Some(value) = exec_ibin_op(a, b, op) else { + error!() + }; + value_stack.push(value.into()); } } } Opcode::I32WrapI64 => { - let x = match self.value_stack.pop() { + let x = match value_stack.pop() { Some(Value::I64(x)) => x, v => bail!( "WASM validation failed: wrong type for i32.wrapi64: {:?}", v, ), }; - self.value_stack.push(Value::I32(x as u32)); + value_stack.push(Value::I32(x as u32)); } Opcode::I64ExtendI32(signed) => { - let x: u32 = self.value_stack.pop().unwrap().assume_u32(); + let x: u32 = value_stack.pop().unwrap().assume_u32(); let x64 = match signed { true => x as i32 as i64 as u64, false => x as u64, }; - self.value_stack.push(Value::I64(x64)); + value_stack.push(x64.into()); } Opcode::Reinterpret(dest, source) => { - let val = match self.value_stack.pop() { + let val = match value_stack.pop() { Some(Value::I32(x)) if source == ArbValueType::I32 => { assert_eq!(dest, ArbValueType::F32, "Unsupported reinterpret"); - Value::F32(f32::from_bits(x)) + f32::from_bits(x).into() } Some(Value::I64(x)) if source == ArbValueType::I64 => { assert_eq!(dest, ArbValueType::F64, "Unsupported reinterpret"); - Value::F64(f64::from_bits(x)) + f64::from_bits(x).into() } Some(Value::F32(x)) if source == ArbValueType::F32 => { assert_eq!(dest, ArbValueType::I32, "Unsupported reinterpret"); - Value::I32(x.to_bits()) + x.to_bits().into() } Some(Value::F64(x)) if source == ArbValueType::F64 => { assert_eq!(dest, ArbValueType::I64, "Unsupported reinterpret"); - Value::I64(x.to_bits()) + x.to_bits().into() } v => bail!("bad reinterpret: val {:?} source {:?}", v, source), }; - self.value_stack.push(val); + value_stack.push(val); } Opcode::I32ExtendS(b) => { - let mut x = self.value_stack.pop().unwrap().assume_u32(); + let mut x = value_stack.pop().unwrap().assume_u32(); let mask = (1u32 << b) - 1; x &= mask; if x & (1 << (b - 1)) != 0 { x |= !mask; } - self.value_stack.push(Value::I32(x)); + value_stack.push(x.into()); } Opcode::I64ExtendS(b) => { - let mut x = self.value_stack.pop().unwrap().assume_u64(); + let mut x = value_stack.pop().unwrap().assume_u64(); let mask = (1u64 << b) - 1; x &= mask; if x & (1 << (b - 1)) != 0 { x |= !mask; } - self.value_stack.push(Value::I64(x)); + value_stack.push(x.into()); } Opcode::MoveFromStackToInternal => { - self.internal_stack.push(self.value_stack.pop().unwrap()); + self.internal_stack.push(value_stack.pop().unwrap()); } Opcode::MoveFromInternalToStack => { - self.value_stack.push(self.internal_stack.pop().unwrap()); + value_stack.push(self.internal_stack.pop().unwrap()); } Opcode::Dup => { - let val = self.value_stack.last().cloned().unwrap(); - self.value_stack.push(val); + let val = value_stack.last().cloned().unwrap(); + value_stack.push(val); } Opcode::GetGlobalStateBytes32 => { - let ptr = self.value_stack.pop().unwrap().assume_u32(); - let idx = self.value_stack.pop().unwrap().assume_u32() as usize; + let ptr = value_stack.pop().unwrap().assume_u32(); + let idx = value_stack.pop().unwrap().assume_u32() as usize; if idx >= self.global_state.bytes32_vals.len() || !module .memory @@ -1835,8 +2398,8 @@ impl Machine { } } Opcode::SetGlobalStateBytes32 => { - let ptr = self.value_stack.pop().unwrap().assume_u32(); - let idx = self.value_stack.pop().unwrap().assume_u32() as usize; + let ptr = value_stack.pop().unwrap().assume_u32(); + let idx = value_stack.pop().unwrap().assume_u32() as usize; if idx >= self.global_state.bytes32_vals.len() { error!(); } else if let Some(hash) = module.memory.load_32_byte_aligned(ptr.into()) { @@ -1846,17 +2409,16 @@ impl Machine { } } Opcode::GetGlobalStateU64 => { - let idx = self.value_stack.pop().unwrap().assume_u32() as usize; + let idx = value_stack.pop().unwrap().assume_u32() as usize; if idx >= self.global_state.u64_vals.len() { error!(); } else { - self.value_stack - .push(Value::I64(self.global_state.u64_vals[idx])); + value_stack.push(self.global_state.u64_vals[idx].into()); } } Opcode::SetGlobalStateU64 => { - let val = self.value_stack.pop().unwrap().assume_u64(); - let idx = self.value_stack.pop().unwrap().assume_u32() as usize; + let val = value_stack.pop().unwrap().assume_u64(); + let idx = value_stack.pop().unwrap().assume_u32() as usize; if idx >= self.global_state.u64_vals.len() { error!(); } else { @@ -1864,50 +2426,49 @@ impl Machine { } } Opcode::ReadPreImage => { - let offset = self.value_stack.pop().unwrap().assume_u32(); - let ptr = self.value_stack.pop().unwrap().assume_u32(); + let offset = value_stack.pop().unwrap().assume_u32(); + let ptr = value_stack.pop().unwrap().assume_u32(); let preimage_ty = PreimageType::try_from(u8::try_from(inst.argument_data)?)?; // Preimage reads must be word aligned if offset % 32 != 0 { error!(); } - if let Some(hash) = module.memory.load_32_byte_aligned(ptr.into()) { - if let Some(preimage) = - self.preimage_resolver.get(self.context, preimage_ty, hash) - { - if preimage_ty == PreimageType::EthVersionedHash - && preimage.len() != BYTES_PER_BLOB - { - bail!( - "kzg hash {} preimage should be {} bytes long but is instead {}", - hash, - BYTES_PER_BLOB, - preimage.len(), - ); - } - let offset = usize::try_from(offset).unwrap(); - let len = std::cmp::min(32, preimage.len().saturating_sub(offset)); - let read = preimage.get(offset..(offset + len)).unwrap_or_default(); - let success = module.memory.store_slice_aligned(ptr.into(), read); - assert!(success, "Failed to write to previously read memory"); - self.value_stack.push(Value::I32(len as u32)); - } else { - eprintln!( - "{} for hash {}", - "Missing requested preimage".red(), - hash.red(), - ); - self.eprint_backtrace(); - bail!("missing requested preimage for hash {}", hash); - } - } else { + + let Some(hash) = module.memory.load_32_byte_aligned(ptr.into()) else { error!(); + }; + let Some(preimage) = + self.preimage_resolver.get(self.context, preimage_ty, hash) + else { + eprintln!( + "{} for hash {}", + "Missing requested preimage".red(), + hash.red(), + ); + self.print_backtrace(true); + bail!("missing requested preimage for hash {}", hash); + }; + if preimage_ty == PreimageType::EthVersionedHash + && preimage.len() != BYTES_PER_BLOB + { + bail!( + "kzg hash {} preimage should be {} bytes long but is instead {}", + hash, + BYTES_PER_BLOB, + preimage.len(), + ); } + let offset = usize::try_from(offset).unwrap(); + let len = std::cmp::min(32, preimage.len().saturating_sub(offset)); + let read = preimage.get(offset..(offset + len)).unwrap_or_default(); + let success = module.memory.store_slice_aligned(ptr.into(), read); + assert!(success, "Failed to write to previously read memory"); + value_stack.push(Value::I32(len as u32)); } Opcode::ReadInboxMessage => { - let offset = self.value_stack.pop().unwrap().assume_u32(); - let ptr = self.value_stack.pop().unwrap().assume_u32(); - let msg_num = self.value_stack.pop().unwrap().assume_u64(); + let offset = value_stack.pop().unwrap().assume_u32(); + let ptr = value_stack.pop().unwrap().assume_u32(); + let msg_num = value_stack.pop().unwrap().assume_u64(); let inbox_identifier = argument_data_to_inbox(inst.argument_data).expect("Bad inbox indentifier"); if let Some(message) = self.inbox_contents.get(&(inbox_identifier, msg_num)) { @@ -1918,7 +2479,7 @@ impl Machine { let len = std::cmp::min(32, message.len().saturating_sub(offset)); let read = message.get(offset..(offset + len)).unwrap_or_default(); if module.memory.store_slice_aligned(ptr.into(), read) { - self.value_stack.push(Value::I32(len as u32)); + value_stack.push(Value::I32(len as u32)); } else { error!(); } @@ -1927,7 +2488,7 @@ impl Machine { let delayed = inbox_identifier == InboxIdentifier::Delayed; if msg_num < self.first_too_far || delayed { eprintln!("{} {msg_num}", "Missing inbox message".red()); - self.eprint_backtrace(); + self.print_backtrace(true); bail!( "missing inbox message {msg_num} of {}", self.first_too_far - 1 @@ -1937,25 +2498,80 @@ impl Machine { break; } } + Opcode::LinkModule => { + let ptr = value_stack.pop().unwrap().assume_u32(); + let Some(hash) = module.memory.load_32_byte_aligned(ptr.into()) else { + error!("no hash for {}", ptr) + }; + let Some(bytes) = self.stylus_modules.get(&hash) else { + let modules = &self.stylus_modules; + let keys: Vec<_> = modules.keys().take(16).map(hex::encode).collect(); + let dots = (modules.len() > 16).then_some("...").unwrap_or_default(); + bail!("no program for {hash} in {{{}{dots}}}", keys.join(", ")) + }; + flush_module!(); + + // put the new module's offset on the stack + let index = self.modules.len() as u32; + value_stack.push(index.into()); + + self.modules.push(unsafe { Module::from_bytes(bytes) }); + if let Some(cached) = &mut self.modules_merkle { + cached.push_leaf(hash); + } + reset_refs!(); + } + Opcode::UnlinkModule => { + flush_module!(); + self.modules.pop(); + if let Some(cached) = &mut self.modules_merkle { + cached.pop_leaf(); + } + reset_refs!(); + } Opcode::HaltAndSetFinished => { self.status = MachineStatus::Finished; break; } + Opcode::NewCoThread => { + if self.thread_state.is_cothread() { + error!("called NewCoThread from cothread") + } + self.value_stacks.push(Vec::new()); + self.frame_stacks.push(Vec::new()); + reset_refs!(); + } + Opcode::PopCoThread => { + if self.thread_state.is_cothread() { + error!("called PopCoThread from cothread") + } + self.value_stacks.pop(); + self.frame_stacks.pop(); + reset_refs!(); + } + Opcode::SwitchThread => { + let next_recovery = match inst.argument_data { + 0 => ThreadState::Main, + x => ThreadState::CoThread(self.pc.add((x - 1).try_into().unwrap())), + }; + if next_recovery.is_cothread() == self.thread_state.is_cothread() { + error!("SwitchThread doesn't switch") + } + self.thread_state = next_recovery; + reset_refs!(); + } } } flush_module!(); if self.is_halted() && !self.stdio_output.is_empty() { // If we halted, print out any trailing output that didn't have a newline. - println!( - "{} {}", - "WASM says:".yellow(), - String::from_utf8_lossy(&self.stdio_output), - ); + Self::say(String::from_utf8_lossy(&self.stdio_output)); self.stdio_output.clear(); } Ok(()) } + #[cfg(feature = "native")] fn host_call_hook( value_stack: &[Value], module: &Module, @@ -2019,10 +2635,7 @@ impl Machine { stdio_output.extend_from_slice(read_bytes_segment!(data_ptr, data_size)); } while let Some(mut idx) = stdio_output.iter().position(|&c| c == b'\n') { - println!( - "\x1b[33mWASM says:\x1b[0m {}", - String::from_utf8_lossy(&stdio_output[..idx]), - ); + Self::say(String::from_utf8_lossy(&stdio_output[..idx])); if stdio_output.get(idx + 1) == Some(&b'\r') { idx += 1; } @@ -2030,10 +2643,40 @@ impl Machine { } Ok(()) } + ("console", "log_i32" | "log_i64" | "log_f32" | "log_f64") + | ("console", "tee_i32" | "tee_i64" | "tee_f32" | "tee_f64") => { + let value = value_stack.last().ok_or_else(|| eyre!("missing value"))?; + Self::say(value); + Ok(()) + } + ("console", "log_txt") => { + let ptr = pull_arg!(1, I32); + let len = pull_arg!(0, I32); + let text = read_bytes_segment!(ptr, len); + match std::str::from_utf8(text) { + Ok(text) => Self::say(text), + Err(_) => Self::say(hex::encode(text)), + } + Ok(()) + } _ => Ok(()), } } + pub fn say(text: D) { + println!("{} {text}", "WASM says:".yellow()); + } + + pub fn print_modules(&self) { + for module in &self.modules { + println!("{module}\n"); + } + for module in self.stylus_modules.values() { + let module = unsafe { Module::from_bytes(module) }; + println!("{module}\n"); + } + } + pub fn is_halted(&self) -> bool { self.status != MachineStatus::Running } @@ -2057,18 +2700,87 @@ impl Machine { self.get_modules_merkle().root() } + fn stack_hashes(&self) -> (FrameStackHash, ValueStackHash, InterStackHash) { + macro_rules! compute { + ($stack:expr, $prefix:expr) => {{ + let frames = $stack.iter().map(|v| v.hash()); + hash_stack(frames, concat!($prefix, " stack:")) + }}; + } + // compute_multistack returns the hash of multistacks as follows: + // Keccak( + // "multistack:" + // + hash_stack(first_stack) + // + hash_stack(last_stack) + // + Keccak("cothread:" + 2nd_stack+Keccak("cothread:" + 3drd_stack + ...) + // ) + macro_rules! compute_multistack { + ($field:expr, $stacks:expr, $prefix:expr, $hasher: expr) => {{ + let first_elem = *$stacks.first().unwrap(); + let first_hash = hash_stack( + first_elem.iter().map(|v| v.hash()), + concat!($prefix, " stack:"), + ); + + let last_hash = if $stacks.len() <= 1 { + Machine::NO_STACK_HASH + } else { + let last_elem = *$stacks.last().unwrap(); + hash_stack( + last_elem.iter().map(|v| v.hash()), + concat!($prefix, " stack:"), + ) + }; + + // Hash of stacks [2nd..last) or 0xfff...f if len <= 2. + let mut hash = if $stacks.len() <= 2 { + Bytes32::default() + } else { + hash_multistack(&$stacks[1..$stacks.len() - 1], $hasher) + }; + + hash = Keccak256::new() + .chain("multistack:") + .chain(first_hash) + .chain(last_hash) + .chain(hash) + .finalize() + .into(); + hash + }}; + } + let frame_stacks = compute_multistack!( + |x| x.frame_stack, + self.get_frame_stacks(), + "Stack frame", + hash_stack_frame_stack + ); + let value_stacks = compute_multistack!( + |x| x.value_stack, + self.get_data_stacks(), + "Value", + hash_value_stack + ); + let inter_stack = compute!(self.internal_stack, "Value"); + + (frame_stacks, value_stacks, inter_stack) + } + pub fn hash(&self) -> Bytes32 { let mut h = Keccak256::new(); match self.status { MachineStatus::Running => { + let (frame_stacks, value_stacks, inter_stack) = self.stack_hashes(); + h.update(b"Machine running:"); - h.update(hash_value_stack(&self.value_stack)); - h.update(hash_value_stack(&self.internal_stack)); - h.update(hash_stack_frame_stack(&self.frame_stack)); + h.update(value_stacks); + h.update(inter_stack); + h.update(frame_stacks); h.update(self.global_state.hash()); h.update(self.pc.module.to_be_bytes()); h.update(self.pc.func.to_be_bytes()); h.update(self.pc.inst.to_be_bytes()); + h.update(self.thread_state.serialize()); h.update(self.get_modules_root()); } MachineStatus::Finished => { @@ -2085,53 +2797,74 @@ impl Machine { h.finalize().into() } + #[cfg(feature = "native")] pub fn serialize_proof(&self) -> Vec { // Could be variable, but not worth it yet const STACK_PROVING_DEPTH: usize = 3; let mut data = vec![self.status as u8]; - data.extend(prove_stack( - &self.value_stack, - STACK_PROVING_DEPTH, + macro_rules! out { + ($bytes:expr) => { + data.extend($bytes); + }; + } + macro_rules! fail { + ($format:expr $(,$message:expr)*) => {{ + let text = format!($format, $($message.red()),*); + panic!("WASM validation failed: {text}"); + }}; + } + out!(prove_multistack( + self.thread_state.is_cothread(), + self.get_data_stacks(), hash_value_stack, - |v| v.serialize_for_proof(), + hash_multistack, + |stack| prove_stack(stack, STACK_PROVING_DEPTH, hash_value_stack, |v| v + .serialize_for_proof()), )); - data.extend(prove_stack( + out!(prove_stack( &self.internal_stack, 1, hash_value_stack, |v| v.serialize_for_proof(), )); - data.extend(prove_window( - &self.frame_stack, + out!(prove_multistack( + self.thread_state.is_cothread(), + self.get_frame_stacks(), hash_stack_frame_stack, - StackFrame::serialize_for_proof, + hash_multistack, + |stack| prove_window( + stack, + hash_stack_frame_stack, + StackFrame::serialize_for_proof + ), )); - data.extend(self.global_state.hash()); + out!(self.global_state.hash()); + + out!(self.pc.module.to_be_bytes()); + out!(self.pc.func.to_be_bytes()); + out!(self.pc.inst.to_be_bytes()); + + out!(self.thread_state.serialize()); - data.extend(self.pc.module.to_be_bytes()); - data.extend(self.pc.func.to_be_bytes()); - data.extend(self.pc.inst.to_be_bytes()); let mod_merkle = self.get_modules_merkle(); - data.extend(mod_merkle.root()); + out!(mod_merkle.root()); // End machine serialization, serialize module let module = &self.modules[self.pc.module()]; let mem_merkle = module.memory.merkelize(); - data.extend(module.serialize_for_proof(&mem_merkle)); + out!(module.serialize_for_proof(&mem_merkle)); // Prove module is in modules merkle tree - data.extend( - mod_merkle - .prove(self.pc.module()) - .expect("Failed to prove module"), - ); + out!(mod_merkle + .prove(self.pc.module()) + .expect("Failed to prove module")); if self.is_halted() { return data; @@ -2140,59 +2873,49 @@ impl Machine { // Begin next instruction proof let func = &module.funcs[self.pc.func()]; - data.extend(func.code[self.pc.inst()].serialize_for_proof()); - data.extend( - func.code_merkle - .prove(self.pc.inst()) - .expect("Failed to prove against code merkle"), - ); - data.extend( - module - .funcs_merkle - .prove(self.pc.func()) - .expect("Failed to prove against function merkle"), - ); + out!(func.serialize_body_for_proof(self.pc)); + out!(func + .code_merkle + .prove(self.pc.inst() / Function::CHUNK_SIZE) + .expect("Failed to prove against code merkle")); + out!(module + .funcs_merkle + .prove(self.pc.func()) + .expect("Failed to prove against function merkle")); // End next instruction proof, begin instruction specific serialization - if let Some(next_inst) = func.code.get(self.pc.inst()) { - if matches!( - next_inst.opcode, - Opcode::GetGlobalStateBytes32 - | Opcode::SetGlobalStateBytes32 - | Opcode::GetGlobalStateU64 - | Opcode::SetGlobalStateU64 - ) { - data.extend(self.global_state.serialize()); + let Some(next_inst) = func.code.get(self.pc.inst()) else { + return data; + }; + + let op = next_inst.opcode; + let arg = next_inst.argument_data; + let value_stack = self.get_data_stack(); + let frame_stack = self.get_frame_stack(); + + use Opcode::*; + match op { + GetGlobalStateU64 | SetGlobalStateU64 => { + out!(self.global_state.serialize()); } - if matches!(next_inst.opcode, Opcode::LocalGet | Opcode::LocalSet) { - let locals = &self.frame_stack.last().unwrap().locals; - let idx = next_inst.argument_data as usize; - data.extend(locals[idx].serialize_for_proof()); - let locals_merkle = + LocalGet | LocalSet => { + let locals = &frame_stack.last().unwrap().locals; + let idx = arg as usize; + out!(locals[idx].serialize_for_proof()); + let merkle = Merkle::new(MerkleType::Value, locals.iter().map(|v| v.hash()).collect()); - data.extend( - locals_merkle - .prove(idx) - .expect("Out of bounds local access"), - ); - } else if matches!(next_inst.opcode, Opcode::GlobalGet | Opcode::GlobalSet) { - let idx = next_inst.argument_data as usize; - data.extend(module.globals[idx].serialize_for_proof()); - let locals_merkle = Merkle::new( - MerkleType::Value, - module.globals.iter().map(|v| v.hash()).collect(), - ); - data.extend( - locals_merkle - .prove(idx) - .expect("Out of bounds global access"), - ); - } else if matches!( - next_inst.opcode, - Opcode::MemoryLoad { .. } | Opcode::MemoryStore { .. }, - ) { - let is_store = matches!(next_inst.opcode, Opcode::MemoryStore { .. }); + out!(merkle.prove(idx).expect("Out of bounds local access")); + } + GlobalGet | GlobalSet => { + let idx = arg as usize; + out!(module.globals[idx].serialize_for_proof()); + let globals_merkle = module.globals.iter().map(|v| v.hash()).collect(); + let merkle = Merkle::new(MerkleType::Value, globals_merkle); + out!(merkle.prove(idx).expect("Out of bounds global access")); + } + MemoryLoad { .. } | MemoryStore { .. } => { + let is_store = matches!(op, MemoryStore { .. }); // this isn't really a bool -> int, it's determining an offset based on a bool #[allow(clippy::bool_to_int_with_if)] let stack_idx_offset = if is_store { @@ -2201,24 +2924,21 @@ impl Machine { } else { 0 }; - let base = match self - .value_stack - .get(self.value_stack.len() - 1 - stack_idx_offset) - { + let base = match value_stack.get(value_stack.len() - 1 - stack_idx_offset) { Some(Value::I32(x)) => *x, - x => panic!("WASM validation failed: memory index type is {:?}", x), + x => fail!("memory index type is {x:?}"), }; if let Some(mut idx) = u64::from(base) - .checked_add(next_inst.argument_data) + .checked_add(arg) .and_then(|x| usize::try_from(x).ok()) { // Prove the leaf this index is in, and the next one, if they are within the memory's size. idx /= Memory::LEAF_SIZE; - data.extend(module.memory.get_leaf_data(idx)); - data.extend(mem_merkle.prove(idx).unwrap_or_default()); + out!(module.memory.get_leaf_data(idx)); + out!(mem_merkle.prove(idx).unwrap_or_default()); // Now prove the next leaf too, in case it's accessed. let next_leaf_idx = idx.saturating_add(1); - data.extend(module.memory.get_leaf_data(next_leaf_idx)); + out!(module.memory.get_leaf_data(next_leaf_idx)); let second_mem_merkle = if is_store { // For stores, prove the second merkle against a state after the first leaf is set. // This state also happens to have the second leaf set, but that's irrelevant. @@ -2232,86 +2952,77 @@ impl Machine { } else { mem_merkle.into_owned() }; - data.extend(second_mem_merkle.prove(next_leaf_idx).unwrap_or_default()); + out!(second_mem_merkle.prove(next_leaf_idx).unwrap_or_default()); } - } else if next_inst.opcode == Opcode::CallIndirect { - let (table, ty) = crate::wavm::unpack_call_indirect(next_inst.argument_data); - let idx = match self.value_stack.last() { + } + CallIndirect => { + let (table, ty) = crate::wavm::unpack_call_indirect(arg); + let idx = match value_stack.last() { Some(Value::I32(i)) => *i, - x => panic!( - "WASM validation failed: top of stack before call_indirect is {:?}", - x, - ), + x => fail!("top of stack before call_indirect is {x:?}"), }; let ty = &module.types[usize::try_from(ty).unwrap()]; - data.extend((table as u64).to_be_bytes()); - data.extend(ty.hash()); + out!((table as u64).to_be_bytes()); + out!(ty.hash()); let table_usize = usize::try_from(table).unwrap(); let table = &module.tables[table_usize]; - data.extend( - table - .serialize_for_proof() - .expect("failed to serialize table"), - ); - data.extend( - module - .tables_merkle - .prove(table_usize) - .expect("Failed to prove tables merkle"), - ); + out!(table + .serialize_for_proof() + .expect("failed to serialize table")); + out!(module + .tables_merkle + .prove(table_usize) + .expect("Failed to prove tables merkle")); let idx_usize = usize::try_from(idx).unwrap(); if let Some(elem) = table.elems.get(idx_usize) { - data.extend(elem.func_ty.hash()); - data.extend(elem.val.serialize_for_proof()); - data.extend( - table - .elems_merkle - .prove(idx_usize) - .expect("Failed to prove elements merkle"), - ); + out!(elem.func_ty.hash()); + out!(elem.val.serialize_for_proof()); + out!(table + .elems_merkle + .prove(idx_usize) + .expect("Failed to prove elements merkle")); } - } else if matches!( - next_inst.opcode, - Opcode::GetGlobalStateBytes32 | Opcode::SetGlobalStateBytes32, - ) { - let ptr = self.value_stack.last().unwrap().assume_u32(); + } + CrossModuleInternalCall => { + let module_idx = value_stack.last().unwrap().assume_u32() as usize; + let called_module = &self.modules[module_idx]; + out!(called_module.serialize_for_proof(&called_module.memory.merkelize())); + out!(mod_merkle + .prove(module_idx) + .expect("Failed to prove module for CrossModuleInternalCall")); + } + GetGlobalStateBytes32 | SetGlobalStateBytes32 => { + out!(self.global_state.serialize()); + let ptr = value_stack.last().unwrap().assume_u32(); if let Some(mut idx) = usize::try_from(ptr).ok().filter(|x| x % 32 == 0) { // Prove the leaf this index is in idx /= Memory::LEAF_SIZE; - data.extend(module.memory.get_leaf_data(idx)); - data.extend(mem_merkle.prove(idx).unwrap_or_default()); + out!(module.memory.get_leaf_data(idx)); + out!(mem_merkle.prove(idx).unwrap_or_default()); } - } else if matches!( - next_inst.opcode, - Opcode::ReadPreImage | Opcode::ReadInboxMessage, - ) { - let offset = self.value_stack.last().unwrap().assume_u32(); - let ptr = self - .value_stack - .get(self.value_stack.len() - 2) - .unwrap() - .assume_u32(); + } + ReadPreImage | ReadInboxMessage => { + let offset = value_stack.last().unwrap().assume_u32(); + let ptr = value_stack.get(value_stack.len() - 2).unwrap().assume_u32(); if let Some(mut idx) = usize::try_from(ptr).ok().filter(|x| x % 32 == 0) { // Prove the leaf this index is in idx /= Memory::LEAF_SIZE; let prev_data = module.memory.get_leaf_data(idx); - data.extend(prev_data); - data.extend(mem_merkle.prove(idx).unwrap_or_default()); - if next_inst.opcode == Opcode::ReadPreImage { + out!(prev_data); + out!(mem_merkle.prove(idx).unwrap_or_default()); + if op == Opcode::ReadPreImage { let hash = Bytes32(prev_data); let preimage_ty = PreimageType::try_from( u8::try_from(next_inst.argument_data) .expect("ReadPreImage argument data is out of range for a u8"), ) .expect("Invalid preimage type in ReadPreImage argument data"); - let preimage = - match self - .preimage_resolver + let Some(preimage) = + self.preimage_resolver .get_const(self.context, preimage_ty, hash) - { - Some(b) => b, - None => panic!("Missing requested preimage for hash {}", hash), - }; + else { + panic!("Missing requested preimage for hash {}", hash) + }; data.push(0); // preimage proof type match preimage_ty { PreimageType::Keccak256 | PreimageType::Sha2_256 => { @@ -2324,31 +3035,96 @@ impl Machine { } } } else if next_inst.opcode == Opcode::ReadInboxMessage { - let msg_idx = self - .value_stack - .get(self.value_stack.len() - 3) - .unwrap() - .assume_u64(); - let inbox_identifier = argument_data_to_inbox(next_inst.argument_data) - .expect("Bad inbox indentifier"); + let msg_idx = value_stack.get(value_stack.len() - 3).unwrap().assume_u64(); + let inbox_identifier = + argument_data_to_inbox(arg).expect("Bad inbox indentifier"); if let Some(msg_data) = self.inbox_contents.get(&(inbox_identifier, msg_idx)) { data.push(0); // inbox proof type - data.extend(msg_data); + out!(msg_data); } } else { - panic!("Should never ever get here"); + unreachable!() } } } - } + LinkModule | UnlinkModule => { + if op == LinkModule { + let leaf_index = match value_stack.last() { + Some(Value::I32(x)) => *x as usize / Memory::LEAF_SIZE, + x => fail!("module pointer has invalid type {x:?}"), + }; + out!(module.memory.get_leaf_data(leaf_index)); + out!(mem_merkle.prove(leaf_index).unwrap_or_default()); + } + + // prove that our proposed leaf x has a leaf-like hash + let module = self.modules.last().unwrap(); + out!(module.serialize_for_proof(&module.memory.merkelize())); + // prove that leaf x is under the root at position p + let leaf = self.modules.len() - 1; + out!((leaf as u32).to_be_bytes()); + out!(mod_merkle.prove(leaf).unwrap()); + + // if needed, prove that x is the last module by proving that leaf p + 1 is 0 + let balanced = math::is_power_of_2(leaf + 1); + if !balanced { + out!(mod_merkle.prove_any(leaf + 1)); + } + } + PopCoThread => { + macro_rules! prove_pop { + ($multistack:expr, $hasher:expr) => { + let len = $multistack.len(); + if (len > 2) { + out!($hasher($multistack[len - 2])); + } else { + out!(Machine::NO_STACK_HASH); + } + if (len > 3) { + out!(hash_multistack(&$multistack[1..len - 2], $hasher)); + } else { + out!(Bytes32::default()); + } + }; + } + prove_pop!(self.get_data_stacks(), hash_value_stack); + prove_pop!(self.get_frame_stacks(), hash_stack_frame_stack); + } + _ => {} + } data } pub fn get_data_stack(&self) -> &[Value] { - &self.value_stack + match self.thread_state { + ThreadState::Main => &self.value_stacks[0], + ThreadState::CoThread(_) => self.value_stacks.last().unwrap(), + } + } + + pub fn get_data_stacks(&self) -> Vec<&[Value]> { + self.value_stacks.iter().map(|v| v.as_slice()).collect() + } + + fn get_frame_stack(&self) -> &[StackFrame] { + match self.thread_state { + ThreadState::Main => &self.frame_stacks[0], + ThreadState::CoThread(_) => self.frame_stacks.last().unwrap(), + } + } + + fn get_frame_stacks(&self) -> Vec<&[StackFrame]> { + self.frame_stacks + .iter() + .map(|v: &Vec<_>| v.as_slice()) + .collect() + } + + pub fn get_internals_stack(&self) -> &[Value] { + &self.internal_stack } pub fn get_global_state(&self) -> GlobalState { @@ -2378,35 +3154,43 @@ impl Machine { self.modules.get(module).map(|m| &*m.names) } - pub fn get_backtrace(&self) -> Vec<(String, String, usize)> { - let mut res = Vec::new(); - let mut push_pc = |pc: ProgramCounter| { + pub fn print_backtrace(&self, stderr: bool) { + let print = |line: String| match stderr { + true => println!("{}", line), + false => eprintln!("{}", line), + }; + + let print_pc = |pc: ProgramCounter| { let names = &self.modules[pc.module()].names; let func = names .functions .get(&pc.func) .cloned() - .unwrap_or_else(|| format!("{}", pc.func)); - let mut module = names.module.clone(); - if module.is_empty() { - module = format!("{}", pc.module); - } - res.push((module, func, pc.inst())); + .unwrap_or_else(|| pc.func.to_string()); + let func = rustc_demangle::demangle(&func); + let module = match names.module.is_empty() { + true => pc.module.to_string(), + false => names.module.clone(), + }; + let inst = format!("#{}", pc.inst); + print(format!( + " {} {} {} {}", + module.grey(), + func.mint(), + "inst".grey(), + inst.blue(), + )); }; - push_pc(self.pc); - for frame in self.frame_stack.iter().rev() { + + print_pc(self.pc); + let frame_stack = self.get_frame_stack(); + for frame in frame_stack.iter().rev().take(25) { if let Value::InternalRef(pc) = frame.return_ref { - push_pc(pc); + print_pc(pc); } } - res - } - - pub fn eprint_backtrace(&self) { - eprintln!("Backtrace:"); - for (module, func, pc) in self.get_backtrace() { - let func = rustc_demangle::demangle(&func); - eprintln!(" {} {} @ {}", module, func.mint(), pc.blue()); + if frame_stack.len() > 25 { + print(format!(" ... and {} more", frame_stack.len() - 25).grey()); } } } diff --git a/arbitrator/prover/src/main.rs b/arbitrator/prover/src/main.rs index 089111da1d..9ddd5020c8 100644 --- a/arbitrator/prover/src/main.rs +++ b/arbitrator/prover/src/main.rs @@ -1,12 +1,14 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE +// Copyright 2021-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE -use arbutil::{format, Color, DebugColor, PreimageType}; -use eyre::{Context, Result}; +#![cfg(feature = "native")] + +use arbutil::{format, Bytes32, Color, DebugColor, PreimageType}; +use eyre::{eyre, Context, Result}; use fnv::{FnvHashMap as HashMap, FnvHashSet as HashSet}; use prover::{ machine::{GlobalState, InboxIdentifier, Machine, MachineStatus, PreimageResolver, ProofInfo}, - utils::{hash_preimage, Bytes32, CBytes}, + utils::{file_bytes, hash_preimage, CBytes}, wavm::Opcode, }; use std::sync::Arc; @@ -34,6 +36,11 @@ struct Opts { inbox_add_stub_headers: bool, #[structopt(long)] always_merkleize: bool, + #[structopt(long)] + debug_funcs: bool, + #[structopt(long)] + /// print modules to the console + print_modules: bool, /// profile output instead of generting proofs #[structopt(short = "p", long)] profile_run: bool, @@ -66,6 +73,8 @@ struct Opts { delayed_inbox: Vec, #[structopt(long)] preimages: Option, + #[structopt(long)] + stylus_modules: Vec, /// Require that the machine end in the Finished state #[structopt(long)] require_success: bool, @@ -109,6 +118,7 @@ struct SimpleProfile { const INBOX_HEADER_LEN: usize = 40; // also in test-case's host-io.rs & contracts's OneStepProverHostIo.sol const DELAYED_HEADER_LEN: usize = 112; // also in test-case's host-io.rs & contracts's OneStepProverHostIo.sol +#[cfg(feature = "native")] fn main() -> Result<()> { let opts = Opts::from_args(); @@ -184,10 +194,25 @@ fn main() -> Result<()> { true, opts.always_merkleize, opts.allow_hostapi, + opts.debug_funcs, + true, global_state, inbox_contents, preimage_resolver, )?; + + for path in &opts.stylus_modules { + let err = || eyre!("failed to read module at {}", path.to_string_lossy().red()); + let wasm = file_bytes(path).wrap_err_with(err)?; + let codehash = &Bytes32::default(); + mach.add_program(&wasm, codehash, 1, true) + .wrap_err_with(err)?; + } + + if opts.print_modules { + mach.print_modules(); + } + if let Some(output_path) = opts.generate_binaries { let mut module_root_file = File::create(output_path.join("module-root.txt"))?; writeln!(module_root_file, "0x{}", mach.get_modules_root())?; @@ -238,7 +263,10 @@ fn main() -> Result<()> { if opts.proving_backoff { let mut extra_data = 0; - if matches!(next_opcode, Opcode::ReadInboxMessage | Opcode::ReadPreImage) { + if matches!( + next_opcode, + Opcode::ReadInboxMessage | Opcode::ReadPreImage | Opcode::SwitchThread + ) { extra_data = next_inst.argument_data; } let count_entry = proving_backoff @@ -319,9 +347,13 @@ fn main() -> Result<()> { } } else { let values = mach.get_data_stack(); + let inters = mach.get_internals_stack(); if !values.is_empty() { println!("{} {}", "Machine stack".grey(), format::commas(values)); } + if !inters.is_empty() { + println!("{} {}", "Internals ".grey(), format::commas(inters)); + } print!( "Generating proof {} (inst {}) for {}{}", proofs.len().blue(), @@ -374,10 +406,7 @@ fn main() -> Result<()> { println!("End machine hash: {}", mach.hash()); println!("End machine stack: {:?}", mach.get_data_stack()); println!("End machine backtrace:"); - for (module, func, pc) in mach.get_backtrace() { - let func = rustc_demangle::demangle(&func); - println!(" {} {} @ {}", module, func.mint(), pc.blue()); - } + mach.print_backtrace(false); if let Some(out) = opts.output { let out = File::create(out)?; @@ -425,14 +454,11 @@ fn main() -> Result<()> { let opts_binary = opts.binary; let opts_libraries = opts.libraries; let format_pc = |module_num: usize, func_num: usize| -> (String, String) { - let names = match mach.get_module_names(module_num) { - Some(n) => n, - None => { - return ( - format!("[unknown {}]", module_num), - format!("[unknown {}]", func_num), - ); - } + let Some(names) = mach.get_module_names(module_num) else { + return ( + format!("[unknown {}]", module_num), + format!("[unknown {}]", func_num), + ); }; let module_name = if module_num == 0 { names.module.clone() @@ -503,6 +529,5 @@ fn main() -> Result<()> { eprintln!("Machine didn't finish: {}", mach.get_status().red()); std::process::exit(1); } - Ok(()) } diff --git a/arbitrator/prover/src/memory.rs b/arbitrator/prover/src/memory.rs index 8cf5b8e94c..bd96221091 100644 --- a/arbitrator/prover/src/memory.rs +++ b/arbitrator/prover/src/memory.rs @@ -3,14 +3,46 @@ use crate::{ merkle::{Merkle, MerkleType}, - utils::Bytes32, value::{ArbValueType, Value}, }; +use arbutil::Bytes32; use digest::Digest; -use rayon::prelude::*; +use eyre::{bail, ErrReport, Result}; use serde::{Deserialize, Serialize}; use sha3::Keccak256; use std::{borrow::Cow, convert::TryFrom}; +use wasmer_types::Pages; + +#[cfg(feature = "rayon")] +use rayon::prelude::*; + +pub struct MemoryType { + pub min: Pages, + pub max: Option, +} + +impl MemoryType { + pub fn new(min: Pages, max: Option) -> Self { + Self { min, max } + } +} + +impl From<&wasmer_types::MemoryType> for MemoryType { + fn from(value: &wasmer_types::MemoryType) -> Self { + Self::new(value.minimum, value.maximum) + } +} + +impl TryFrom<&wasmparser::MemoryType> for MemoryType { + type Error = ErrReport; + + fn try_from(value: &wasmparser::MemoryType) -> std::result::Result { + Ok(Self { + min: Pages(value.initial.try_into()?), + max: value.maximum.map(|x| x.try_into()).transpose()?.map(Pages), + }) + } +} #[derive(PartialEq, Eq, Clone, Debug, Default, Serialize, Deserialize)] pub struct Memory { @@ -72,9 +104,14 @@ impl Memory { } // Round the size up to 8 byte long leaves, then round up to the next power of two number of leaves let leaves = round_up_to_power_of_two(div_round_up(self.buffer.len(), Self::LEAF_SIZE)); - let mut leaf_hashes: Vec = self - .buffer - .par_chunks(Self::LEAF_SIZE) + + #[cfg(feature = "rayon")] + let leaf_hashes = self.buffer.par_chunks(Self::LEAF_SIZE); + + #[cfg(not(feature = "rayon"))] + let leaf_hashes = self.buffer.chunks(Self::LEAF_SIZE); + + let mut leaf_hashes: Vec = leaf_hashes .map(|leaf| { let mut full_leaf = [0u8; 32]; full_leaf[..leaf.len()].copy_from_slice(leaf); @@ -174,11 +211,11 @@ impl Memory { ArbValueType::I64 => Value::I64(contents as u64), ArbValueType::F32 => { assert!(bytes == 4 && !signed, "Invalid source for f32"); - Value::F32(f32::from_bits(contents as u32)) + f32::from_bits(contents as u32).into() } ArbValueType::F64 => { assert!(bytes == 8 && !signed, "Invalid source for f64"); - Value::F64(f64::from_bits(contents as u64)) + f64::from_bits(contents as u64).into() } _ => panic!("Invalid memory load output type {:?}", ty), }) @@ -186,9 +223,8 @@ impl Memory { #[must_use] pub fn store_value(&mut self, idx: u64, value: u64, bytes: u8) -> bool { - let end_idx = match idx.checked_add(bytes.into()) { - Some(x) => x, - None => return false, + let Some(end_idx) = idx.checked_add(bytes.into()) else { + return false; }; if end_idx > self.buffer.len() as u64 { return false; @@ -216,9 +252,8 @@ impl Memory { if idx % Self::LEAF_SIZE as u64 != 0 { return false; } - let end_idx = match idx.checked_add(value.len() as u64) { - Some(x) => x, - None => return false, + let Some(end_idx) = idx.checked_add(value.len() as u64) else { + return false; }; if end_idx > self.buffer.len() as u64 { return false; @@ -242,9 +277,8 @@ impl Memory { if idx % Self::LEAF_SIZE as u64 != 0 { return None; } - let idx = match usize::try_from(idx) { - Ok(x) => x, - Err(_) => return None, + let Ok(idx) = usize::try_from(idx) else { + return None; }; let slice = self.get_range(idx, 32)?; @@ -261,12 +295,13 @@ impl Memory { Some(&self.buffer[offset..end]) } - pub fn set_range(&mut self, offset: usize, data: &[u8]) { + pub fn set_range(&mut self, offset: usize, data: &[u8]) -> Result<()> { self.merkle = None; - let end = offset - .checked_add(data.len()) - .expect("Overflow in offset+data.len() in Memory::set_range"); + let Some(end) = offset.checked_add(data.len()) else { + bail!("Overflow in offset+data.len() in Memory::set_range") + }; self.buffer[offset..end].copy_from_slice(data); + Ok(()) } pub fn cache_merkle_tree(&mut self) { diff --git a/arbitrator/prover/src/merkle.rs b/arbitrator/prover/src/merkle.rs index 6a4c3dac1f..16306bd611 100644 --- a/arbitrator/prover/src/merkle.rs +++ b/arbitrator/prover/src/merkle.rs @@ -1,13 +1,16 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -use crate::utils::Bytes32; +use arbutil::Bytes32; use digest::Digest; -use rayon::prelude::*; +use serde::{Deserialize, Serialize}; use sha3::Keccak256; use std::convert::TryFrom; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg(feature = "rayon")] +use rayon::prelude::*; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum MerkleType { Empty, Value, @@ -40,11 +43,12 @@ impl MerkleType { } } -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] pub struct Merkle { ty: MerkleType, layers: Vec>, empty_layers: Vec, + min_depth: usize, } fn hash_node(ty: MerkleType, a: Bytes32, b: Bytes32) -> Bytes32 { @@ -73,13 +77,15 @@ impl Merkle { let mut empty_layers = vec![empty_hash]; while layers.last().unwrap().len() > 1 || layers.len() < min_depth { let empty_layer = *empty_layers.last().unwrap(); - let new_layer = layers - .last() - .unwrap() - .par_chunks(2) - .map(|window| { - hash_node(ty, window[0], window.get(1).cloned().unwrap_or(empty_layer)) - }) + + #[cfg(feature = "rayon")] + let new_layer = layers.last().unwrap().par_chunks(2); + + #[cfg(not(feature = "rayon"))] + let new_layer = layers.last().unwrap().chunks(2); + + let new_layer = new_layer + .map(|chunk| hash_node(ty, chunk[0], chunk.get(1).cloned().unwrap_or(empty_layer))) .collect(); empty_layers.push(hash_node(ty, empty_layer, empty_layer)); layers.push(new_layer); @@ -88,6 +94,7 @@ impl Merkle { ty, layers, empty_layers, + min_depth, } } @@ -109,10 +116,16 @@ impl Merkle { } #[must_use] - pub fn prove(&self, mut idx: usize) -> Option> { + pub fn prove(&self, idx: usize) -> Option> { if idx >= self.leaves().len() { return None; } + Some(self.prove_any(idx)) + } + + /// creates a merkle proof regardless of if the leaf has content + #[must_use] + pub fn prove_any(&self, mut idx: usize) -> Vec { let mut proof = vec![u8::try_from(self.layers.len() - 1).unwrap()]; for (layer_i, layer) in self.layers.iter().enumerate() { if layer_i == self.layers.len() - 1 { @@ -127,7 +140,25 @@ impl Merkle { ); idx >>= 1; } - Some(proof) + proof + } + + /// Adds a new leaf to the merkle + /// Currently O(n) in the number of leaves (could be log(n)) + pub fn push_leaf(&mut self, leaf: Bytes32) { + let mut leaves = self.layers.swap_remove(0); + leaves.push(leaf); + let empty = self.empty_layers[0]; + *self = Self::new_advanced(self.ty, leaves, empty, self.min_depth); + } + + /// Removes the rightmost leaf from the merkle + /// Currently O(n) in the number of leaves (could be log(n)) + pub fn pop_leaf(&mut self) { + let mut leaves = self.layers.swap_remove(0); + leaves.pop(); + let empty = self.empty_layers[0]; + *self = Self::new_advanced(self.ty, leaves, empty, self.min_depth); } pub fn set(&mut self, mut idx: usize, hash: Bytes32) { diff --git a/arbitrator/prover/src/print.rs b/arbitrator/prover/src/print.rs new file mode 100644 index 0000000000..138a01f4b5 --- /dev/null +++ b/arbitrator/prover/src/print.rs @@ -0,0 +1,303 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{ + host::InternalFunc, + machine::Module, + value::{FunctionType, Value}, + wavm::{self, Opcode}, +}; +use arbutil::Color; +use fnv::FnvHashSet as HashSet; +use num_traits::FromPrimitive; +use std::fmt::{self, Display}; +use wasmer_types::WASM_PAGE_SIZE; + +impl FunctionType { + fn wat_string(&self, name_args: bool) -> String { + let params = if !self.inputs.is_empty() { + let inputs = self.inputs.iter().enumerate(); + let params = inputs.fold(String::new(), |acc, (j, ty)| match name_args { + true => format!("{acc} {} {}", format!("$arg{j}").pink(), ty.mint()), + false => format!("{acc} {}", ty.mint()), + }); + format!(" ({}{params})", "param".grey()) + } else { + String::new() + }; + + let results = if !self.outputs.is_empty() { + let outputs = self.outputs.iter(); + let results = outputs.fold(String::new(), |acc, t| format!("{acc} {t}")); + format!(" ({}{})", "result".grey(), results.mint()) + } else { + String::new() + }; + + format!("{params}{results}") + } +} + +impl Module { + fn func_name(&self, i: u32) -> String { + match self.maybe_func_name(i) { + Some(func) => format!("${func}"), + None => format!("$func_{i}"), + } + .pink() + } + + fn maybe_func_name(&self, i: u32) -> Option { + if let Some(name) = self.names.functions.get(&i) { + Some(name.to_owned()) + } else if i >= self.internals_offset { + InternalFunc::from_u32(i - self.internals_offset).map(|f| format!("{f:?}")) + } else { + None + } + } +} + +impl Display for Module { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut pad = 0; + + macro_rules! w { + ($($args:expr),*) => {{ + let text = format!($($args),*); + write!(f, "{:pad$}{text}", "")?; + }}; + } + macro_rules! wln { + ($($args:expr),*) => {{ + w!($($args),*); + writeln!(f)?; + }}; + } + + wln!("({} {}", "module".grey(), self.name().mint()); + pad += 4; + + for ty in &*self.types { + let ty = ty.wat_string(false); + wln!("({} ({}{ty}))", "type".grey(), "func".grey()); + } + + for (i, hook) in self.host_call_hooks.iter().enumerate() { + if let Some((module, func)) = hook { + wln!( + r#"({} "{}" "{}" ({} {}{}))"#, + "import".grey(), + module.pink(), + func.pink(), + "func".grey(), + self.func_name(i as u32), + self.funcs[i].ty.wat_string(false) + ); + } + } + + for (i, g) in self.globals.iter().enumerate() { + let global_label = format!("$global_{i}").pink(); + wln!("({} {global_label} {})", "global".grey(), g.mint()); + } + + for (i, table) in self.tables.iter().enumerate() { + let ty = table.ty; + let initial = format!("{}", ty.initial).mint(); + let max = ty.maximum.map(|x| format!(" {x}")).unwrap_or_default(); + let type_str = format!("{:?}", ty.element_type).mint(); + w!( + "({} {} {initial} {}{type_str}", + "table".grey(), + format!("$table_{i}").pink(), + max.mint() + ); + + pad += 4; + let mut empty = true; + let mut segment = vec![]; + let mut start = None; + let mut end = 0; + for (j, elem) in table.elems.iter().enumerate() { + if let Value::FuncRef(id) = elem.val { + segment.push(self.func_name(id)); + start.get_or_insert(j); + end = j; + empty = false; + } + + let last = j == table.elems.len() - 1; + if (last || matches!(elem.val, Value::RefNull)) && !segment.is_empty() { + let start = start.unwrap(); + wln!(""); + w!("{}", format!("[{start:#05x}-{end:#05x}]:").grey()); + for item in &segment { + write!(f, " {item}")?; + } + segment.clear(); + } + } + pad -= 4; + if !empty { + wln!(""); + w!(""); + } + writeln!(f, ")")?; + } + + let args = format!( + "{} {}", + self.memory.size() / WASM_PAGE_SIZE as u64, + self.memory.max_size + ); + w!("({} {}", "memory".grey(), args.mint()); + + pad += 4; + let mut empty = true; + let mut segment = None; + for index in 0..self.memory.size() { + let byte = self.memory.get_u8(index).unwrap(); + + // start new segment + if byte != 0 && segment.is_none() { + segment = Some(index as usize); + empty = false; + } + + // print the segment + if (byte == 0x00 || index == self.memory.size() - 1) && segment.is_some() { + let start = segment.unwrap(); + let end = index - 1 + (byte != 0x00) as u64; + let len = end as usize - start + 1; + let range = format!("[{start:#06x}-{end:#06x}]"); + let data = self.memory.get_range(start, len).unwrap(); + wln!(""); + w!("{}: {}", range.grey(), hex::encode(data).yellow()); + segment = None; + } + } + pad -= 4; + if !empty { + wln!(""); + w!(""); + } + writeln!(f, ")")?; + + for (i, func) in self.funcs.iter().enumerate() { + let i1 = i as u32; + let padding = 12; + + let export_str = match self.maybe_func_name(i1) { + Some(name) => { + let description = if (i1 as usize) < self.host_call_hooks.len() { + "import" + } else { + "export" + }; + format!(r#" ({} "{}")"#, description.grey(), name.pink()) + } + None => format!(" $func_{i}").pink(), + }; + w!( + "({}{}{}", + "func".grey(), + export_str, + func.ty.wat_string(true) + ); + + pad += 4; + if !func.local_types.is_empty() { + write!(f, " ({}", "local".grey())?; + for (i, ty) in func.local_types.iter().enumerate() { + let local_str = format!("$local_{i}"); + write!(f, " {} {}", local_str.pink(), ty.mint())?; + } + write!(f, ")")?; + } + writeln!(f)?; + + let mut labels = HashSet::default(); + use Opcode::*; + for op in func.code.iter() { + if op.opcode == ArbitraryJump || op.opcode == ArbitraryJumpIf { + labels.insert(op.argument_data as usize); + } + } + + for (j, op) in func.code.iter().enumerate() { + let op_str = format!("{:?}", op.opcode).grey(); + let arg_str = match op.opcode { + ArbitraryJump | ArbitraryJumpIf => { + match labels.get(&(op.argument_data as usize)) { + Some(label) => format!(" label_${label}").pink(), + None => " ???".to_string().red(), + } + } + Call + | CallerModuleInternalCall + | CrossModuleForward + | CrossModuleInternalCall => { + format!(" {}", self.func_name(op.argument_data as u32)) + } + CrossModuleCall => { + let (module, func) = wavm::unpack_cross_module_call(op.argument_data); + format!( + " {} {}", + format!("{module}").mint(), + format!("{func}").mint() + ) + } + CallIndirect => { + let (table_index, type_index) = + wavm::unpack_call_indirect(op.argument_data); + format!( + " {} {}", + self.types[type_index as usize].pink(), + format!("{table_index}").mint() + ) + } + F32Const | F64Const | I32Const | I64Const => { + format!(" {:#x}", op.argument_data).mint() + } + GlobalGet | GlobalSet => format!(" $global_{}", op.argument_data).pink(), + LocalGet | LocalSet => format!(" $local_{}", op.argument_data).pink(), + MemoryLoad { .. } | MemoryStore { .. } | ReadInboxMessage => { + format!(" {:#x}", op.argument_data).mint() + } + _ => { + if op.argument_data == 0 { + String::new() + } else { + format!(" UNEXPECTED_ARG: {}", op.argument_data).mint() + } + } + }; + + let proof = op + .proving_argument_data + .map(hex::encode) + .unwrap_or_default() + .orange(); + + match labels.get(&j) { + Some(label) => { + let label = format!("label_{label}"); + let spaces = padding - label.len() - 1; + wln!("{}:{:spaces$}{op_str}{arg_str} {proof}", label.pink(), "") + } + None => wln!("{:padding$}{op_str}{arg_str} {proof}", ""), + } + } + pad -= 4; + wln!(")"); + } + + if let Some(start) = self.start_function { + wln!("({} {})", "start".grey(), self.func_name(start)); + } + pad -= 4; + wln!(")"); + Ok(()) + } +} diff --git a/arbitrator/prover/src/programs/config.rs b/arbitrator/prover/src/programs/config.rs new file mode 100644 index 0000000000..0b5ce17475 --- /dev/null +++ b/arbitrator/prover/src/programs/config.rs @@ -0,0 +1,222 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![allow(clippy::field_reassign_with_default)] + +use crate::{programs::meter, value::FunctionType}; +use derivative::Derivative; +use fnv::FnvHashMap as HashMap; +use std::fmt::Debug; +use wasmer_types::{Pages, SignatureIndex, WASM_PAGE_SIZE}; +use wasmparser::Operator; + +#[cfg(feature = "native")] +use { + super::{ + counter::Counter, depth::DepthChecker, dynamic::DynamicMeter, heap::HeapBound, + meter::Meter, start::StartMover, MiddlewareWrapper, + }, + std::sync::Arc, + wasmer::{Cranelift, CraneliftOptLevel, Engine, Store}, + wasmer_compiler_singlepass::Singlepass, +}; + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct StylusConfig { + /// Version the program was compiled against + pub version: u16, + /// The maximum size of the stack, measured in words + pub max_depth: u32, + /// Pricing parameters supplied at runtime + pub pricing: PricingParams, +} + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct PricingParams { + /// The price of ink, measured in bips of an evm gas + pub ink_price: u32, +} + +impl Default for StylusConfig { + fn default() -> Self { + Self { + version: 0, + max_depth: u32::MAX, + pricing: PricingParams::default(), + } + } +} + +impl Default for PricingParams { + fn default() -> Self { + Self { ink_price: 1 } + } +} + +impl StylusConfig { + pub const fn new(version: u16, max_depth: u32, ink_price: u32) -> Self { + let pricing = PricingParams::new(ink_price); + Self { + version, + max_depth, + pricing, + } + } +} + +#[allow(clippy::inconsistent_digit_grouping)] +impl PricingParams { + pub const fn new(ink_price: u32) -> Self { + Self { ink_price } + } + + pub fn gas_to_ink(&self, gas: u64) -> u64 { + gas.saturating_mul(self.ink_price.into()) + } + + pub fn ink_to_gas(&self, ink: u64) -> u64 { + ink / self.ink_price as u64 // never 0 + } +} + +pub type SigMap = HashMap; +pub type OpCosts = fn(&Operator, &SigMap) -> u64; + +#[derive(Clone, Debug, Default)] +pub struct CompileConfig { + /// Version of the compiler to use + pub version: u16, + /// Pricing parameters used for metering + pub pricing: CompilePricingParams, + /// Memory bounds + pub bounds: CompileMemoryParams, + /// Debug parameters for test chains + pub debug: CompileDebugParams, +} + +#[derive(Clone, Copy, Debug)] +pub struct CompileMemoryParams { + /// The maximum number of pages a program may start with + pub heap_bound: Pages, + /// The maximum size of a stack frame, measured in words + pub max_frame_size: u32, + /// The maximum number of overlapping value lifetimes in a frame + pub max_frame_contention: u16, +} + +#[derive(Clone, Derivative)] +#[derivative(Debug)] +pub struct CompilePricingParams { + /// Associates opcodes to their ink costs + #[derivative(Debug = "ignore")] + pub costs: OpCosts, + /// Cost of checking the amount of ink left. + pub ink_header_cost: u64, + /// Per-byte `MemoryFill` cost + pub memory_fill_ink: u64, + /// Per-byte `MemoryCopy` cost + pub memory_copy_ink: u64, +} + +#[derive(Clone, Debug, Default)] +pub struct CompileDebugParams { + /// Allow debug functions + pub debug_funcs: bool, + /// Retain debug info + pub debug_info: bool, + /// Add instrumentation to count the number of times each kind of opcode is executed + pub count_ops: bool, + /// Whether to use the Cranelift compiler + pub cranelift: bool, +} + +impl Default for CompilePricingParams { + fn default() -> Self { + Self { + costs: |_, _| 0, + ink_header_cost: 0, + memory_fill_ink: 0, + memory_copy_ink: 0, + } + } +} + +impl Default for CompileMemoryParams { + fn default() -> Self { + Self { + heap_bound: Pages(u32::MAX / WASM_PAGE_SIZE as u32), + max_frame_size: u32::MAX, + max_frame_contention: u16::MAX, + } + } +} + +impl CompileConfig { + pub fn version(version: u16, debug_chain: bool) -> Self { + let mut config = Self::default(); + config.version = version; + config.debug.debug_funcs = debug_chain; + config.debug.debug_info = debug_chain; + + match version { + 0 => {} + 1 => { + // TODO: settle on reasonable values for the v1 release + config.bounds.heap_bound = Pages(128); // 8 mb + config.bounds.max_frame_size = 10 * 1024; + config.bounds.max_frame_contention = 4096; + config.pricing = CompilePricingParams { + costs: meter::pricing_v1, + ink_header_cost: 2450, + memory_fill_ink: 800 / 8, + memory_copy_ink: 800 / 8, + }; + } + _ => panic!("no config exists for Stylus version {version}"), + } + + config + } + + #[cfg(feature = "native")] + pub fn store(&self) -> Store { + let mut compiler: Box = match self.debug.cranelift { + true => { + let mut compiler = Cranelift::new(); + compiler.opt_level(CraneliftOptLevel::Speed); + Box::new(compiler) + } + false => Box::new(Singlepass::new()), + }; + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + + let start = MiddlewareWrapper::new(StartMover::new(self.debug.debug_info)); + let meter = MiddlewareWrapper::new(Meter::new(&self.pricing)); + let dygas = MiddlewareWrapper::new(DynamicMeter::new(&self.pricing)); + let depth = MiddlewareWrapper::new(DepthChecker::new(self.bounds)); + let bound = MiddlewareWrapper::new(HeapBound::new(self.bounds)); + + // add the instrumentation in the order of application + // note: this must be consistent with the prover + compiler.push_middleware(Arc::new(start)); + compiler.push_middleware(Arc::new(meter)); + compiler.push_middleware(Arc::new(dygas)); + compiler.push_middleware(Arc::new(depth)); + compiler.push_middleware(Arc::new(bound)); + + if self.debug.count_ops { + let counter = Counter::new(); + compiler.push_middleware(Arc::new(MiddlewareWrapper::new(counter))); + } + + Store::new(compiler) + } + + #[cfg(feature = "native")] + pub fn engine(&self) -> Engine { + self.store().engine().clone() + } +} diff --git a/arbitrator/prover/src/programs/counter.rs b/arbitrator/prover/src/programs/counter.rs new file mode 100644 index 0000000000..cd54178cf8 --- /dev/null +++ b/arbitrator/prover/src/programs/counter.rs @@ -0,0 +1,155 @@ +// Copyright 2021-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use super::{FuncMiddleware, Middleware, ModuleMod}; +use crate::Machine; + +use arbutil::operator::{OperatorCode, OperatorInfo}; +use eyre::{eyre, Result}; +use fnv::FnvHashMap as HashMap; +use lazy_static::lazy_static; +use parking_lot::Mutex; +use std::collections::BTreeMap; +use std::{clone::Clone, fmt::Debug, sync::Arc}; +use wasmer_types::{GlobalIndex, GlobalInit, LocalFunctionIndex, Type}; +use wasmparser::Operator; + +lazy_static! { + /// Assigns each operator a sequential offset + pub static ref OP_OFFSETS: Mutex> = Mutex::new(HashMap::default()); +} + +#[derive(Debug)] +pub struct Counter { + /// Assigns each relative offset a global variable + pub counters: Arc>>, +} + +impl Counter { + pub fn new() -> Self { + let counters = Arc::new(Mutex::new(Vec::with_capacity(OperatorCode::OPERATOR_COUNT))); + Self { counters } + } + + pub fn global_name(index: usize) -> String { + format!("stylus_opcode{}_count", index) + } +} + +impl Default for Counter { + fn default() -> Self { + Self::new() + } +} + +impl Middleware for Counter +where + M: ModuleMod, +{ + type FM<'a> = FuncCounter<'a>; + + fn update_module(&self, module: &mut M) -> Result<()> { + let mut counters = self.counters.lock(); + for index in 0..OperatorCode::OPERATOR_COUNT { + let zero_count = GlobalInit::I64Const(0); + let global = module.add_global(&Self::global_name(index), Type::I64, zero_count)?; + counters.push(global); + } + Ok(()) + } + + fn instrument<'a>(&self, _: LocalFunctionIndex) -> Result> { + Ok(FuncCounter::new(self.counters.clone())) + } + + fn name(&self) -> &'static str { + "operator counter" + } +} + +#[derive(Debug)] +pub struct FuncCounter<'a> { + /// Assigns each relative offset a global variable + counters: Arc>>, + /// Instructions of the current basic block + block: Vec>, +} + +impl<'a> FuncCounter<'a> { + fn new(counters: Arc>>) -> Self { + let block = vec![]; + Self { counters, block } + } +} + +impl<'a> FuncMiddleware<'a> for FuncCounter<'a> { + fn feed(&mut self, op: Operator<'a>, out: &mut O) -> Result<()> + where + O: Extend>, + { + use Operator::*; + + let end = op.ends_basic_block(); + self.block.push(op); + + if end { + let update = |global_index: u32, value: i64| { + [ + GlobalGet { global_index }, + I64Const { value }, + I64Add, + GlobalSet { global_index }, + ] + }; + + // there's always at least one op, so we chain the instrumentation + let mut increments = HashMap::default(); + for op in self.block.iter().chain(update(0, 0).iter()) { + let count = increments.entry(op.code()).or_default(); + *count += 1; + } + + // add the instrumentation's contribution to the overall counts + let kinds = increments.len() as i64; + for op in update(0, 0) { + let count = increments.get_mut(&op.code()).unwrap(); + *count += kinds - 1; // we included one in the last loop + } + + let counters = self.counters.lock(); + let mut operators = OP_OFFSETS.lock(); + for (op, count) in increments { + let opslen = operators.len(); + let offset = *operators.entry(op).or_insert(opslen); + let global = *counters.get(offset).ok_or_else(|| eyre!("no global"))?; + out.extend(update(global.as_u32(), count)); + } + + out.extend(self.block.drain(..)); + } + Ok(()) + } + + fn name(&self) -> &'static str { + "operator counter" + } +} + +pub trait CountingMachine { + fn operator_counts(&mut self) -> Result>; +} + +impl CountingMachine for Machine { + fn operator_counts(&mut self) -> Result> { + let mut counts = BTreeMap::new(); + + for (&op, &offset) in OP_OFFSETS.lock().iter() { + let count = self.get_global(&Counter::global_name(offset))?; + let count: u64 = count.try_into()?; + if count != 0 { + counts.insert(op, count); + } + } + Ok(counts) + } +} diff --git a/arbitrator/prover/src/programs/depth.rs b/arbitrator/prover/src/programs/depth.rs new file mode 100644 index 0000000000..2000190917 --- /dev/null +++ b/arbitrator/prover/src/programs/depth.rs @@ -0,0 +1,541 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use super::{ + config::{CompileMemoryParams, SigMap}, + FuncMiddleware, Middleware, ModuleMod, +}; +use crate::{host::InternalFunc, value::FunctionType, Machine}; + +use arbutil::Color; +use eyre::{bail, Result}; +use fnv::FnvHashMap as HashMap; +use parking_lot::RwLock; +use std::sync::Arc; +use wasmer_types::{ + FunctionIndex, GlobalIndex, GlobalInit, LocalFunctionIndex, SignatureIndex, Type, +}; +use wasmparser::{BlockType, Operator, ValType}; + +pub const STYLUS_STACK_LEFT: &str = "stylus_stack_left"; + +/// This middleware ensures stack overflows are deterministic across different compilers and targets. +/// The internal notion of "stack space left" that makes this possible is strictly smaller than that of +/// the real stack space consumed on any target platform and is formed by inspecting the contents of each +/// function's frame. +/// Setting a limit smaller than that of any native platform's ensures stack overflows will have the same, +/// logical effect rather than actually exhausting the space provided by the OS. +#[derive(Debug)] +pub struct DepthChecker { + /// The amount of stack space left + global: RwLock>, + /// The maximum size of a stack frame, measured in words + frame_limit: u32, + /// The maximum number of overlapping value lifetimes in a frame + frame_contention: u16, + /// The function types of the module being instrumented + funcs: RwLock>>>, + /// The types of the module being instrumented + sigs: RwLock>>, +} + +impl DepthChecker { + pub fn new(params: CompileMemoryParams) -> Self { + Self { + global: RwLock::default(), + frame_limit: params.max_frame_size, + frame_contention: params.max_frame_contention, + funcs: RwLock::default(), + sigs: RwLock::default(), + } + } + + pub fn globals(&self) -> GlobalIndex { + self.global.read().unwrap() + } +} + +impl Middleware for DepthChecker { + type FM<'a> = FuncDepthChecker<'a>; + + fn update_module(&self, module: &mut M) -> Result<()> { + let limit = GlobalInit::I32Const(0); + let space = module.add_global(STYLUS_STACK_LEFT, Type::I32, limit)?; + *self.global.write() = Some(space); + *self.funcs.write() = Some(Arc::new(module.all_functions()?)); + *self.sigs.write() = Some(Arc::new(module.all_signatures()?)); + Ok(()) + } + + fn instrument<'a>(&self, func: LocalFunctionIndex) -> Result> { + Ok(FuncDepthChecker::new( + self.global.read().expect("no global"), + self.funcs.read().clone().expect("no funcs"), + self.sigs.read().clone().expect("no sigs"), + self.frame_limit, + self.frame_contention, + func, + )) + } + + fn name(&self) -> &'static str { + "depth checker" + } +} + +#[derive(Debug)] +pub struct FuncDepthChecker<'a> { + /// The amount of stack space left + global: GlobalIndex, + /// The function types in this function's module + funcs: Arc>, + /// All the types in this function's modules + sigs: Arc>, + /// The number of local variables this func has + locals: Option, + /// The function being instrumented + func: LocalFunctionIndex, + /// The maximum size of a stack frame, measured in words + frame_limit: u32, + /// The maximum number of overlapping value lifetimes in a frame + frame_contention: u16, + /// The number of open scopes + scopes: isize, + /// The entirety of the func's original instructions + code: Vec>, + /// True once it's statically known feed() won't be called again + done: bool, +} + +impl<'a> FuncDepthChecker<'a> { + fn new( + global: GlobalIndex, + funcs: Arc>, + sigs: Arc>, + frame_limit: u32, + frame_contention: u16, + func: LocalFunctionIndex, + ) -> Self { + Self { + global, + funcs, + sigs, + locals: None, + func, + frame_limit, + frame_contention, + scopes: 1, // a function starts with an open scope + code: vec![], + done: false, + } + } +} + +impl<'a> FuncMiddleware<'a> for FuncDepthChecker<'a> { + fn locals_info(&mut self, locals: &[ValType]) { + self.locals = Some(locals.len()); + } + + fn feed(&mut self, op: Operator<'a>, out: &mut O) -> Result<()> + where + O: Extend>, + { + use Operator::*; + + // Knowing when the feed ends requires detecting the final instruction, which is + // guaranteed to be an "End" opcode closing out function's initial opening scope. + if self.done { + bail!("finalized too soon"); + } + + let scopes = &mut self.scopes; + match op { + Block { .. } | Loop { .. } | If { .. } => *scopes += 1, + End => *scopes -= 1, + _ => {} + } + if *scopes < 0 { + bail!("malformed scoping detected"); + } + + let last = *scopes == 0 && matches!(op, End); // true when the feed ends + self.code.push(op); + if !last { + return Ok(()); + } + + // We've reached the final instruction and can instrument the function as follows: + // - When entering, check that the stack has sufficient space and deduct the amount used + // - When returning, credit back the amount used + + let size = self.worst_case_depth()?; + let global_index = self.global.as_u32(); + + if size > self.frame_limit { + let limit = self.frame_limit.red(); + bail!("frame too large: {} > {}-word limit", size.red(), limit); + } + + let blockty = BlockType::Empty; + out.extend([ + // if space <= size => panic with depth = 0 + GlobalGet { global_index }, + I32Const { value: size as i32 }, + I32LeU, + If { blockty }, + I32Const { value: 0 }, + GlobalSet { global_index }, + Unreachable, + End, + // space -= size + GlobalGet { global_index }, + I32Const { value: size as i32 }, + I32Sub, + GlobalSet { global_index }, + ]); + + let reclaim = |out: &mut O| { + out.extend([ + // space += size + GlobalGet { global_index }, + I32Const { value: size as i32 }, + I32Add, + GlobalSet { global_index }, + ]) + }; + + // add an extraneous return instruction to the end to match Arbitrator + let mut code = std::mem::take(&mut self.code); + let last = code.pop().unwrap(); + code.push(Return); + code.push(last); + + for op in code { + let exit = matches!(op, Return); + if exit { + reclaim(out); + } + out.extend([op]); + } + + self.done = true; + Ok(()) + } + + fn name(&self) -> &'static str { + "depth checker" + } +} + +impl<'a> FuncDepthChecker<'a> { + fn worst_case_depth(&self) -> Result { + use Operator::*; + + let mut worst: u32 = 0; + let mut stack: u32 = 0; + + macro_rules! push { + ($count:expr) => {{ + stack += $count; + worst = worst.max(stack); + }}; + () => { + push!(1) + }; + } + macro_rules! pop { + ($count:expr) => {{ + stack = stack.saturating_sub($count); + }}; + () => { + pop!(1) + }; + } + macro_rules! ins_and_outs { + ($ty:expr) => {{ + let ins = $ty.inputs.len() as u32; + let outs = $ty.outputs.len() as u32; + push!(outs); + pop!(ins); + }}; + } + macro_rules! op { + ($first:ident $(,$opcode:ident)* $(,)?) => { + $first $(| $opcode)* + }; + } + macro_rules! dot { + ($first:ident $(,$opcode:ident)* $(,)?) => { + $first { .. } $(| $opcode { .. })* + }; + } + #[rustfmt::skip] + macro_rules! block_type { + ($ty:expr) => {{ + match $ty { + BlockType::Empty => {} + BlockType::Type(_) => push!(1), + BlockType::FuncType(id) => { + let index = SignatureIndex::from_u32(*id); + let Some(ty) = self.sigs.get(&index) else { + bail!("missing type for func {}", id.red()) + }; + ins_and_outs!(ty); + } + } + }}; + } + + let mut scopes = vec![stack]; + + for op in &self.code { + #[rustfmt::skip] + match op { + Block { blockty } => { + block_type!(blockty); // we'll say any return slots have been pre-allocated + scopes.push(stack); + } + Loop { blockty } => { + block_type!(blockty); // return slots + scopes.push(stack); + } + If { blockty } => { + pop!(); // pop the conditional + block_type!(blockty); // return slots + scopes.push(stack); + } + Else => { + stack = match scopes.last() { + Some(scope) => *scope, + None => bail!("malformed if-else scope"), + }; + } + End => { + stack = match scopes.pop() { + Some(stack) => stack, + None => bail!("malformed scoping detected at end of block"), + }; + } + + Call { function_index } => { + let index = FunctionIndex::from_u32(*function_index); + let Some(ty) = self.funcs.get(&index) else { + bail!("missing type for func {}", function_index.red()) + }; + ins_and_outs!(ty) + } + CallIndirect { type_index, .. } => { + let index = SignatureIndex::from_u32(*type_index); + let Some(ty) = self.sigs.get(&index) else { + bail!("missing type for signature {}", type_index.red()) + }; + ins_and_outs!(ty); + pop!() // the table index + } + + MemoryFill { .. } => ins_and_outs!(InternalFunc::MemoryFill.ty()), + MemoryCopy { .. } => ins_and_outs!(InternalFunc::MemoryCopy.ty()), + + op!( + Nop, Unreachable, + I32Eqz, I64Eqz, I32Clz, I32Ctz, I32Popcnt, I64Clz, I64Ctz, I64Popcnt, + ) + | dot!( + Br, Return, + LocalTee, MemoryGrow, + I32Load, I64Load, F32Load, F64Load, + I32Load8S, I32Load8U, I32Load16S, I32Load16U, I64Load8S, I64Load8U, + I64Load16S, I64Load16U, I64Load32S, I64Load32U, + I32WrapI64, I64ExtendI32S, I64ExtendI32U, + I32Extend8S, I32Extend16S, I64Extend8S, I64Extend16S, I64Extend32S, + F32Abs, F32Neg, F32Ceil, F32Floor, F32Trunc, F32Nearest, F32Sqrt, + F64Abs, F64Neg, F64Ceil, F64Floor, F64Trunc, F64Nearest, F64Sqrt, + I32TruncF32S, I32TruncF32U, I32TruncF64S, I32TruncF64U, + I64TruncF32S, I64TruncF32U, I64TruncF64S, I64TruncF64U, + F32ConvertI32S, F32ConvertI32U, F32ConvertI64S, F32ConvertI64U, F32DemoteF64, + F64ConvertI32S, F64ConvertI32U, F64ConvertI64S, F64ConvertI64U, F64PromoteF32, + I32ReinterpretF32, I64ReinterpretF64, F32ReinterpretI32, F64ReinterpretI64, + I32TruncSatF32S, I32TruncSatF32U, I32TruncSatF64S, I32TruncSatF64U, + I64TruncSatF32S, I64TruncSatF32U, I64TruncSatF64S, I64TruncSatF64U, + ) => {} + + dot!( + LocalGet, GlobalGet, MemorySize, + I32Const, I64Const, F32Const, F64Const, + ) => push!(), + + op!( + Drop, + I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU, + I64Eq, I64Ne, I64LtS, I64LtU, I64GtS, I64GtU, I64LeS, I64LeU, I64GeS, I64GeU, + F32Eq, F32Ne, F32Lt, F32Gt, F32Le, F32Ge, + F64Eq, F64Ne, F64Lt, F64Gt, F64Le, F64Ge, + I32Add, I32Sub, I32Mul, I32DivS, I32DivU, I32RemS, I32RemU, + I64Add, I64Sub, I64Mul, I64DivS, I64DivU, I64RemS, I64RemU, + I32And, I32Or, I32Xor, I32Shl, I32ShrS, I32ShrU, I32Rotl, I32Rotr, + I64And, I64Or, I64Xor, I64Shl, I64ShrS, I64ShrU, I64Rotl, I64Rotr, + F32Add, F32Sub, F32Mul, F32Div, F32Min, F32Max, F32Copysign, + F64Add, F64Sub, F64Mul, F64Div, F64Min, F64Max, F64Copysign, + ) + | dot!(BrIf, BrTable, LocalSet, GlobalSet) => pop!(), + + dot!( + Select, + I32Store, I64Store, F32Store, F64Store, I32Store8, I32Store16, I64Store8, I64Store16, I64Store32, + ) => pop!(2), + + unsupported @ dot!(Try, Catch, Throw, Rethrow, ThrowRef, TryTable) => { + bail!("exception-handling extension not supported {unsupported:?}") + }, + + unsupported @ dot!(ReturnCall, ReturnCallIndirect) => { + bail!("tail-call extension not supported {unsupported:?}") + } + + unsupported @ dot!(CallRef, ReturnCallRef) => { + bail!("typed function references extension not supported {unsupported:?}") + } + + unsupported @ (dot!(Delegate) | op!(CatchAll)) => { + bail!("exception-handling extension not supported {unsupported:?}") + }, + + unsupported @ (op!(RefIsNull) | dot!(TypedSelect, RefNull, RefFunc, RefEq)) => { + bail!("reference-types extension not supported {unsupported:?}") + }, + + unsupported @ dot!(RefAsNonNull, BrOnNull, BrOnNonNull) => { + bail!("typed function references extension not supported {unsupported:?}") + }, + + unsupported @ ( + dot!( + MemoryInit, DataDrop, TableInit, ElemDrop, + TableCopy, TableFill, TableGet, TableSet, TableGrow, TableSize + ) + ) => bail!("bulk-memory-operations extension not fully supported {unsupported:?}"), + + unsupported @ dot!(MemoryDiscard) => { + bail!("typed function references extension not supported {unsupported:?}") + } + + unsupported @ ( + dot!( + StructNew, StructNewDefault, StructGet, StructGetS, StructGetU, StructSet, + ArrayNew, ArrayNewDefault, ArrayNewFixed, ArrayNewData, ArrayNewElem, + ArrayGet, ArrayGetS, ArrayGetU, ArraySet, ArrayLen, ArrayFill, ArrayCopy, + ArrayInitData, ArrayInitElem, + RefTestNonNull, RefTestNullable, RefCastNonNull, RefCastNullable, + BrOnCast, BrOnCastFail, AnyConvertExtern, ExternConvertAny, RefI31, I31GetS, I31GetU + ) + ) => bail!("garbage collection extension not supported {unsupported:?}"), + + unsupported @ ( + dot!( + MemoryAtomicNotify, MemoryAtomicWait32, MemoryAtomicWait64, AtomicFence, I32AtomicLoad, + I64AtomicLoad, I32AtomicLoad8U, I32AtomicLoad16U, I64AtomicLoad8U, I64AtomicLoad16U, + I64AtomicLoad32U, I32AtomicStore, I64AtomicStore, I32AtomicStore8, I32AtomicStore16, + I64AtomicStore8, I64AtomicStore16, I64AtomicStore32, I32AtomicRmwAdd, I64AtomicRmwAdd, + I32AtomicRmw8AddU, I32AtomicRmw16AddU, + I64AtomicRmw8AddU, I64AtomicRmw16AddU, I64AtomicRmw32AddU, + I32AtomicRmwSub, I64AtomicRmwSub, I32AtomicRmw8SubU, I32AtomicRmw16SubU, I64AtomicRmw8SubU, + I64AtomicRmw16SubU, I64AtomicRmw32SubU, I32AtomicRmwAnd, I64AtomicRmwAnd, I32AtomicRmw8AndU, + I32AtomicRmw16AndU, I64AtomicRmw8AndU, I64AtomicRmw16AndU, I64AtomicRmw32AndU, I32AtomicRmwOr, + I64AtomicRmwOr, I32AtomicRmw8OrU, I32AtomicRmw16OrU, I64AtomicRmw8OrU, I64AtomicRmw16OrU, + I64AtomicRmw32OrU, I32AtomicRmwXor, I64AtomicRmwXor, I32AtomicRmw8XorU, I32AtomicRmw16XorU, + I64AtomicRmw8XorU, I64AtomicRmw16XorU, I64AtomicRmw32XorU, I32AtomicRmwXchg, I64AtomicRmwXchg, + I32AtomicRmw8XchgU, I32AtomicRmw16XchgU, I64AtomicRmw8XchgU, I64AtomicRmw16XchgU, + I64AtomicRmw32XchgU, I32AtomicRmwCmpxchg, I64AtomicRmwCmpxchg, I32AtomicRmw8CmpxchgU, + I32AtomicRmw16CmpxchgU, I64AtomicRmw8CmpxchgU, I64AtomicRmw16CmpxchgU, I64AtomicRmw32CmpxchgU + ) + ) => bail!("threads extension not supported {unsupported:?}"), + + unsupported @ ( + dot!( + V128Load, V128Load8x8S, V128Load8x8U, V128Load16x4S, V128Load16x4U, V128Load32x2S, + V128Load8Splat, V128Load16Splat, V128Load32Splat, V128Load64Splat, V128Load32Zero, + V128Load64Zero, V128Load32x2U, + V128Store, V128Load8Lane, V128Load16Lane, V128Load32Lane, V128Load64Lane, V128Store8Lane, + V128Store16Lane, V128Store32Lane, V128Store64Lane, V128Const, + I8x16Shuffle, I8x16ExtractLaneS, I8x16ExtractLaneU, I8x16ReplaceLane, I16x8ExtractLaneS, + I16x8ExtractLaneU, I16x8ReplaceLane, I32x4ExtractLane, I32x4ReplaceLane, I64x2ExtractLane, + I64x2ReplaceLane, F32x4ExtractLane, F32x4ReplaceLane, F64x2ExtractLane, F64x2ReplaceLane, + I8x16Swizzle, I8x16Splat, I16x8Splat, I32x4Splat, I64x2Splat, F32x4Splat, F64x2Splat, I8x16Eq, + I8x16Ne, I8x16LtS, I8x16LtU, I8x16GtS, I8x16GtU, I8x16LeS, I8x16LeU, I8x16GeS, I8x16GeU, + I16x8Eq, I16x8Ne, I16x8LtS, I16x8LtU, I16x8GtS, I16x8GtU, I16x8LeS, I16x8LeU, I16x8GeS, + I16x8GeU, I32x4Eq, I32x4Ne, I32x4LtS, I32x4LtU, I32x4GtS, I32x4GtU, I32x4LeS, I32x4LeU, + I32x4GeS, I32x4GeU, I64x2Eq, I64x2Ne, I64x2LtS, I64x2GtS, I64x2LeS, I64x2GeS, + F32x4Eq, F32x4Ne, F32x4Lt, F32x4Gt, F32x4Le, F32x4Ge, + F64x2Eq, F64x2Ne, F64x2Lt, F64x2Gt, F64x2Le, F64x2Ge, + V128Not, V128And, V128AndNot, V128Or, V128Xor, V128Bitselect, V128AnyTrue, + I8x16Abs, I8x16Neg, I8x16Popcnt, I8x16AllTrue, I8x16Bitmask, + I8x16NarrowI16x8S, I8x16NarrowI16x8U, + I8x16Shl, I8x16ShrS, I8x16ShrU, I8x16Add, I8x16AddSatS, I8x16AddSatU, I8x16Sub, I8x16SubSatS, + I8x16SubSatU, I8x16MinS, I8x16MinU, I8x16MaxS, I8x16MaxU, I8x16AvgrU, + I16x8ExtAddPairwiseI8x16S, I16x8ExtAddPairwiseI8x16U, I16x8Abs, I16x8Neg, I16x8Q15MulrSatS, + I16x8AllTrue, I16x8Bitmask, I16x8NarrowI32x4S, I16x8NarrowI32x4U, I16x8ExtendLowI8x16S, + I16x8ExtendHighI8x16S, I16x8ExtendLowI8x16U, I16x8ExtendHighI8x16U, + I16x8Shl, I16x8ShrS, I16x8ShrU, I16x8Add, I16x8AddSatS, I16x8AddSatU, + I16x8Sub, I16x8SubSatS, I16x8SubSatU, I16x8Mul, I16x8MinS, I16x8MinU, + I16x8MaxS, I16x8MaxU, I16x8AvgrU, I16x8ExtMulLowI8x16S, + I16x8ExtMulHighI8x16S, I16x8ExtMulLowI8x16U, I16x8ExtMulHighI8x16U, + I32x4ExtAddPairwiseI16x8U, I32x4Abs, I32x4Neg, I32x4AllTrue, I32x4Bitmask, + I32x4ExtAddPairwiseI16x8S, I32x4ExtendLowI16x8S, I32x4ExtendHighI16x8S, I32x4ExtendLowI16x8U, + I32x4ExtendHighI16x8U, I32x4Shl, I32x4ShrS, I32x4ShrU, I32x4Add, I32x4Sub, I32x4Mul, + I32x4MinS, I32x4MinU, I32x4MaxS, I32x4MaxU, I32x4DotI16x8S, + I32x4ExtMulLowI16x8S, I32x4ExtMulHighI16x8S, I32x4ExtMulLowI16x8U, I32x4ExtMulHighI16x8U, + I64x2Abs, I64x2Neg, I64x2AllTrue, I64x2Bitmask, I64x2ExtendLowI32x4S, I64x2ExtendHighI32x4S, + I64x2ExtendLowI32x4U, I64x2ExtendHighI32x4U, I64x2Shl, I64x2ShrS, I64x2ShrU, I64x2Add, + I64x2ExtMulLowI32x4S, I64x2ExtMulHighI32x4S, I64x2Sub, I64x2Mul, + I64x2ExtMulLowI32x4U, I64x2ExtMulHighI32x4U, F32x4Ceil, F32x4Floor, F32x4Trunc, + F32x4Nearest, F32x4Abs, F32x4Neg, F32x4Sqrt, F32x4Add, F32x4Sub, F32x4Mul, F32x4Div, + F32x4Min, F32x4Max, F32x4PMin, F32x4PMax, F64x2Ceil, F64x2Floor, F64x2Trunc, + F64x2Nearest, F64x2Abs, F64x2Neg, F64x2Sqrt, F64x2Add, F64x2Sub, F64x2Mul, F64x2Div, F64x2Min, + F64x2Max, F64x2PMin, F64x2PMax, I32x4TruncSatF32x4S, I32x4TruncSatF32x4U, F32x4ConvertI32x4S, + F32x4ConvertI32x4U, I32x4TruncSatF64x2SZero, I32x4TruncSatF64x2UZero, F64x2ConvertLowI32x4S, + F64x2ConvertLowI32x4U, F32x4DemoteF64x2Zero, F64x2PromoteLowF32x4, I8x16RelaxedSwizzle, + I32x4RelaxedTruncF32x4S, I32x4RelaxedTruncF32x4U, I32x4RelaxedTruncF64x2SZero, + I32x4RelaxedTruncF64x2UZero, F32x4RelaxedMadd, F32x4RelaxedNmadd, F64x2RelaxedMadd, + F64x2RelaxedNmadd, I8x16RelaxedLaneselect, I16x8RelaxedLaneselect, I32x4RelaxedLaneselect, + I64x2RelaxedLaneselect, F32x4RelaxedMin, F32x4RelaxedMax, F64x2RelaxedMin, F64x2RelaxedMax, + I16x8RelaxedQ15mulrS, I16x8RelaxedDotI8x16I7x16S, I32x4RelaxedDotI8x16I7x16AddS + ) + ) => bail!("SIMD extension not supported {unsupported:?}"), + }; + } + + if self.locals.is_none() { + bail!("missing locals info for func {}", self.func.as_u32().red()) + } + + let contention = worst; + if contention > self.frame_contention.into() { + bail!( + "too many values on the stack at once in func {}: {} > {}", + self.func.as_u32().red(), + contention.red(), + self.frame_contention.red() + ); + } + + let locals = self.locals.unwrap_or_default(); + Ok(worst + locals as u32 + 4) + } +} + +/// Note: implementers may panic if uninstrumented +pub trait DepthCheckedMachine { + fn stack_left(&mut self) -> u32; + fn set_stack(&mut self, size: u32); +} + +impl DepthCheckedMachine for Machine { + fn stack_left(&mut self) -> u32 { + let global = self.get_global(STYLUS_STACK_LEFT).unwrap(); + global.try_into().expect("instrumentation type mismatch") + } + + fn set_stack(&mut self, size: u32) { + self.set_global(STYLUS_STACK_LEFT, size.into()).unwrap(); + } +} diff --git a/arbitrator/prover/src/programs/dynamic.rs b/arbitrator/prover/src/programs/dynamic.rs new file mode 100644 index 0000000000..36f49af856 --- /dev/null +++ b/arbitrator/prover/src/programs/dynamic.rs @@ -0,0 +1,154 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use super::{ + config::CompilePricingParams, + meter::{STYLUS_INK_LEFT, STYLUS_INK_STATUS}, + FuncMiddleware, Middleware, ModuleMod, +}; +use eyre::{bail, Result}; +use parking_lot::RwLock; +use wasmer_types::{GlobalIndex, GlobalInit, LocalFunctionIndex, Type}; +use wasmparser::{BlockType, Operator}; + +pub const SCRATCH_GLOBAL: &str = "stylus_scratch_global"; + +#[derive(Debug)] +pub struct DynamicMeter { + memory_fill: u64, + memory_copy: u64, + globals: RwLock>, +} + +impl DynamicMeter { + pub fn new(pricing: &CompilePricingParams) -> Self { + Self { + memory_fill: pricing.memory_fill_ink, + memory_copy: pricing.memory_copy_ink, + globals: RwLock::default(), + } + } +} + +impl Middleware for DynamicMeter { + type FM<'a> = FuncDynamicMeter; + + fn update_module(&self, module: &mut M) -> Result<()> { + let ink = module.get_global(STYLUS_INK_LEFT)?; + let status = module.get_global(STYLUS_INK_STATUS)?; + let scratch = module.add_global(SCRATCH_GLOBAL, Type::I32, GlobalInit::I32Const(0))?; + *self.globals.write() = Some([ink, status, scratch]); + Ok(()) + } + + fn instrument<'a>(&self, _: LocalFunctionIndex) -> Result> { + let globals = self.globals.read().expect("no globals"); + Ok(FuncDynamicMeter::new( + self.memory_fill, + self.memory_copy, + globals, + )) + } + + fn name(&self) -> &'static str { + "dynamic ink meter" + } +} + +#[derive(Debug)] +pub struct FuncDynamicMeter { + memory_fill: u64, + memory_copy: u64, + globals: [GlobalIndex; 3], +} + +impl FuncDynamicMeter { + fn new(memory_fill: u64, memory_copy: u64, globals: [GlobalIndex; 3]) -> Self { + Self { + memory_fill, + memory_copy, + globals, + } + } +} + +impl<'a> FuncMiddleware<'a> for FuncDynamicMeter { + fn feed(&mut self, op: Operator<'a>, out: &mut O) -> Result<()> + where + O: Extend>, + { + use Operator::*; + macro_rules! dot { + ($first:ident $(,$opcode:ident)* $(,)?) => { + $first { .. } $(| $opcode { .. })* + }; + } + macro_rules! get { + ($global:expr) => { + GlobalGet { + global_index: $global, + } + }; + } + macro_rules! set { + ($global:expr) => { + GlobalSet { + global_index: $global, + } + }; + } + + let [ink, status, scratch] = self.globals.map(|x| x.as_u32()); + let blockty = BlockType::Empty; + + #[rustfmt::skip] + let linear = |coefficient| { + [ + // [user] → move user value to scratch + set!(scratch), + get!(ink), + get!(ink), + get!(scratch), + + // [ink ink size] → cost = size * coefficient (can't overflow) + I64ExtendI32U, + I64Const { value: coefficient }, + I64Mul, + + // [ink ink cost] → ink -= cost + I64Sub, + set!(ink), + get!(ink), + + // [old_ink, new_ink] → (old_ink < new_ink) (overflow detected) + I64LtU, + If { blockty }, + I32Const { value: 1 }, + set!(status), + Unreachable, + End, + + // [] → resume since user paid for ink + get!(scratch), + ] + }; + + match op { + dot!(MemoryFill) => out.extend(linear(self.memory_fill as i64)), + dot!(MemoryCopy) => out.extend(linear(self.memory_copy as i64)), + dot!( + MemoryInit, DataDrop, ElemDrop, TableInit, TableCopy, TableFill, TableGet, + TableSet, TableGrow, TableSize + ) => { + bail!("opcode not supported") + } + _ => {} + } + out.extend([op]); + Ok(()) + } + + fn name(&self) -> &'static str { + "dynamic ink meter" + } +} diff --git a/arbitrator/prover/src/programs/heap.rs b/arbitrator/prover/src/programs/heap.rs new file mode 100644 index 0000000000..310405ec95 --- /dev/null +++ b/arbitrator/prover/src/programs/heap.rs @@ -0,0 +1,117 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::value::{ArbValueType, FunctionType}; + +use super::{ + config::CompileMemoryParams, dynamic::SCRATCH_GLOBAL, FuncMiddleware, Middleware, ModuleMod, +}; +use arbutil::Color; +use eyre::{bail, Result}; +use parking_lot::RwLock; +use wasmer_types::{FunctionIndex, GlobalIndex, ImportIndex, LocalFunctionIndex, Pages}; +use wasmparser::Operator; + +#[derive(Debug)] +pub struct HeapBound { + /// Upper bounds the amount of heap memory a module may use + limit: Pages, + /// Import called when allocating new pages + pay_func: RwLock>, + /// Scratch global shared among middlewares + scratch: RwLock>, +} + +impl HeapBound { + const PAY_FUNC: &'static str = "pay_for_memory_grow"; + + pub fn new(bounds: CompileMemoryParams) -> Self { + Self { + limit: bounds.heap_bound, + pay_func: RwLock::default(), + scratch: RwLock::default(), + } + } +} + +impl Middleware for HeapBound { + type FM<'a> = FuncHeapBound; + + fn update_module(&self, module: &mut M) -> Result<()> { + let scratch = module.get_global(SCRATCH_GLOBAL)?; + *self.scratch.write() = Some(scratch); + + let memory = module.memory_info()?; + let min = memory.min; + let max = memory.max; + let lim = self.limit; + + if min > lim { + bail!("memory size {} exceeds bound {}", min.0.red(), lim.0.red()); + } + if max == Some(min) { + return Ok(()); + } + + let ImportIndex::Function(import) = module.get_import("vm_hooks", Self::PAY_FUNC)? else { + bail!("wrong import kind for {}", Self::PAY_FUNC.red()); + }; + + let ty = module.get_function(import)?; + if ty != FunctionType::new(vec![ArbValueType::I32], vec![]) { + bail!("wrong type for {}: {}", Self::PAY_FUNC.red(), ty.red()); + } + + *self.pay_func.write() = Some(import); + Ok(()) + } + + fn instrument<'a>(&self, _: LocalFunctionIndex) -> Result> { + Ok(FuncHeapBound { + scratch: self.scratch.read().expect("no scratch global"), + pay_func: *self.pay_func.read(), + }) + } + + fn name(&self) -> &'static str { + "heap bound" + } +} + +#[derive(Debug)] +pub struct FuncHeapBound { + pay_func: Option, + scratch: GlobalIndex, +} + +impl<'a> FuncMiddleware<'a> for FuncHeapBound { + fn feed(&mut self, op: Operator<'a>, out: &mut O) -> Result<()> + where + O: Extend>, + { + use Operator::*; + + let Some(pay_func) = self.pay_func else { + out.extend([op]); + return Ok(()); + }; + + let global_index = self.scratch.as_u32(); + let function_index = pay_func.as_u32(); + + if let MemoryGrow { .. } = op { + out.extend([ + GlobalSet { global_index }, + GlobalGet { global_index }, + GlobalGet { global_index }, + Call { function_index }, + ]); + } + out.extend([op]); + Ok(()) + } + + fn name(&self) -> &'static str { + "heap bound" + } +} diff --git a/arbitrator/prover/src/programs/memory.rs b/arbitrator/prover/src/programs/memory.rs new file mode 100644 index 0000000000..7253b59dc4 --- /dev/null +++ b/arbitrator/prover/src/programs/memory.rs @@ -0,0 +1,125 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct MemoryModel { + /// Number of pages a tx gets for free + pub free_pages: u16, + /// Base cost of each additional wasm page + pub page_gas: u16, +} + +impl Default for MemoryModel { + fn default() -> Self { + Self { + free_pages: u16::MAX, + page_gas: 0, + } + } +} + +impl MemoryModel { + pub const fn new(free_pages: u16, page_gas: u16) -> Self { + Self { + free_pages, + page_gas, + } + } + + /// Determines the gas cost of allocating `new` pages given `open` are active and `ever` have ever been. + pub fn gas_cost(&self, new: u16, open: u16, ever: u16) -> u64 { + let new_open = open.saturating_add(new); + let new_ever = ever.max(new_open); + + // free until expansion beyond the first few + if new_ever <= self.free_pages { + return 0; + } + + let credit = |pages: u16| pages.saturating_sub(self.free_pages); + let adding = credit(new_open).saturating_sub(credit(open)) as u64; + let linear = adding.saturating_mul(self.page_gas.into()); + let expand = Self::exp(new_ever) - Self::exp(ever); + linear.saturating_add(expand) + } + + fn exp(pages: u16) -> u64 { + MEMORY_EXPONENTS + .get(pages as usize) + .map(|&x| x.into()) + .unwrap_or(u64::MAX) + } +} + +const MEMORY_EXPONENTS: [u32; 129] = [ + 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 14, 17, 19, 22, 25, 29, 33, 38, + 43, 50, 57, 65, 75, 85, 98, 112, 128, 147, 168, 193, 221, 253, 289, 331, 379, 434, 497, 569, + 651, 745, 853, 976, 1117, 1279, 1463, 1675, 1917, 2194, 2511, 2874, 3290, 3765, 4309, 4932, + 5645, 6461, 7395, 8464, 9687, 11087, 12689, 14523, 16621, 19024, 21773, 24919, 28521, 32642, + 37359, 42758, 48938, 56010, 64104, 73368, 83971, 96106, 109994, 125890, 144082, 164904, 188735, + 216010, 247226, 282953, 323844, 370643, 424206, 485509, 555672, 635973, 727880, 833067, 953456, + 1091243, 1248941, 1429429, 1636000, 1872423, 2143012, 2452704, 2807151, 3212820, 3677113, + 4208502, 4816684, 5512756, 6309419, 7221210, 8264766, 9459129, 10826093, 12390601, 14181199, + 16230562, 18576084, 21260563, 24332984, 27849408, 31873999, +]; + +#[test] +fn test_tables() { + let base = f64::exp(31_874_000_f64.ln() / 128.); + + for pages in 0..129 { + let value = base.powi(pages.into()) as u64; + assert_eq!(value, MemoryModel::exp(pages)); + } + assert_eq!(u64::MAX, MemoryModel::exp(129)); + assert_eq!(u64::MAX, MemoryModel::exp(u16::MAX)); +} + +#[test] +fn test_model() { + let model = MemoryModel::new(2, 1000); + + for jump in 1..=128 { + let mut total = 0; + let mut pages = 0; + while pages < 128 { + let jump = jump.min(128 - pages); + total += model.gas_cost(jump, pages, pages); + pages += jump; + } + assert_eq!(total, 31999998); + } + + for jump in 1..=128 { + let mut total = 0; + let mut open = 0; + let mut ever = 0; + let mut adds = 0; + while ever < 128 { + let jump = jump.min(128 - open); + total += model.gas_cost(jump, open, ever); + open += jump; + ever = ever.max(open); + + if ever > model.free_pages { + adds += jump.min(ever - model.free_pages) as u64; + } + + // pretend we've deallocated some pages + open -= jump / 2; + } + let expected = 31873998 + adds * model.page_gas as u64; + assert_eq!(total, expected); + } + + // check saturation + assert_eq!(u64::MAX, model.gas_cost(129, 0, 0)); + assert_eq!(u64::MAX, model.gas_cost(u16::MAX, 0, 0)); + + // check free pages + let model = MemoryModel::new(128, 1000); + assert_eq!(0, model.gas_cost(128, 0, 0)); + assert_eq!(0, model.gas_cost(128, 0, 128)); + assert_eq!(u64::MAX, model.gas_cost(129, 0, 0)); +} diff --git a/arbitrator/prover/src/programs/meter.rs b/arbitrator/prover/src/programs/meter.rs new file mode 100644 index 0000000000..ab069fd911 --- /dev/null +++ b/arbitrator/prover/src/programs/meter.rs @@ -0,0 +1,532 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use crate::{ + programs::{ + config::{CompilePricingParams, PricingParams, SigMap}, + FuncMiddleware, Middleware, ModuleMod, + }, + value::FunctionType, + Machine, +}; +use arbutil::{evm, operator::OperatorInfo, Bytes32}; +use derivative::Derivative; +use eyre::Result; +use fnv::FnvHashMap as HashMap; +use parking_lot::RwLock; +use std::{ + fmt::{Debug, Display}, + sync::Arc, +}; +use wasmer_types::{GlobalIndex, GlobalInit, LocalFunctionIndex, SignatureIndex, Type}; +use wasmparser::{BlockType, Operator}; + +use super::config::OpCosts; + +pub const STYLUS_INK_LEFT: &str = "stylus_ink_left"; +pub const STYLUS_INK_STATUS: &str = "stylus_ink_status"; + +pub trait OpcodePricer: Fn(&Operator, &SigMap) -> u64 + Send + Sync + Clone {} + +impl OpcodePricer for T where T: Fn(&Operator, &SigMap) -> u64 + Send + Sync + Clone {} + +#[derive(Derivative)] +#[derivative(Debug)] +pub struct Meter { + /// Associates opcodes to their ink costs. + #[derivative(Debug = "ignore")] + costs: F, + /// Cost of checking the amount of ink left. + header_cost: u64, + /// Ink and ink status globals. + globals: RwLock>, + /// The types of the module being instrumented + sigs: RwLock>>, +} + +impl Meter { + pub fn new(pricing: &CompilePricingParams) -> Meter { + Self { + costs: pricing.costs, + header_cost: pricing.ink_header_cost, + globals: RwLock::default(), + sigs: RwLock::default(), + } + } +} + +impl Meter { + pub fn globals(&self) -> [GlobalIndex; 2] { + self.globals.read().expect("missing globals") + } +} + +impl Middleware for Meter +where + M: ModuleMod, + F: OpcodePricer + 'static, +{ + type FM<'a> = FuncMeter<'a, F>; + + fn update_module(&self, module: &mut M) -> Result<()> { + let start_status = GlobalInit::I32Const(0); + let ink = module.add_global(STYLUS_INK_LEFT, Type::I64, GlobalInit::I64Const(0))?; + let status = module.add_global(STYLUS_INK_STATUS, Type::I32, start_status)?; + *self.globals.write() = Some([ink, status]); + *self.sigs.write() = Some(Arc::new(module.all_signatures()?)); + Ok(()) + } + + fn instrument<'a>(&self, _: LocalFunctionIndex) -> Result> { + let [ink, status] = self.globals(); + let sigs = self.sigs.read(); + let sigs = sigs.as_ref().expect("no types"); + Ok(FuncMeter::new( + ink, + status, + self.costs.clone(), + self.header_cost, + sigs.clone(), + )) + } + + fn name(&self) -> &'static str { + "ink meter" + } +} + +#[derive(Derivative)] +#[derivative(Debug)] +pub struct FuncMeter<'a, F: OpcodePricer> { + /// Represents the amount of ink left for consumption. + ink_global: GlobalIndex, + /// Represents whether the machine is out of ink. + status_global: GlobalIndex, + /// Instructions of the current basic block. + block: Vec>, + /// The accumulated cost of the current basic block. + block_cost: u64, + /// Cost of checking the amount of ink left. + header_cost: u64, + /// Associates opcodes to their ink costs. + #[derivative(Debug = "ignore")] + costs: F, + /// The types of the module being instrumented. + sigs: Arc, +} + +impl<'a, F: OpcodePricer> FuncMeter<'a, F> { + fn new( + ink_global: GlobalIndex, + status_global: GlobalIndex, + costs: F, + header_cost: u64, + sigs: Arc, + ) -> Self { + Self { + ink_global, + status_global, + block: vec![], + block_cost: 0, + header_cost, + costs, + sigs, + } + } +} + +impl<'a, F: OpcodePricer> FuncMiddleware<'a> for FuncMeter<'a, F> { + fn feed(&mut self, op: Operator<'a>, out: &mut O) -> Result<()> + where + O: Extend>, + { + use Operator::*; + + let end = op.ends_basic_block(); + + let op_cost = (self.costs)(&op, &self.sigs); + let mut cost = self.block_cost.saturating_add(op_cost); + self.block_cost = cost; + self.block.push(op); + + if end { + let ink = self.ink_global.as_u32(); + let status = self.status_global.as_u32(); + let blockty = BlockType::Empty; + + // include the cost of executing the header + cost = cost.saturating_add(self.header_cost); + + out.extend([ + // if ink < cost => panic with status = 1 + GlobalGet { global_index: ink }, + I64Const { value: cost as i64 }, + I64LtU, + If { blockty }, + I32Const { value: 1 }, + GlobalSet { + global_index: status, + }, + Unreachable, + End, + // ink -= cost + GlobalGet { global_index: ink }, + I64Const { value: cost as i64 }, + I64Sub, + GlobalSet { global_index: ink }, + ]); + out.extend(self.block.drain(..)); + self.block_cost = 0; + } + Ok(()) + } + + fn name(&self) -> &'static str { + "ink meter" + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MachineMeter { + Ready(u64), + Exhausted, +} + +impl MachineMeter { + pub fn ink(self) -> u64 { + match self { + Self::Ready(ink) => ink, + Self::Exhausted => 0, + } + } + + pub fn status(self) -> u32 { + match self { + Self::Ready(_) => 0, + Self::Exhausted => 1, + } + } +} + +/// We don't implement `From` since it's unclear what 0 would map to +#[allow(clippy::from_over_into)] +impl Into for MachineMeter { + fn into(self) -> u64 { + self.ink() + } +} + +impl Display for MachineMeter { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Ready(ink) => write!(f, "{ink} ink"), + Self::Exhausted => write!(f, "exhausted"), + } + } +} + +#[derive(Debug)] +pub struct OutOfInkError; + +impl std::error::Error for OutOfInkError {} + +impl Display for OutOfInkError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "out of ink") + } +} + +/// Note: implementers may panic if uninstrumented +pub trait MeteredMachine { + fn ink_left(&self) -> MachineMeter; + fn set_meter(&mut self, meter: MachineMeter); + + fn set_ink(&mut self, ink: u64) { + self.set_meter(MachineMeter::Ready(ink)); + } + + fn out_of_ink(&mut self) -> Result { + self.set_meter(MachineMeter::Exhausted); + Err(OutOfInkError) + } + + fn ink_ready(&mut self) -> Result { + let MachineMeter::Ready(ink_left) = self.ink_left() else { + return self.out_of_ink(); + }; + Ok(ink_left) + } + + fn buy_ink(&mut self, ink: u64) -> Result<(), OutOfInkError> { + let ink_left = self.ink_ready()?; + if ink_left < ink { + return self.out_of_ink(); + } + self.set_ink(ink_left - ink); + Ok(()) + } + + /// Checks if the user has enough ink, but doesn't burn any + fn require_ink(&mut self, ink: u64) -> Result<(), OutOfInkError> { + let ink_left = self.ink_ready()?; + if ink_left < ink { + return self.out_of_ink(); + } + Ok(()) + } + + /// Pays for a write into the client. + fn pay_for_write(&mut self, bytes: u32) -> Result<(), OutOfInkError> { + self.buy_ink(sat_add_mul(5040, 30, bytes.saturating_sub(32))) + } + + /// Pays for a read into the host. + fn pay_for_read(&mut self, bytes: u32) -> Result<(), OutOfInkError> { + self.buy_ink(sat_add_mul(16381, 55, bytes.saturating_sub(32))) + } + + /// Pays for both I/O and keccak. + fn pay_for_keccak(&mut self, bytes: u32) -> Result<(), OutOfInkError> { + let words = evm::evm_words(bytes).saturating_sub(2); + self.buy_ink(sat_add_mul(121800, 21000, words)) + } + + /// Pays for copying bytes from geth. + fn pay_for_geth_bytes(&mut self, bytes: u32) -> Result<(), OutOfInkError> { + self.pay_for_read(bytes) // TODO: determine value + } + + /// Pays for the variable costs of exponentiation. + fn pay_for_pow(&mut self, exponent: &Bytes32) -> Result<(), OutOfInkError> { + let mut exp = 33; + for byte in exponent.iter() { + match *byte == 0 { + true => exp -= 1, // reduce cost for each big-endian 0 byte + false => break, + } + } + self.buy_ink(3000 + exp * 17500) + } +} + +pub trait GasMeteredMachine: MeteredMachine { + fn pricing(&self) -> PricingParams; + + fn gas_left(&self) -> Result { + let pricing = self.pricing(); + match self.ink_left() { + MachineMeter::Ready(ink) => Ok(pricing.ink_to_gas(ink)), + MachineMeter::Exhausted => Err(OutOfInkError), + } + } + + fn buy_gas(&mut self, gas: u64) -> Result<(), OutOfInkError> { + let pricing = self.pricing(); + self.buy_ink(pricing.gas_to_ink(gas)) + } + + /// Checks if the user has enough gas, but doesn't burn any + fn require_gas(&mut self, gas: u64) -> Result<(), OutOfInkError> { + let pricing = self.pricing(); + self.require_ink(pricing.gas_to_ink(gas)) + } + + fn pay_for_evm_log(&mut self, topics: u32, data_len: u32) -> Result<(), OutOfInkError> { + let cost = (1 + topics as u64) * evm::LOG_TOPIC_GAS; + let cost = cost.saturating_add(data_len as u64 * evm::LOG_DATA_GAS); + self.buy_gas(cost) + } +} + +fn sat_add_mul(base: u64, per: u64, count: u32) -> u64 { + base.saturating_add(per.saturating_mul(count.into())) +} + +impl MeteredMachine for Machine { + fn ink_left(&self) -> MachineMeter { + macro_rules! convert { + ($global:expr) => {{ + $global.unwrap().try_into().expect("type mismatch") + }}; + } + + let ink = || convert!(self.get_global(STYLUS_INK_LEFT)); + let status: u32 = convert!(self.get_global(STYLUS_INK_STATUS)); + + match status { + 0 => MachineMeter::Ready(ink()), + _ => MachineMeter::Exhausted, + } + } + + fn set_meter(&mut self, meter: MachineMeter) { + let ink = meter.ink(); + let status = meter.status(); + self.set_global(STYLUS_INK_LEFT, ink.into()).unwrap(); + self.set_global(STYLUS_INK_STATUS, status.into()).unwrap(); + } +} + +pub fn pricing_v1(op: &Operator, tys: &HashMap) -> u64 { + use Operator::*; + + macro_rules! op { + ($first:ident $(,$opcode:ident)*) => { + $first $(| $opcode)* + }; + } + macro_rules! dot { + ($first:ident $(,$opcode:ident)*) => { + $first { .. } $(| $opcode { .. })* + }; + } + + #[rustfmt::skip] + let ink = match op { + op!(Unreachable, Return) => 1, + op!(Nop) | dot!(I32Const, I64Const) => 1, + + op!(Drop) => 9, // could be 1, but using a higher number helps limit the number of ops in BOLD + + dot!(Block, Loop) | op!(Else, End) => 1, + dot!(Br, BrIf, If) => 765, + dot!(Select) => 1250, // TODO: improve wasmer codegen + dot!(Call) => 3800, + dot!(LocalGet, LocalTee) => 75, + dot!(LocalSet) => 210, + dot!(GlobalGet) => 225, + dot!(GlobalSet) => 575, + dot!(I32Load, I32Load8S, I32Load8U, I32Load16S, I32Load16U) => 670, + dot!(I64Load, I64Load8S, I64Load8U, I64Load16S, I64Load16U, I64Load32S, I64Load32U) => 680, + dot!(I32Store, I32Store8, I32Store16) => 825, + dot!(I64Store, I64Store8, I64Store16, I64Store32) => 950, + dot!(MemorySize) => 3000, + dot!(MemoryGrow) => 8050, // rest of cost handled by memory pricer + + op!(I32Eqz, I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU) => 170, + op!(I64Eqz, I64Eq, I64Ne, I64LtS, I64LtU, I64GtS, I64GtU, I64LeS, I64LeU, I64GeS, I64GeU) => 225, + + op!(I32Clz, I32Ctz) => 210, + op!(I32Add, I32Sub) => 70, + op!(I32Mul) => 160, + op!(I32DivS, I32DivU, I32RemS, I32RemU) => 1120, + op!(I32And, I32Or, I32Xor, I32Shl, I32ShrS, I32ShrU, I32Rotl, I32Rotr) => 70, + + op!(I64Clz, I64Ctz) => 210, + op!(I64Add, I64Sub) => 100, + op!(I64Mul) => 160, + op!(I64DivS, I64DivU, I64RemS, I64RemU) => 1270, + op!(I64And, I64Or, I64Xor, I64Shl, I64ShrS, I64ShrU, I64Rotl, I64Rotr) => 100, + + op!(I32Popcnt) => 2650, // slow on ARM, fast on x86 + op!(I64Popcnt) => 6000, // slow on ARM, fast on x86 + + op!(I32WrapI64, I64ExtendI32S, I64ExtendI32U) => 100, + op!(I32Extend8S, I32Extend16S, I64Extend8S, I64Extend16S, I64Extend32S) => 100, + dot!(MemoryCopy) => 950, + dot!(MemoryFill) => 950, + + BrTable { targets } => { + 2400 + 325 * targets.len() as u64 + }, + CallIndirect { type_index, .. } => { + let ty = tys.get(&SignatureIndex::from_u32(*type_index)).expect("no type"); + 13610 + 650 * ty.inputs.len() as u64 + }, + + // we don't support the following, so return u64::MAX + dot!( + Try, Catch, CatchAll, Delegate, Throw, Rethrow, ThrowRef, TryTable, + + RefNull, RefIsNull, RefFunc, RefEq, + + CallRef, ReturnCallRef, RefAsNonNull, BrOnNull, BrOnNonNull, + + TypedSelect, ReturnCall, ReturnCallIndirect, + + MemoryInit, DataDrop, TableInit, ElemDrop, + TableCopy, TableFill, TableGet, TableSet, TableGrow, TableSize, + + MemoryDiscard, + + StructNew, StructNewDefault, StructGet, StructGetS, StructGetU, StructSet, + ArrayNew, ArrayNewDefault, ArrayNewFixed, ArrayNewData, ArrayNewElem, + ArrayGet, ArrayGetS, ArrayGetU, ArraySet, ArrayLen, ArrayFill, ArrayCopy, + ArrayInitData, ArrayInitElem, RefTestNonNull, RefTestNullable, RefCastNonNull, RefCastNullable, + BrOnCast, BrOnCastFail, AnyConvertExtern, ExternConvertAny, RefI31, I31GetS, I31GetU, + + F32Load, F64Load, F32Store, F64Store, F32Const, F64Const, + F32Eq, F32Ne, F32Lt, F32Gt, F32Le, F32Ge, + F64Eq, F64Ne, F64Lt, F64Gt, F64Le, F64Ge, + F32Abs, F32Neg, F32Ceil, F32Floor, F32Trunc, F32Nearest, F32Sqrt, F32Add, F32Sub, F32Mul, + F32Div, F32Min, F32Max, F32Copysign, F64Abs, F64Neg, F64Ceil, F64Floor, F64Trunc, + F64Nearest, F64Sqrt, F64Add, F64Sub, F64Mul, F64Div, F64Min, F64Max, F64Copysign, + I32TruncF32S, I32TruncF32U, I32TruncF64S, I32TruncF64U, + I64TruncF32S, I64TruncF32U, I64TruncF64S, I64TruncF64U, + F32ConvertI32S, F32ConvertI32U, F32ConvertI64S, F32ConvertI64U, F32DemoteF64, + F64ConvertI32S, F64ConvertI32U, F64ConvertI64S, F64ConvertI64U, F64PromoteF32, + I32ReinterpretF32, I64ReinterpretF64, F32ReinterpretI32, F64ReinterpretI64, + I32TruncSatF32S, I32TruncSatF32U, I32TruncSatF64S, I32TruncSatF64U, + I64TruncSatF32S, I64TruncSatF32U, I64TruncSatF64S, I64TruncSatF64U, + + MemoryAtomicNotify, MemoryAtomicWait32, MemoryAtomicWait64, AtomicFence, I32AtomicLoad, + I64AtomicLoad, I32AtomicLoad8U, I32AtomicLoad16U, I64AtomicLoad8U, I64AtomicLoad16U, + I64AtomicLoad32U, I32AtomicStore, I64AtomicStore, I32AtomicStore8, I32AtomicStore16, + I64AtomicStore8, I64AtomicStore16, I64AtomicStore32, I32AtomicRmwAdd, I64AtomicRmwAdd, + I32AtomicRmw8AddU, I32AtomicRmw16AddU, I64AtomicRmw8AddU, I64AtomicRmw16AddU, I64AtomicRmw32AddU, + I32AtomicRmwSub, I64AtomicRmwSub, I32AtomicRmw8SubU, I32AtomicRmw16SubU, I64AtomicRmw8SubU, + I64AtomicRmw16SubU, I64AtomicRmw32SubU, I32AtomicRmwAnd, I64AtomicRmwAnd, I32AtomicRmw8AndU, + I32AtomicRmw16AndU, I64AtomicRmw8AndU, I64AtomicRmw16AndU, I64AtomicRmw32AndU, I32AtomicRmwOr, + I64AtomicRmwOr, I32AtomicRmw8OrU, I32AtomicRmw16OrU, I64AtomicRmw8OrU, I64AtomicRmw16OrU, + I64AtomicRmw32OrU, I32AtomicRmwXor, I64AtomicRmwXor, I32AtomicRmw8XorU, I32AtomicRmw16XorU, + I64AtomicRmw8XorU, I64AtomicRmw16XorU, I64AtomicRmw32XorU, I32AtomicRmwXchg, I64AtomicRmwXchg, + I32AtomicRmw8XchgU, I32AtomicRmw16XchgU, I64AtomicRmw8XchgU, I64AtomicRmw16XchgU, + I64AtomicRmw32XchgU, I32AtomicRmwCmpxchg, I64AtomicRmwCmpxchg, I32AtomicRmw8CmpxchgU, + I32AtomicRmw16CmpxchgU, I64AtomicRmw8CmpxchgU, I64AtomicRmw16CmpxchgU, I64AtomicRmw32CmpxchgU, + + V128Load, V128Load8x8S, V128Load8x8U, V128Load16x4S, V128Load16x4U, V128Load32x2S, V128Load32x2U, + V128Load8Splat, V128Load16Splat, V128Load32Splat, V128Load64Splat, V128Load32Zero, V128Load64Zero, + V128Store, V128Load8Lane, V128Load16Lane, V128Load32Lane, V128Load64Lane, V128Store8Lane, + V128Store16Lane, V128Store32Lane, V128Store64Lane, V128Const, + I8x16Shuffle, I8x16ExtractLaneS, I8x16ExtractLaneU, I8x16ReplaceLane, I16x8ExtractLaneS, + I16x8ExtractLaneU, I16x8ReplaceLane, I32x4ExtractLane, I32x4ReplaceLane, I64x2ExtractLane, + I64x2ReplaceLane, F32x4ExtractLane, F32x4ReplaceLane, F64x2ExtractLane, F64x2ReplaceLane, + I8x16Swizzle, I8x16Splat, I16x8Splat, I32x4Splat, I64x2Splat, F32x4Splat, F64x2Splat, I8x16Eq, + I8x16Ne, I8x16LtS, I8x16LtU, I8x16GtS, I8x16GtU, I8x16LeS, I8x16LeU, I8x16GeS, I8x16GeU, I16x8Eq, + I16x8Ne, I16x8LtS, I16x8LtU, I16x8GtS, I16x8GtU, I16x8LeS, I16x8LeU, I16x8GeS, I16x8GeU, I32x4Eq, + I32x4Ne, I32x4LtS, I32x4LtU, I32x4GtS, I32x4GtU, I32x4LeS, I32x4LeU, I32x4GeS, I32x4GeU, I64x2Eq, + I64x2Ne, I64x2LtS, I64x2GtS, I64x2LeS, I64x2GeS, + F32x4Eq, F32x4Ne, F32x4Lt, F32x4Gt, F32x4Le, F32x4Ge, + F64x2Eq, F64x2Ne, F64x2Lt, F64x2Gt, F64x2Le, F64x2Ge, + V128Not, V128And, V128AndNot, V128Or, V128Xor, V128Bitselect, V128AnyTrue, + I8x16Abs, I8x16Neg, I8x16Popcnt, I8x16AllTrue, I8x16Bitmask, I8x16NarrowI16x8S, I8x16NarrowI16x8U, + I8x16Shl, I8x16ShrS, I8x16ShrU, I8x16Add, I8x16AddSatS, I8x16AddSatU, I8x16Sub, I8x16SubSatS, + I8x16SubSatU, I8x16MinS, I8x16MinU, I8x16MaxS, I8x16MaxU, I8x16AvgrU, + I16x8ExtAddPairwiseI8x16S, I16x8ExtAddPairwiseI8x16U, I16x8Abs, I16x8Neg, I16x8Q15MulrSatS, + I16x8AllTrue, I16x8Bitmask, I16x8NarrowI32x4S, I16x8NarrowI32x4U, I16x8ExtendLowI8x16S, + I16x8ExtendHighI8x16S, I16x8ExtendLowI8x16U, I16x8ExtendHighI8x16U, I16x8Shl, I16x8ShrS, I16x8ShrU, + I16x8Add, I16x8AddSatS, I16x8AddSatU, I16x8Sub, I16x8SubSatS, I16x8SubSatU, I16x8Mul, I16x8MinS, + I16x8MinU, I16x8MaxS, I16x8MaxU, I16x8AvgrU, I16x8ExtMulLowI8x16S, + I16x8ExtMulHighI8x16S, I16x8ExtMulLowI8x16U, I16x8ExtMulHighI8x16U, I32x4ExtAddPairwiseI16x8S, + I32x4ExtAddPairwiseI16x8U, I32x4Abs, I32x4Neg, I32x4AllTrue, I32x4Bitmask, I32x4ExtendLowI16x8S, + I32x4ExtendHighI16x8S, I32x4ExtendLowI16x8U, I32x4ExtendHighI16x8U, I32x4Shl, I32x4ShrS, I32x4ShrU, + I32x4Add, I32x4Sub, I32x4Mul, I32x4MinS, I32x4MinU, I32x4MaxS, I32x4MaxU, I32x4DotI16x8S, + I32x4ExtMulLowI16x8S, I32x4ExtMulHighI16x8S, I32x4ExtMulLowI16x8U, I32x4ExtMulHighI16x8U, I64x2Abs, + I64x2Neg, I64x2AllTrue, I64x2Bitmask, I64x2ExtendLowI32x4S, I64x2ExtendHighI32x4S, + I64x2ExtendLowI32x4U, I64x2ExtendHighI32x4U, I64x2Shl, I64x2ShrS, I64x2ShrU, I64x2Add, I64x2Sub, + I64x2Mul, I64x2ExtMulLowI32x4S, I64x2ExtMulHighI32x4S, I64x2ExtMulLowI32x4U, I64x2ExtMulHighI32x4U, + F32x4Ceil, F32x4Floor, F32x4Trunc, F32x4Nearest, F32x4Abs, F32x4Neg, F32x4Sqrt, F32x4Add, F32x4Sub, + F32x4Mul, F32x4Div, F32x4Min, F32x4Max, F32x4PMin, F32x4PMax, F64x2Ceil, F64x2Floor, F64x2Trunc, + F64x2Nearest, F64x2Abs, F64x2Neg, F64x2Sqrt, F64x2Add, F64x2Sub, F64x2Mul, F64x2Div, F64x2Min, + F64x2Max, F64x2PMin, F64x2PMax, I32x4TruncSatF32x4S, I32x4TruncSatF32x4U, F32x4ConvertI32x4S, + F32x4ConvertI32x4U, I32x4TruncSatF64x2SZero, I32x4TruncSatF64x2UZero, F64x2ConvertLowI32x4S, + F64x2ConvertLowI32x4U, F32x4DemoteF64x2Zero, F64x2PromoteLowF32x4, I8x16RelaxedSwizzle, + I32x4RelaxedTruncF32x4S, I32x4RelaxedTruncF32x4U, I32x4RelaxedTruncF64x2SZero, + I32x4RelaxedTruncF64x2UZero, F32x4RelaxedMadd, F32x4RelaxedNmadd, F64x2RelaxedMadd, + F64x2RelaxedNmadd, I8x16RelaxedLaneselect, I16x8RelaxedLaneselect, I32x4RelaxedLaneselect, + I64x2RelaxedLaneselect, F32x4RelaxedMin, F32x4RelaxedMax, F64x2RelaxedMin, F64x2RelaxedMax, + I16x8RelaxedQ15mulrS, I16x8RelaxedDotI8x16I7x16S, I32x4RelaxedDotI8x16I7x16AddS + ) => u64::MAX, + }; + ink +} diff --git a/arbitrator/prover/src/programs/mod.rs b/arbitrator/prover/src/programs/mod.rs new file mode 100644 index 0000000000..a5df2e31a8 --- /dev/null +++ b/arbitrator/prover/src/programs/mod.rs @@ -0,0 +1,479 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{ + binary::{ExportKind, WasmBinary}, + machine::Module, + memory::MemoryType, + programs::config::CompileConfig, + value::{FunctionType as ArbFunctionType, Value}, +}; +use arbutil::{math::SaturatingSum, Bytes32, Color}; +use eyre::{bail, eyre, Report, Result, WrapErr}; +use fnv::FnvHashMap as HashMap; +use std::fmt::Debug; +use wasmer_types::{ + entity::EntityRef, FunctionIndex, GlobalIndex, GlobalInit, ImportIndex, LocalFunctionIndex, + SignatureIndex, Type, +}; +use wasmparser::{Operator, ValType}; + +#[cfg(feature = "native")] +use { + super::value, + std::marker::PhantomData, + wasmer::{ + ExportIndex, FunctionMiddleware, GlobalType, MiddlewareError, ModuleMiddleware, Mutability, + }, + wasmer_types::{MemoryIndex, ModuleInfo}, +}; + +pub mod config; +pub mod counter; +pub mod depth; +pub mod dynamic; +pub mod heap; +pub mod memory; +pub mod meter; +pub mod prelude; +pub mod start; + +pub const STYLUS_ENTRY_POINT: &str = "user_entrypoint"; + +pub trait ModuleMod { + fn add_global(&mut self, name: &str, ty: Type, init: GlobalInit) -> Result; + fn get_global(&mut self, name: &str) -> Result; + fn get_signature(&self, sig: SignatureIndex) -> Result; + fn get_function(&self, func: FunctionIndex) -> Result; + fn all_functions(&self) -> Result>; + fn all_signatures(&self) -> Result>; + fn get_import(&self, module: &str, name: &str) -> Result; + /// Moves the start function, returning true if present. + fn move_start_function(&mut self, name: &str) -> Result; + /// Drops debug-only info like export names. + fn drop_exports_and_names(&mut self, keep: &HashMap<&str, ExportKind>); + fn memory_info(&self) -> Result; +} + +pub trait Middleware { + type FM<'a>: FuncMiddleware<'a> + Debug; + + fn update_module(&self, module: &mut M) -> Result<()>; // not mutable due to wasmer + fn instrument<'a>(&self, func_index: LocalFunctionIndex) -> Result>; + fn name(&self) -> &'static str; +} + +pub trait FuncMiddleware<'a> { + /// Provide info on the function's locals. This is called before feed. + fn locals_info(&mut self, _locals: &[ValType]) {} + + /// Processes the given operator. + fn feed(&mut self, op: Operator<'a>, out: &mut O) -> Result<()> + where + O: Extend>; + + /// The name of the middleware + fn name(&self) -> &'static str; +} + +#[derive(Debug)] +pub struct DefaultFuncMiddleware; + +impl<'a> FuncMiddleware<'a> for DefaultFuncMiddleware { + fn feed(&mut self, op: Operator<'a>, out: &mut O) -> Result<()> + where + O: Extend>, + { + out.extend([op]); + Ok(()) + } + + fn name(&self) -> &'static str { + "default middleware" + } +} + +/// This wrapper exists to impl wasmer's `ModuleMiddleware` generically. +/// We can't use `T` directly since we don't define `ModuleMiddleware`, +/// and we need `M` to be part of the type. +#[cfg(feature = "native")] +#[derive(Debug)] +pub struct MiddlewareWrapper(pub T, PhantomData) +where + T: Middleware + Debug + Send + Sync, + M: ModuleMod; + +#[cfg(feature = "native")] +impl MiddlewareWrapper +where + T: Middleware + Debug + Send + Sync, + M: ModuleMod, +{ + pub fn new(middleware: T) -> Self { + Self(middleware, PhantomData) + } +} + +#[cfg(feature = "native")] +impl ModuleMiddleware for MiddlewareWrapper +where + T: Middleware + Debug + Send + Sync + 'static, +{ + fn transform_module_info(&self, module: &mut ModuleInfo) -> Result<(), MiddlewareError> { + let error = |err| MiddlewareError::new(self.0.name().red(), format!("{:?}", err)); + self.0.update_module(module).map_err(error) + } + + fn generate_function_middleware<'a>( + &self, + local_function_index: LocalFunctionIndex, + ) -> Box + 'a> { + let worker = self.0.instrument(local_function_index).unwrap(); + Box::new(FuncMiddlewareWrapper(worker, PhantomData)) + } +} + +/// This wrapper exists to impl wasmer's `FunctionMiddleware` generically. +/// The logic is analogous to that of `ModuleMiddleware`, except this time +/// we need a phantom marker to parameterize by `T`'s reference's lifetime. +#[cfg(feature = "native")] +#[derive(Debug)] +pub struct FuncMiddlewareWrapper<'a, T: 'a>(T, PhantomData<&'a T>) +where + T: FuncMiddleware<'a> + Debug; + +#[cfg(feature = "native")] +impl<'a, T> FunctionMiddleware<'a> for FuncMiddlewareWrapper<'a, T> +where + T: FuncMiddleware<'a> + Debug, +{ + fn locals_info(&mut self, locals: &[ValType]) { + self.0.locals_info(locals); + } + + fn feed( + &mut self, + op: Operator<'a>, + out: &mut wasmer::MiddlewareReaderState<'a>, + ) -> Result<(), MiddlewareError> { + let name = self.0.name().red(); + let error = |err| MiddlewareError::new(name, format!("{:?}", err)); + self.0.feed(op, out).map_err(error) + } +} + +#[cfg(feature = "native")] +impl ModuleMod for ModuleInfo { + fn add_global(&mut self, name: &str, ty: Type, init: GlobalInit) -> Result { + let global_type = GlobalType::new(ty, Mutability::Var); + let name = name.to_owned(); + if self.exports.contains_key(&name) { + bail!("wasm already contains {}", name.red()) + } + let index = self.globals.push(global_type); + self.exports.insert(name, ExportIndex::Global(index)); + self.global_initializers.push(init); + Ok(index) + } + + fn get_global(&mut self, name: &str) -> Result { + let Some(ExportIndex::Global(global)) = self.exports.get(name) else { + bail!("missing global {}", name.red()) + }; + Ok(*global) + } + + fn get_signature(&self, sig: SignatureIndex) -> Result { + let error = Report::msg(format!("missing signature {}", sig.as_u32().red())); + let ty = self.signatures.get(sig).cloned().ok_or(error)?; + let ty = value::parser_func_type(ty); + ty.try_into() + } + + fn get_function(&self, func: FunctionIndex) -> Result { + let index = func.as_u32(); + match self.functions.get(func) { + Some(sig) => self.get_signature(*sig), + None => match self.function_names.get(&func) { + Some(name) => bail!("missing func {} @ index {}", name.red(), index.red()), + None => bail!("missing func @ index {}", index.red()), + }, + } + } + + fn all_functions(&self) -> Result> { + let mut funcs = HashMap::default(); + for (func, sig) in &self.functions { + let ty = self.get_signature(*sig)?; + funcs.insert(func, ty); + } + Ok(funcs) + } + + fn all_signatures(&self) -> Result> { + let mut signatures = HashMap::default(); + for (index, _) in &self.signatures { + let ty = self.get_signature(index)?; + signatures.insert(index, ty); + } + Ok(signatures) + } + + fn get_import(&self, module: &str, name: &str) -> Result { + self.imports + .iter() + .find(|(k, _)| k.module == module && k.field == name) + .map(|(_, v)| v.clone()) + .ok_or_else(|| eyre!("missing import {}", name.red())) + } + + fn move_start_function(&mut self, name: &str) -> Result { + if let Some(prior) = self.exports.get(name) { + bail!("function {} already exists @ index {:?}", name.red(), prior) + } + + let start = self.start_function.take(); + if let Some(start) = start { + let export = ExportIndex::Function(start); + self.exports.insert(name.to_owned(), export); + self.function_names.insert(start, name.to_owned()); + } + Ok(start.is_some()) + } + + fn drop_exports_and_names(&mut self, keep: &HashMap<&str, ExportKind>) { + self.exports.retain(|name, export| { + keep.get(name.as_str()) + .map_or(false, |x| *x == (*export).into()) + }); + self.function_names.clear(); + } + + fn memory_info(&self) -> Result { + if self.memories.is_empty() { + bail!("missing memory export with name {}", "memory".red()); + } + if self.memories.len() > 1 { + bail!("only one memory is allowed"); + } + if self.exports.get("memory") != Some(&ExportIndex::Memory(MemoryIndex::from_u32(0))) { + bail!("missing memory with export name {}", "memory".red()); + } + Ok(self.memories.last().unwrap().into()) + } +} + +impl<'a> ModuleMod for WasmBinary<'a> { + fn add_global(&mut self, name: &str, _ty: Type, init: GlobalInit) -> Result { + let global = match init { + GlobalInit::I32Const(x) => Value::I32(x as u32), + GlobalInit::I64Const(x) => Value::I64(x as u64), + GlobalInit::F32Const(x) => Value::F32(x), + GlobalInit::F64Const(x) => Value::F64(x), + ty => bail!("cannot add global of type {:?}", ty), + }; + if self.exports.contains_key(name) { + bail!("wasm already contains {}", name.red()) + } + let name = name.to_owned(); + let index = self.globals.len() as u32; + self.exports.insert(name, (index, ExportKind::Global)); + self.globals.push(global); + Ok(GlobalIndex::from_u32(index)) + } + + fn get_global(&mut self, name: &str) -> Result { + let Some((global, ExportKind::Global)) = self.exports.get(name) else { + bail!("missing global {}", name.red()) + }; + Ok(GlobalIndex::from_u32(*global)) + } + + fn get_signature(&self, sig: SignatureIndex) -> Result { + let index = sig.as_u32() as usize; + let error = Report::msg(format!("missing signature {}", index.red())); + self.types.get(index).cloned().ok_or(error) + } + + fn get_function(&self, func: FunctionIndex) -> Result { + let mut index = func.as_u32() as usize; + + let sig = if index < self.imports.len() { + self.imports.get(index).map(|x| &x.offset) + } else { + index -= self.imports.len(); + self.functions.get(index) + }; + + let func = func.as_u32(); + match sig { + Some(sig) => self.get_signature(SignatureIndex::from_u32(*sig)), + None => match self.names.functions.get(&func) { + Some(name) => bail!("missing func {} @ index {}", name.red(), func.red()), + None => bail!("missing func @ index {}", func.red()), + }, + } + } + + fn all_functions(&self) -> Result> { + let mut funcs = HashMap::default(); + let mut index = 0; + for import in &self.imports { + let ty = self.get_signature(SignatureIndex::from_u32(import.offset))?; + funcs.insert(FunctionIndex::new(index), ty); + index += 1; + } + for sig in &self.functions { + let ty = self.get_signature(SignatureIndex::from_u32(*sig))?; + funcs.insert(FunctionIndex::new(index), ty); + index += 1; + } + Ok(funcs) + } + + fn all_signatures(&self) -> Result> { + let mut signatures = HashMap::default(); + for (index, ty) in self.types.iter().enumerate() { + let sig = SignatureIndex::new(index); + signatures.insert(sig, ty.clone()); + } + Ok(signatures) + } + + fn get_import(&self, module: &str, name: &str) -> Result { + self.imports + .iter() + .position(|x| x.module == module && x.name == name) + .map(|x| ImportIndex::Function(FunctionIndex::from_u32(x as u32))) + .ok_or_else(|| eyre!("missing import {}", name.red())) + } + + fn move_start_function(&mut self, name: &str) -> Result { + if let Some(prior) = self.exports.get(name) { + bail!("function {} already exists @ index {:?}", name.red(), prior) + } + + let start = self.start.take(); + if let Some(start) = start { + let name = name.to_owned(); + self.exports.insert(name.clone(), (start, ExportKind::Func)); + self.names.functions.insert(start, name); + } + Ok(start.is_some()) + } + + fn drop_exports_and_names(&mut self, keep: &HashMap<&str, ExportKind>) { + self.exports + .retain(|name, ty| keep.get(name.as_str()).map_or(false, |x| *x == ty.1)); + self.names.functions.clear(); + } + + fn memory_info(&self) -> Result { + if self.memories.is_empty() { + bail!("missing memory export with name {}", "memory".red()); + } + if self.memories.len() > 1 { + bail!("only one memory is allowed"); + } + if self.exports.get("memory") != Some(&(0, ExportKind::Memory)) { + bail!("missing memory with export name {}", "memory".red()); + } + self.memories.last().unwrap().try_into() + } +} + +/// Information about an activated program. +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct StylusData { + /// Global index for the amount of ink left. + pub ink_left: u32, + /// Global index for whether the program is out of ink. + pub ink_status: u32, + /// Global index for the amount of stack space remaining. + pub depth_left: u32, + /// Cost paid to invoke the program. See `programs.go` for the translation to gas. + pub init_cost: u16, + /// Cost paid to invoke the program when stored in the init cache. + pub cached_init_cost: u16, + /// Canonical estimate of the asm length in bytes. + pub asm_estimate: u32, + /// Initial memory size in pages. + pub footprint: u16, + /// Entrypoint offset. + pub user_main: u32, +} + +impl StylusData { + pub fn global_offsets(&self) -> (u64, u64, u64) { + ( + self.ink_left as u64, + self.ink_status as u64, + self.depth_left as u64, + ) + } +} + +impl Module { + pub fn activate( + wasm: &[u8], + codehash: &Bytes32, + version: u16, + page_limit: u16, + debug: bool, + gas: &mut u64, + ) -> Result<(Self, StylusData)> { + // converts a number of microseconds to gas + // TODO: collapse to a single value after finalizing factors + let us_to_gas = |us: u64| { + let fudge = 2; + let sync_rate = 1_000_000 / 2; + let speed = 7_000_000; + us.saturating_mul(fudge * speed) / sync_rate + }; + + macro_rules! pay { + ($us:expr) => { + let amount = us_to_gas($us); + if *gas < amount { + *gas = 0; + bail!("out of gas"); + } + *gas -= amount; + }; + } + + // pay for wasm + let wasm_len = wasm.len() as u64; + pay!(wasm_len.saturating_mul(31_733 / 100_000)); + + let compile = CompileConfig::version(version, debug); + let (bin, stylus_data) = WasmBinary::parse_user(wasm, page_limit, &compile, codehash) + .wrap_err("failed to parse wasm")?; + + // pay for funcs + let funcs = bin.functions.len() as u64; + pay!(funcs.saturating_mul(17_263) / 100_000); + + // pay for data + let data = bin.datas.iter().map(|x| x.data.len()).saturating_sum() as u64; + pay!(data.saturating_mul(17_376) / 100_000); + + // pay for elements + let elems = bin.elements.iter().map(|x| x.range.len()).saturating_sum() as u64; + pay!(elems.saturating_mul(17_376) / 100_000); + + // pay for memory + let mem = bin.memories.first().map(|x| x.initial).unwrap_or_default(); + pay!(mem.saturating_mul(2217)); + + // pay for code + let code = bin.codes.iter().map(|x| x.expr.len()).saturating_sum() as u64; + pay!(code.saturating_mul(535) / 1_000); + + let module = Self::from_user_binary(&bin, compile.debug.debug_funcs, Some(stylus_data)) + .wrap_err("failed to build user module")?; + + Ok((module, stylus_data)) + } +} diff --git a/arbitrator/prover/src/programs/prelude.rs b/arbitrator/prover/src/programs/prelude.rs new file mode 100644 index 0000000000..4db8e03419 --- /dev/null +++ b/arbitrator/prover/src/programs/prelude.rs @@ -0,0 +1,12 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +pub use super::{ + config::{CompileConfig, StylusConfig}, + counter::CountingMachine, + depth::DepthCheckedMachine, + meter::{GasMeteredMachine, MachineMeter, MeteredMachine}, +}; + +#[cfg(feature = "native")] +pub use super::start::StartlessMachine; diff --git a/arbitrator/prover/src/programs/start.rs b/arbitrator/prover/src/programs/start.rs new file mode 100644 index 0000000000..d3a19942f2 --- /dev/null +++ b/arbitrator/prover/src/programs/start.rs @@ -0,0 +1,67 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use crate::{ + binary::ExportKind, + programs::{DefaultFuncMiddleware, Middleware, ModuleMod, STYLUS_ENTRY_POINT}, +}; +use eyre::{bail, Result}; +use fnv::FnvHashMap as HashMap; +use lazy_static::lazy_static; +use wasmer_types::LocalFunctionIndex; + +#[cfg(feature = "native")] +use wasmer::TypedFunction; + +lazy_static! { + /// Lists the exports a user program map have + static ref EXPORT_WHITELIST: HashMap<&'static str, ExportKind> = { + let mut map = HashMap::default(); + map.insert(STYLUS_ENTRY_POINT, ExportKind::Func); + map.insert(StartMover::NAME, ExportKind::Func); + map.insert("memory", ExportKind::Memory); + map + }; +} + +#[derive(Debug)] +pub struct StartMover { + /// Whether to keep offchain information. + debug: bool, +} + +impl StartMover { + pub const NAME: &'static str = "stylus_start"; + + pub fn new(debug: bool) -> Self { + Self { debug } + } +} + +impl Middleware for StartMover { + type FM<'a> = DefaultFuncMiddleware; + + fn update_module(&self, module: &mut M) -> Result<()> { + let had_start = module.move_start_function(Self::NAME)?; + if had_start && !self.debug { + bail!("start functions not allowed"); + } + if !self.debug { + module.drop_exports_and_names(&EXPORT_WHITELIST); + } + Ok(()) + } + + fn instrument<'a>(&self, _: LocalFunctionIndex) -> Result> { + Ok(DefaultFuncMiddleware) + } + + fn name(&self) -> &'static str { + "start mover" + } +} + +#[cfg(feature = "native")] +pub trait StartlessMachine { + fn get_start(&self) -> Result>; +} diff --git a/arbitrator/prover/src/test.rs b/arbitrator/prover/src/test.rs new file mode 100644 index 0000000000..97170441ff --- /dev/null +++ b/arbitrator/prover/src/test.rs @@ -0,0 +1,71 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +#![cfg(test)] + +use crate::binary; +use brotli::Dictionary; +use eyre::Result; +use std::path::Path; + +fn as_wasm(wat: &str) -> Vec { + let wasm = wasmer::wat2wasm(wat.as_bytes()); + wasm.unwrap().to_vec() +} + +#[test] +pub fn reject_reexports() { + let wasm = as_wasm( + r#" + (module + (import "env" "some_hostio_func" (func (param) (result))) + (func $should_reject (export "some_hostio_func") (param) (result)) + )"#, + ); + let _ = binary::parse(&wasm, Path::new("")).unwrap_err(); + + let wasm = as_wasm( + r#" + (module + (import "env" "some_hostio_func" (func (param) (result))) + (global $should_reject (export "some_hostio_func") f32 (f32.const 0)) + )"#, + ); + let _ = binary::parse(&wasm, Path::new("")).unwrap_err(); +} + +#[test] +pub fn reject_ambiguous_imports() { + let wasm = as_wasm( + r#" + (module + (import "vm_hooks" "some_import" (func (param i64) (result i64 i32))) + (import "vm_hooks" "some_import" (func (param i64) (result i64 i32))) + )"#, + ); + let _ = binary::parse(&wasm, Path::new("")).unwrap(); + + let wasm = as_wasm( + r#" + (module + (import "vm_hooks" "some_import" (func (param i32) (result f64))) + (import "vm_hooks" "some_import" (func (param i32) (result))) + )"#, + ); + let _ = binary::parse(&wasm, Path::new("")).unwrap_err(); +} + +#[test] +pub fn test_compress() -> Result<()> { + let data = include_bytes!("../../../target/machines/latest/forward_stub.wasm"); + let mut last = vec![]; + + for dict in [Dictionary::Empty, Dictionary::StylusProgram] { + let deflate = brotli::compress(data, 11, 22, dict).unwrap(); + let inflate = brotli::decompress(&deflate, dict).unwrap(); + assert_eq!(hex::encode(inflate), hex::encode(data)); + assert!(&deflate != &last); + last = deflate; + } + Ok(()) +} diff --git a/arbitrator/prover/src/utils.rs b/arbitrator/prover/src/utils.rs index 3929dbc2a8..49b4ea0c3d 100644 --- a/arbitrator/prover/src/utils.rs +++ b/arbitrator/prover/src/utils.rs @@ -1,114 +1,18 @@ // Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE +#[cfg(feature = "native")] use crate::kzg::ETHEREUM_KZG_SETTINGS; use arbutil::PreimageType; +#[cfg(feature = "native")] use c_kzg::{Blob, KzgCommitment}; use digest::Digest; use eyre::{eyre, Result}; use serde::{Deserialize, Serialize}; use sha2::Sha256; use sha3::Keccak256; -use std::{ - borrow::Borrow, - convert::TryInto, - fmt, - fs::File, - io::Read, - ops::{Deref, DerefMut}, - path::Path, -}; -use wasmparser::{TableType, Type}; - -/// cbindgen:field-names=[bytes] -#[derive(Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -#[repr(C)] -pub struct Bytes32(pub [u8; 32]); - -impl Deref for Bytes32 { - type Target = [u8; 32]; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl DerefMut for Bytes32 { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl AsRef<[u8]> for Bytes32 { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl Borrow<[u8]> for Bytes32 { - fn borrow(&self) -> &[u8] { - &self.0 - } -} - -impl From<[u8; 32]> for Bytes32 { - fn from(x: [u8; 32]) -> Self { - Self(x) - } -} - -impl From for Bytes32 { - fn from(x: u32) -> Self { - let mut b = [0u8; 32]; - b[(32 - 4)..].copy_from_slice(&x.to_be_bytes()); - Self(b) - } -} - -impl From for Bytes32 { - fn from(x: u64) -> Self { - let mut b = [0u8; 32]; - b[(32 - 8)..].copy_from_slice(&x.to_be_bytes()); - Self(b) - } -} - -impl From for Bytes32 { - fn from(x: usize) -> Self { - let mut b = [0u8; 32]; - b[(32 - (usize::BITS as usize / 8))..].copy_from_slice(&x.to_be_bytes()); - Self(b) - } -} - -impl IntoIterator for Bytes32 { - type Item = u8; - type IntoIter = std::array::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - IntoIterator::into_iter(self.0) - } -} - -type GenericBytes32 = digest::generic_array::GenericArray; - -impl From for Bytes32 { - fn from(x: GenericBytes32) -> Self { - <[u8; 32]>::from(x).into() - } -} - -impl fmt::Display for Bytes32 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", hex::encode(self)) - } -} - -impl fmt::Debug for Bytes32 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", hex::encode(self)) - } -} +use std::{borrow::Borrow, convert::TryInto, fmt, fs::File, io::Read, ops::Deref, path::Path}; +use wasmparser::{RefType, TableType}; /// A Vec allocated with libc::malloc pub struct CBytes { @@ -171,23 +75,41 @@ impl From<&[u8]> for CBytes { unsafe impl Send for CBytes {} unsafe impl Sync for CBytes {} +/// Unfortunately, [`wasmparser::RefType`] isn't serde and its contents aren't public. +/// This type enables serde via a 1:1 transmute. #[derive(Serialize, Deserialize)] -#[serde(remote = "Type")] -enum RemoteType { - I32, - I64, - F32, - F64, - V128, - FuncRef, - ExternRef, +struct RemoteRefType(pub [u8; 3]); + +impl From for RemoteRefType { + fn from(value: RefType) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +impl From for RefType { + fn from(value: RemoteRefType) -> Self { + unsafe { std::mem::transmute(value) } + } +} + +mod remote_convert { + use super::{RefType, RemoteRefType}; + use serde::{Deserialize, Deserializer, Serialize, Serializer}; + + pub fn serialize(value: &RefType, serializer: S) -> Result { + RemoteRefType::from(*value).serialize(serializer) + } + + pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result { + Ok(RemoteRefType::deserialize(deserializer)?.into()) + } } #[derive(Serialize, Deserialize)] #[serde(remote = "TableType")] pub struct RemoteTableType { - #[serde(with = "RemoteType")] - pub element_type: Type, + #[serde(with = "remote_convert")] + pub element_type: RefType, pub initial: u32, pub maximum: Option, } @@ -268,6 +190,7 @@ pub fn split_import(qualified: &str) -> Result<(&str, &str)> { Ok((module, name)) } +#[cfg(feature = "native")] pub fn hash_preimage(preimage: &[u8], ty: PreimageType) -> Result<[u8; 32]> { match ty { PreimageType::Keccak256 => Ok(Keccak256::digest(preimage).into()), diff --git a/arbitrator/prover/src/value.rs b/arbitrator/prover/src/value.rs index c63486bd04..4ec02f5463 100644 --- a/arbitrator/prover/src/value.rs +++ b/arbitrator/prover/src/value.rs @@ -1,15 +1,19 @@ // Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -use crate::{binary::FloatType, utils::Bytes32}; -use arbutil::Color; +use crate::binary::FloatType; +use arbutil::{Bytes32, Color}; use digest::Digest; -use eyre::{bail, Result}; +use eyre::{bail, ErrReport, Result}; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, TryFromInto}; use sha3::Keccak256; -use std::{convert::TryFrom, fmt::Display}; -use wasmparser::{FuncType, Type}; +use std::{ + convert::{TryFrom, TryInto}, + fmt::Display, + ops::Add, +}; +use wasmparser::{FuncType, RefType, ValType}; #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)] #[repr(u8)] @@ -29,23 +33,71 @@ impl ArbValueType { } } -impl TryFrom for ArbValueType { +impl TryFrom for ArbValueType { type Error = eyre::Error; - fn try_from(ty: Type) -> Result { - use Type::*; + fn try_from(ty: ValType) -> Result { + use ValType as V; Ok(match ty { - I32 => Self::I32, - I64 => Self::I64, - F32 => Self::F32, - F64 => Self::F64, - FuncRef => Self::FuncRef, - ExternRef => Self::FuncRef, - V128 => bail!("128-bit types are not supported"), + V::I32 => Self::I32, + V::I64 => Self::I64, + V::F32 => Self::F32, + V::F64 => Self::F64, + V::Ref(ty) => ty.try_into()?, + V::V128 => bail!("128-bit types are not supported"), }) } } +impl TryFrom for ArbValueType { + type Error = eyre::Error; + + fn try_from(value: RefType) -> Result { + Ok(match value { + RefType::FUNCREF => Self::FuncRef, + RefType::EXTERNREF => Self::FuncRef, + RefType::NULLREF => Self::RefNull, + _ => bail!("ref extensions not supported"), + }) + } +} + +impl From for ValType { + fn from(ty: ArbValueType) -> Self { + use ArbValueType as V; + match ty { + V::I32 => Self::I32, + V::I64 => Self::I64, + V::F32 => Self::F32, + V::F64 => Self::F64, + V::RefNull => Self::Ref(RefType::NULLREF), + V::FuncRef => Self::Ref(RefType::FUNCREF), + V::InternalRef => Self::Ref(RefType::FUNCREF), // not analogous, but essentially a func pointer + } + } +} + +#[cfg(feature = "native")] +pub fn parser_type(ty: &wasmer::Type) -> wasmer::wasmparser::ValType { + match ty { + wasmer::Type::I32 => wasmer::wasmparser::ValType::I32, + wasmer::Type::I64 => wasmer::wasmparser::ValType::I64, + wasmer::Type::F32 => wasmer::wasmparser::ValType::F32, + wasmer::Type::F64 => wasmer::wasmparser::ValType::F64, + wasmer::Type::V128 => wasmer::wasmparser::ValType::V128, + wasmer::Type::ExternRef => wasmer::wasmparser::ValType::Ref(RefType::EXTERNREF), + wasmer::Type::FuncRef => wasmer::wasmparser::ValType::Ref(RefType::FUNCREF), + } +} + +#[cfg(feature = "native")] +pub fn parser_func_type(ty: wasmer::FunctionType) -> FuncType { + let convert = |t: &[wasmer::Type]| -> Vec { t.iter().map(parser_type).collect() }; + let params = convert(ty.params()); + let results = convert(ty.results()); + FuncType::new(params, results) +} + impl From for ArbValueType { fn from(ty: FloatType) -> ArbValueType { match ty { @@ -112,6 +164,16 @@ impl ProgramCounter { } } +impl Add for ProgramCounter { + type Output = ProgramCounter; + + fn add(self, rhs: u32) -> Self::Output { + let mut counter = self; + counter.inst += rhs; + counter + } +} + impl Display for ProgramCounter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( @@ -282,6 +344,70 @@ impl PartialEq for Value { } } +impl From for Value { + fn from(value: u8) -> Self { + Value::I32(value.into()) + } +} + +impl From for Value { + fn from(value: u16) -> Self { + Value::I32(value.into()) + } +} + +impl From for Value { + fn from(value: u32) -> Self { + Value::I32(value) + } +} + +impl From for Value { + fn from(value: u64) -> Self { + Value::I64(value) + } +} + +impl From for Value { + fn from(value: f32) -> Self { + Value::F32(value) + } +} + +impl From for Value { + fn from(value: f64) -> Self { + Value::F64(value) + } +} + +impl From for Value { + fn from(value: ProgramCounter) -> Self { + Value::InternalRef(value) + } +} + +impl TryInto for Value { + type Error = ErrReport; + + fn try_into(self) -> Result { + match self { + Value::I32(value) => Ok(value), + _ => bail!("value not a u32"), + } + } +} + +impl TryInto for Value { + type Error = ErrReport; + + fn try_into(self) -> Result { + match self { + Value::I64(value) => Ok(value), + _ => bail!("value not a u64"), + } + } +} + impl Eq for Value {} #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)] @@ -291,8 +417,15 @@ pub struct FunctionType { } impl FunctionType { - pub fn new(inputs: Vec, outputs: Vec) -> FunctionType { - FunctionType { inputs, outputs } + pub fn new(inputs: T, outputs: U) -> FunctionType + where + T: Into>, + U: Into>, + { + FunctionType { + inputs: inputs.into(), + outputs: outputs.into(), + } } pub fn hash(&self) -> Bytes32 { @@ -317,13 +450,58 @@ impl TryFrom for FunctionType { let mut inputs = vec![]; let mut outputs = vec![]; - for input in func.params.iter() { + for input in func.params() { inputs.push(ArbValueType::try_from(*input)?) } - for output in func.returns.iter() { + for output in func.results() { outputs.push(ArbValueType::try_from(*output)?) } - Ok(Self { inputs, outputs }) } } + +impl Display for FunctionType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let mut signature = "λ(".to_string(); + if !self.inputs.is_empty() { + for arg in &self.inputs { + signature += &format!("{}, ", arg); + } + signature.pop(); + signature.pop(); + } + signature += ")"; + + let output_tuple = self.outputs.len() > 2; + if !self.outputs.is_empty() { + signature += " -> "; + if output_tuple { + signature += "("; + } + for out in &self.outputs { + signature += &format!("{}, ", out); + } + signature.pop(); + signature.pop(); + if output_tuple { + signature += ")"; + } + } + write!(f, "{}", signature) + } +} + +impl Display for ArbValueType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use ArbValueType::*; + match self { + I32 => write!(f, "i32"), + I64 => write!(f, "i64"), + F32 => write!(f, "f32"), + F64 => write!(f, "f64"), + RefNull => write!(f, "null"), + FuncRef => write!(f, "func"), + InternalRef => write!(f, "internal"), + } + } +} diff --git a/arbitrator/prover/src/wavm.rs b/arbitrator/prover/src/wavm.rs index 690789b445..de016452f3 100644 --- a/arbitrator/prover/src/wavm.rs +++ b/arbitrator/prover/src/wavm.rs @@ -4,9 +4,9 @@ use crate::{ binary::FloatInstruction, host::InternalFunc, - utils::Bytes32, value::{ArbValueType, FunctionType, IntegerValType}, }; +use arbutil::{Bytes32, Color, DebugColor}; use digest::Digest; use eyre::{bail, ensure, Result}; use fnv::FnvHashMap as HashMap; @@ -141,6 +141,10 @@ pub enum Opcode { Dup, /// Call a function in a different module CrossModuleCall, + /// Call a function in a different module, acting as the caller's module + CrossModuleForward, + /// Call a function in a different module provided via the stack + CrossModuleInternalCall, /// Call a caller module's internal method with a given function offset CallerModuleInternalCall, /// Gets bytes32 from global state @@ -155,8 +159,18 @@ pub enum Opcode { ReadPreImage, /// Reads the current inbox message into the pointer on the stack at an offset ReadInboxMessage, + /// Dynamically adds a module to the replay machine + LinkModule, + /// Dynamically removes the last module to the replay machine + UnlinkModule, /// Stop exexcuting the machine and move to the finished status HaltAndSetFinished, + /// create cothread (cannot be called from cothread) + NewCoThread, + /// pop cothread (cannot be called from cothread) + PopCoThread, + /// switch between main and a cothread + SwitchThread, } impl Opcode { @@ -259,6 +273,8 @@ impl Opcode { Opcode::MoveFromInternalToStack => 0x8006, Opcode::Dup => 0x8008, Opcode::CrossModuleCall => 0x8009, + Opcode::CrossModuleForward => 0x800B, + Opcode::CrossModuleInternalCall => 0x800C, Opcode::CallerModuleInternalCall => 0x800A, Opcode::GetGlobalStateBytes32 => 0x8010, Opcode::SetGlobalStateBytes32 => 0x8011, @@ -266,7 +282,12 @@ impl Opcode { Opcode::SetGlobalStateU64 => 0x8013, Opcode::ReadPreImage => 0x8020, Opcode::ReadInboxMessage => 0x8021, + Opcode::LinkModule => 0x8023, + Opcode::UnlinkModule => 0x8024, Opcode::HaltAndSetFinished => 0x8022, + Opcode::NewCoThread => 0x8030, + Opcode::PopCoThread => 0x8031, + Opcode::SwitchThread => 0x8032, } } @@ -337,18 +358,24 @@ impl Instruction { } } - pub fn serialize_for_proof(self) -> [u8; 34] { - let mut ret = [0u8; 34]; - ret[..2].copy_from_slice(&self.opcode.repr().to_be_bytes()); - ret[2..].copy_from_slice(&*self.get_proving_argument_data()); - ret + pub fn serialize_for_proof(code: &[Self]) -> Vec { + let mut data = Vec::with_capacity(1 + 34 * code.len()); + data.push(code.len() as u8); + for inst in code { + data.extend(inst.opcode.repr().to_be_bytes()); + data.extend(inst.get_proving_argument_data()); + } + data } - pub fn hash(self) -> Bytes32 { + pub fn hash(code: &[Self]) -> Bytes32 { let mut h = Keccak256::new(); - h.update(b"Instruction:"); - h.update(self.opcode.repr().to_be_bytes()); - h.update(self.get_proving_argument_data()); + h.update(b"Instructions:"); + h.update((code.len() as u8).to_be_bytes()); + for inst in code { + h.update(inst.opcode.repr().to_be_bytes()); + h.update(inst.get_proving_argument_data()); + } h.finalize().into() } } @@ -416,14 +443,8 @@ impl Sub for StackState { type Output = isize; fn sub(self, rhs: Self) -> Self::Output { - let s = match self { - Self::Reachable(s) => s, - Self::Unreachable => return 0, - }; - let rhs = match rhs { - Self::Reachable(rhs) => rhs, - Self::Unreachable => return 0, - }; + let Self::Reachable(s) = self else { return 0 }; + let Self::Reachable(rhs) = rhs else { return 0 }; s as isize - rhs as isize } } @@ -436,6 +457,7 @@ pub fn wasm_to_wavm( all_types: &[FunctionType], all_types_func_idx: u32, internals_offset: u32, + name: &str, ) -> Result<()> { use Operator::*; @@ -564,9 +586,8 @@ pub fn wasm_to_wavm( let func = $func; let sig = func.signature(); - let (module, func) = match fp_impls.get(&func) { - Some((module, func)) => (module, func), - None => bail!("No implementation for floating point operation {:?}", &func), + let Some((module, func)) = fp_impls.get(&func) else { + bail!("No implementation for floating point operation {} in {}", func.debug_red(), name.red()); }; // Reinterpret float args into ints @@ -702,17 +723,17 @@ pub fn wasm_to_wavm( opcode!(Unreachable); stack = StackState::Unreachable; }, - Nop => opcode!(Nop), - Block { ty } => { - scopes.push(Scope::Simple(*ty, vec![], height_after_block!(ty))); + Nop => {}, + Block { blockty } => { + scopes.push(Scope::Simple(*blockty, vec![], height_after_block!(blockty))); } - Loop { ty } => { - scopes.push(Scope::Loop(*ty, out.len(), stack, height_after_block!(ty))); + Loop { blockty } => { + scopes.push(Scope::Loop(*blockty, out.len(), stack, height_after_block!(blockty))); } - If { ty } => { + If { blockty } => { opcode!(I32Eqz); stack -= 1; // the else block shouldn't have the conditional that gets popped next instruction - scopes.push(Scope::IfElse(*ty, vec![], Some(out.len()), stack, height_after_block!(ty))); + scopes.push(Scope::IfElse(*blockty, vec![], Some(out.len()), stack, height_after_block!(blockty))); opcode!(ArbitraryJumpIf); } Else => { @@ -725,12 +746,12 @@ pub fn wasm_to_wavm( *cond = None; stack = *if_height; } - x => bail!("malformed if-else scope {:?}", x), + x => bail!("malformed if-else scope {x:?}"), } } - unsupported @ dot!(Try, Catch, Throw, Rethrow) => { - bail!("exception-handling extension not supported {:?}", unsupported) + unsupported @ dot!(Try, Catch, Throw, Rethrow, ThrowRef, TryTable) => { + bail!("exception-handling extension not supported {unsupported:?}") }, End => { @@ -750,11 +771,11 @@ pub fn wasm_to_wavm( } Br { relative_depth } => branch!(ArbitraryJump, *relative_depth), BrIf { relative_depth } => branch!(ArbitraryJumpIf, *relative_depth), - BrTable { table } => { + BrTable { targets } => { let start_stack = stack; // evaluate each branch let mut subjumps = vec![]; - for (index, target) in table.targets().enumerate() { + for (index, target) in targets.targets().enumerate() { opcode!(Dup, @push 1); opcode!(I32Const, index as u64, @push 1); compare!(I32, Eq, false); @@ -764,7 +785,7 @@ pub fn wasm_to_wavm( // nothing matched: drop the index and jump to the default. opcode!(Drop, @pop 1); - branch!(ArbitraryJump, table.default()); + branch!(ArbitraryJump, targets.default()); // simulate a jump table of branches for (jump, branch) in subjumps { @@ -777,25 +798,29 @@ pub fn wasm_to_wavm( Return => branch!(ArbitraryJump, scopes.len() - 1), Call { function_index } => call!(*function_index), - CallIndirect { index, table_index, .. } => { - let ty = &all_types[*index as usize]; + CallIndirect { type_index, table_index, .. } => { + let ty = &all_types[*type_index as usize]; let delta = ty.outputs.len() as isize - ty.inputs.len() as isize; - opcode!(CallIndirect, pack_call_indirect(*table_index, *index), @push delta - 1); + opcode!(CallIndirect, pack_call_indirect(*table_index, *type_index), @push delta - 1); } unsupported @ dot!(ReturnCall, ReturnCallIndirect) => { - bail!("tail-call extension not supported {:?}", unsupported) + bail!("tail-call extension not supported {unsupported:?}") + } + + unsupported @ dot!(CallRef, ReturnCallRef) => { + bail!("typed function references extension not supported {unsupported:?}") } unsupported @ (dot!(Delegate) | op!(CatchAll)) => { - bail!("exception-handling extension not supported {:?}", unsupported) + bail!("exception-handling extension not supported {unsupported:?}") }, Drop => opcode!(Drop, @pop 1), Select => opcode!(Select, @pop 2), unsupported @ dot!(TypedSelect) => { - bail!("reference-types extension not supported {:?}", unsupported) + bail!("reference-types extension not supported {unsupported:?}") }, LocalGet { local_index } => opcode!(LocalGet, *local_index as u64, @push 1), @@ -842,10 +867,14 @@ pub fn wasm_to_wavm( F32Const { value } => opcode!(F32Const, value.bits() as u64, @push 1), F64Const { value } => opcode!(F64Const, value.bits(), @push 1), - unsupported @ (dot!(RefNull) | op!(RefIsNull) | dot!(RefFunc)) => { - bail!("reference-types extension not supported {:?}", unsupported) + unsupported @ (dot!(RefNull) | op!(RefIsNull) | dot!(RefFunc, RefEq)) => { + bail!("reference-types extension not supported {unsupported:?}") }, + unsupported @ dot!(RefAsNonNull, BrOnNull, BrOnNonNull) => { + bail!("typed function references extension not supported {unsupported:?}") + } + I32Eqz => opcode!(I32Eqz), I32Eq => compare!(I32, Eq, false), I32Ne => compare!(I32, Ne, false), @@ -987,8 +1016,8 @@ pub fn wasm_to_wavm( ensure!(*mem == 0, "multi-memory proposal not supported"); call!(internals_offset + InternalFunc::MemoryFill as u32) }, - MemoryCopy { src, dst } => { - ensure!(*src == 0 && *dst == 0, "multi-memory proposal not supported"); + MemoryCopy { src_mem, dst_mem } => { + ensure!(*src_mem == 0 && *dst_mem == 0, "multi-memory proposal not supported"); call!(internals_offset + InternalFunc::MemoryCopy as u32) }, @@ -997,7 +1026,21 @@ pub fn wasm_to_wavm( MemoryInit, DataDrop, TableInit, ElemDrop, TableCopy, TableFill, TableGet, TableSet, TableGrow, TableSize ) - ) => bail!("bulk-memory-operations extension not fully supported {:?}", unsupported), + ) => bail!("bulk-memory-operations extension not fully supported {unsupported:?}"), + + unsupported @ dot!(MemoryDiscard) => { + bail!("memory control proposal {unsupported:?}") + }, + + unsupported @ ( + dot!( + StructNew, StructNewDefault, StructGet, StructGetS, StructGetU, StructSet, + ArrayNew, ArrayNewDefault, ArrayNewFixed, ArrayNewData, ArrayNewElem, + ArrayGet, ArrayGetS, ArrayGetU, ArraySet, ArrayLen, ArrayFill, ArrayCopy, + ArrayInitData, ArrayInitElem, RefTestNonNull, RefTestNullable, RefCastNonNull, RefCastNullable, + BrOnCast, BrOnCastFail, AnyConvertExtern, ExternConvertAny, RefI31, I31GetS, I31GetU + ) + ) => bail!("garbage collection extension not supported {unsupported:?}"), unsupported @ ( dot!( @@ -1016,7 +1059,7 @@ pub fn wasm_to_wavm( I64AtomicRmw32XchgU, I32AtomicRmwCmpxchg, I64AtomicRmwCmpxchg, I32AtomicRmw8CmpxchgU, I32AtomicRmw16CmpxchgU, I64AtomicRmw8CmpxchgU, I64AtomicRmw16CmpxchgU, I64AtomicRmw32CmpxchgU ) - ) => bail!("threads extension not supported {:?}", unsupported), + ) => bail!("threads extension not supported {unsupported:?}"), unsupported @ ( dot!( @@ -1037,12 +1080,12 @@ pub fn wasm_to_wavm( V128Not, V128And, V128AndNot, V128Or, V128Xor, V128Bitselect, V128AnyTrue, I8x16Abs, I8x16Neg, I8x16Popcnt, I8x16AllTrue, I8x16Bitmask, I8x16NarrowI16x8S, I8x16NarrowI16x8U, I8x16Shl, I8x16ShrS, I8x16ShrU, I8x16Add, I8x16AddSatS, I8x16AddSatU, I8x16Sub, I8x16SubSatS, - I8x16SubSatU, I8x16MinS, I8x16MinU, I8x16MaxS, I8x16MaxU, I8x16RoundingAverageU, + I8x16SubSatU, I8x16MinS, I8x16MinU, I8x16MaxS, I8x16MaxU, I8x16AvgrU, I16x8ExtAddPairwiseI8x16S, I16x8ExtAddPairwiseI8x16U, I16x8Abs, I16x8Neg, I16x8Q15MulrSatS, I16x8AllTrue, I16x8Bitmask, I16x8NarrowI32x4S, I16x8NarrowI32x4U, I16x8ExtendLowI8x16S, I16x8ExtendHighI8x16S, I16x8ExtendLowI8x16U, I16x8ExtendHighI8x16U, I16x8Shl, I16x8ShrS, I16x8ShrU, I16x8Add, I16x8AddSatS, I16x8AddSatU, I16x8Sub, I16x8SubSatS, I16x8SubSatU, I16x8Mul, I16x8MinS, - I16x8MinU, I16x8MaxS, I16x8MaxU, I16x8RoundingAverageU, I16x8ExtMulLowI8x16S, + I16x8MinU, I16x8MaxS, I16x8MaxU, I16x8AvgrU, I16x8ExtMulLowI8x16S, I16x8ExtMulHighI8x16S, I16x8ExtMulLowI8x16U, I16x8ExtMulHighI8x16U, I32x4ExtAddPairwiseI16x8S, I32x4ExtAddPairwiseI16x8U, I32x4Abs, I32x4Neg, I32x4AllTrue, I32x4Bitmask, I32x4ExtendLowI16x8S, I32x4ExtendHighI16x8S, I32x4ExtendLowI16x8U, I32x4ExtendHighI16x8U, I32x4Shl, I32x4ShrS, I32x4ShrU, @@ -1057,14 +1100,14 @@ pub fn wasm_to_wavm( F64x2Max, F64x2PMin, F64x2PMax, I32x4TruncSatF32x4S, I32x4TruncSatF32x4U, F32x4ConvertI32x4S, F32x4ConvertI32x4U, I32x4TruncSatF64x2SZero, I32x4TruncSatF64x2UZero, F64x2ConvertLowI32x4S, F64x2ConvertLowI32x4U, F32x4DemoteF64x2Zero, F64x2PromoteLowF32x4, I8x16RelaxedSwizzle, - I32x4RelaxedTruncSatF32x4S, I32x4RelaxedTruncSatF32x4U, I32x4RelaxedTruncSatF64x2SZero, - I32x4RelaxedTruncSatF64x2UZero, F32x4Fma, F32x4Fms, F64x2Fma, F64x2Fms, I8x16LaneSelect, - I16x8LaneSelect, I32x4LaneSelect, I64x2LaneSelect, F32x4RelaxedMin, F32x4RelaxedMax, - F64x2RelaxedMin, F64x2RelaxedMax + I32x4RelaxedTruncF32x4S, I32x4RelaxedTruncF32x4U, I32x4RelaxedTruncF64x2SZero, + I32x4RelaxedTruncF64x2UZero, F32x4RelaxedMadd, F32x4RelaxedNmadd, F64x2RelaxedMadd, + F64x2RelaxedNmadd, I8x16RelaxedLaneselect, I16x8RelaxedLaneselect, I32x4RelaxedLaneselect, + I64x2RelaxedLaneselect, F32x4RelaxedMin, F32x4RelaxedMax, F64x2RelaxedMin, F64x2RelaxedMax, + I16x8RelaxedQ15mulrS, I16x8RelaxedDotI8x16I7x16S, I32x4RelaxedDotI8x16I7x16AddS ) - ) => bail!("SIMD extension not supported {:?}", unsupported) + ) => bail!("SIMD extension not supported {unsupported:?}") }; } - Ok(()) } diff --git a/arbitrator/prover/test-cases/block.wat b/arbitrator/prover/test-cases/block.wat index 32ac7a5a1d..2ea3d087d1 100644 --- a/arbitrator/prover/test-cases/block.wat +++ b/arbitrator/prover/test-cases/block.wat @@ -66,4 +66,9 @@ (br_if 0) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/bulk-memory.wat b/arbitrator/prover/test-cases/bulk-memory.wat index 53fc248db1..3ae0728538 100644 --- a/arbitrator/prover/test-cases/bulk-memory.wat +++ b/arbitrator/prover/test-cases/bulk-memory.wat @@ -79,5 +79,10 @@ local.get 1 i32.ne (if (then (unreachable)))) + + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + ) + (start $start) - (memory (export "mem") 1)) + (memory (export "memory") 1 1)) diff --git a/arbitrator/prover/test-cases/call-indirect.wat b/arbitrator/prover/test-cases/call-indirect.wat index 6f1b8ab9f8..1f6bee6d38 100644 --- a/arbitrator/prover/test-cases/call-indirect.wat +++ b/arbitrator/prover/test-cases/call-indirect.wat @@ -26,4 +26,9 @@ (i32.mul) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/call.wat b/arbitrator/prover/test-cases/call.wat index e4bcf8d124..f56849ab75 100644 --- a/arbitrator/prover/test-cases/call.wat +++ b/arbitrator/prover/test-cases/call.wat @@ -16,4 +16,9 @@ (call 1) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/const.wat b/arbitrator/prover/test-cases/const.wat index e9e46ff973..4f3ffbd8d8 100644 --- a/arbitrator/prover/test-cases/const.wat +++ b/arbitrator/prover/test-cases/const.wat @@ -8,4 +8,9 @@ (drop) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/div-overflow.wat b/arbitrator/prover/test-cases/div-overflow.wat index 993185b822..a76493e74a 100644 --- a/arbitrator/prover/test-cases/div-overflow.wat +++ b/arbitrator/prover/test-cases/div-overflow.wat @@ -9,4 +9,9 @@ (drop) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/dynamic.wat b/arbitrator/prover/test-cases/dynamic.wat new file mode 100644 index 0000000000..8771bde87c --- /dev/null +++ b/arbitrator/prover/test-cases/dynamic.wat @@ -0,0 +1,91 @@ + +(module + (import "hostio" "wavm_link_module" (func $link (param i32) (result i32))) + (import "hostio" "wavm_unlink_module" (func $unlink )) + (import "hostio" "program_set_ink" (func $set_ink (param i32 i64) )) + (import "hostio" "program_ink_left" (func $ink_left (param i32) (result i64))) + (import "hostio" "program_ink_status" (func $ink_status (param i32) (result i32))) + (import "hostio" "program_set_stack" (func $set_stack (param i32 i32) )) + (import "hostio" "program_stack_left" (func $stack_left (param i32) (result i32))) + (import "hostio" "program_call_main" (func $user_func (param i32 i32) (result i32))) + (import "env" "wavm_halt_and_set_finished" (func $halt )) + + ;; WAVM Module hash + (data (i32.const 0x000) + "\a1\49\cf\81\13\ff\9c\95\f2\c8\c2\a1\42\35\75\36\7d\e8\6d\d4\22\d8\71\14\bb\9e\a4\7b\af\53\5d\d7") ;; user + (func $start (local $user i32) (local $internals i32) + ;; link in user.wat + i32.const 0 + call $link + local.set $user + + ;; set gas globals + local.get $user + i64.const 65536 + call $set_ink + + ;; get gas + local.get $user + call $ink_left + i64.const 65536 + i64.ne + (if (then (unreachable))) + + ;; get gas status + (call $ink_status (local.get $user)) + i32.const 0 + i32.ne + (if (then (unreachable))) + + ;; set stack global + local.get $user + i32.const 1024 + call $set_stack + + ;; get stack + local.get $user + call $stack_left + i32.const 1024 + i32.ne + (if (then (unreachable))) + + ;; call a successful func in user.wat ($safe) + local.get $user + i32.const 1 ;; $safe + call $user_func + i32.const 1 + i32.ne + (if (then (unreachable))) + + ;; recover from an unreachable + local.get $user + i32.const 2 ;; $unreachable + call $user_func + i32.const 2 ;; indicates failure + i32.ne + (if (then (unreachable))) + + ;; push some items to the stack + i32.const 0xa4b0 + i64.const 0xa4b1 + i32.const 0xa4b2 + + ;; recover from an out-of-bounds memory access + local.get $user + i32.const 3 ;; $out_of_bounds + call $user_func + i32.const 2 ;; indicates failure + i32.ne + (if (then (unreachable))) + + ;; drop the items from the stack + drop + drop + drop + + ;; unlink module + call $unlink + call $halt + ) + (start $start) + (memory 1)) diff --git a/arbitrator/prover/test-cases/forward-test.wat b/arbitrator/prover/test-cases/forward-test.wat new file mode 100644 index 0000000000..b9beff0d82 --- /dev/null +++ b/arbitrator/prover/test-cases/forward-test.wat @@ -0,0 +1,32 @@ + +(module + (import "forward" "add" (func $add (param i32 i32) (result i32))) + (import "forward" "sub" (func $sub (param i32 i32) (result i32))) + (import "forward" "mul" (func $mul (param i32 i32) (result i32))) + (func $start + ;; this address will update each time a forwarded call is made + i32.const 0xa4b + i32.const 805 + i32.store + + i32.const 11 + i32.const 5 + call $sub + + i32.const 3 + i32.const -2 + call $mul + + call $add + (if + (then (unreachable))) + + ;; check that the address has changed + i32.const 0xa4b + i32.load + i32.const 808 + i32.ne + (if + (then (unreachable)))) + (start $start) + (memory 1)) diff --git a/arbitrator/prover/test-cases/forward/forward.wat b/arbitrator/prover/test-cases/forward/forward.wat new file mode 100644 index 0000000000..ff55953e62 --- /dev/null +++ b/arbitrator/prover/test-cases/forward/forward.wat @@ -0,0 +1,8 @@ + +(module + (import "target" "arbitrator_forward__add" (func $add (param i32 i32) (result i32))) + (import "target" "arbitrator_forward__sub" (func $sub (param i32 i32) (result i32))) + (import "target" "arbitrator_forward__mul" (func $mul (param i32 i32) (result i32))) + (export "forward__add" (func $add)) + (export "forward__sub" (func $sub)) + (export "forward__mul" (func $mul))) diff --git a/arbitrator/prover/test-cases/forward/target.wat b/arbitrator/prover/test-cases/forward/target.wat new file mode 100644 index 0000000000..0779eb753d --- /dev/null +++ b/arbitrator/prover/test-cases/forward/target.wat @@ -0,0 +1,27 @@ + +(module + (import "env" "wavm_caller_load8" (func $load (param i32) (result i32))) + (import "env" "wavm_caller_store8" (func $store (param i32 i32))) + (func (export "target__add") (param i32 i32) (result i32) + call $write_caller + local.get 0 + local.get 1 + i32.add) + (func (export "target__sub") (param i32 i32) (result i32) + call $write_caller + local.get 0 + local.get 1 + i32.sub) + (func (export "target__mul") (param i32 i32) (result i32) + call $write_caller + local.get 0 + local.get 1 + i32.mul) + (func $write_caller (export "write_caller") + ;; increment the value at address 0xa4b in the caller + i32.const 0xa4b + i32.const 0xa4b + call $load + i32.const 1 + i32.add + call $store)) diff --git a/arbitrator/prover/test-cases/global-state-wavm.wat b/arbitrator/prover/test-cases/global-state-wavm.wat new file mode 100644 index 0000000000..6ac2b0ee87 --- /dev/null +++ b/arbitrator/prover/test-cases/global-state-wavm.wat @@ -0,0 +1,23 @@ +(import "env" "wavm_set_globalstate_u64" (func $set (param i32) (param i64))) +(import "env" "wavm_get_globalstate_u64" (func $get (param i32) (result i64))) +(import "env" "wavm_halt_and_set_finished" (func $halt)) + +(func $entry + (i32.const 0) + (i64.const 10) + (call $set) + (loop + (i32.const 0) + (i32.const 0) + (call $get) + (i64.sub (i64.const 1)) + (call $set) + (i32.const 0) + (call $get) + (i32.wrap_i64) + (br_if 0) + ) + (call $halt) +) + +(start $entry) diff --git a/arbitrator/prover/test-cases/global-state-wrapper.wat b/arbitrator/prover/test-cases/global-state-wrapper.wat index a133467f74..8c7f30142e 100644 --- a/arbitrator/prover/test-cases/global-state-wrapper.wat +++ b/arbitrator/prover/test-cases/global-state-wrapper.wat @@ -6,7 +6,7 @@ (import "env" "wavm_read_inbox_message" (func $readinbox (param i64) (param i32) (param i32) (result i32))) (import "env" "wavm_halt_and_set_finished" (func $halt)) -(export "env__wavm_set_globalstate_u64" (func $set)) -(export "env__wavm_get_globalstate_u64" (func $get)) -(export "env__wavm_read_inbox_message" (func $readinbox)) -(export "env__wavm_halt_and_set_finished" (func $halt)) +(export "wrapper__set_globalstate_u64" (func $set)) +(export "wrapper__get_globalstate_u64" (func $get)) +(export "wrapper__read_inbox_message" (func $readinbox)) +(export "wrapper__halt_and_set_finished" (func $halt)) diff --git a/arbitrator/prover/test-cases/global-state.wat b/arbitrator/prover/test-cases/global-state.wat index 6ac2b0ee87..6fc0c78b22 100644 --- a/arbitrator/prover/test-cases/global-state.wat +++ b/arbitrator/prover/test-cases/global-state.wat @@ -1,6 +1,6 @@ -(import "env" "wavm_set_globalstate_u64" (func $set (param i32) (param i64))) -(import "env" "wavm_get_globalstate_u64" (func $get (param i32) (result i64))) -(import "env" "wavm_halt_and_set_finished" (func $halt)) +(import "wrapper" "set_globalstate_u64" (func $set (param i32) (param i64))) +(import "wrapper" "get_globalstate_u64" (func $get (param i32) (result i64))) +(import "wrapper" "halt_and_set_finished" (func $halt)) (func $entry (i32.const 0) diff --git a/arbitrator/prover/test-cases/globals.wat b/arbitrator/prover/test-cases/globals.wat index a4b6bfd698..451b83a010 100644 --- a/arbitrator/prover/test-cases/globals.wat +++ b/arbitrator/prover/test-cases/globals.wat @@ -18,7 +18,9 @@ (drop) ) -(start 0) - - +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) +(start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/go/main.go b/arbitrator/prover/test-cases/go/main.go index 549a83f15f..1f81553af2 100644 --- a/arbitrator/prover/test-cases/go/main.go +++ b/arbitrator/prover/test-cases/go/main.go @@ -1,6 +1,9 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE +//go:build wasm +// +build wasm + package main import ( @@ -11,6 +14,7 @@ import ( "math/big" "os" "runtime" + "sync" "time" "github.com/ethereum/go-ethereum/common" @@ -18,6 +22,7 @@ import ( merkletree "github.com/wealdtech/go-merkletree" "github.com/offchainlabs/nitro/arbcompress" + "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/wavmio" ) @@ -48,7 +53,7 @@ func MerkleSample(data [][]byte, toproove int) (bool, error) { // Verify the proof for 'Baz' } -func testCompression(data []byte) { +func testCompression(data []byte, doneChan chan struct{}) { compressed, err := arbcompress.CompressLevel(data, 0) if err != nil { panic(err) @@ -60,6 +65,7 @@ func testCompression(data []byte) { if !bytes.Equal(decompressed, data) { panic("data differs after compression / decompression") } + doneChan <- struct{}{} } const FIELD_ELEMENTS_PER_BLOB = 4096 @@ -67,11 +73,51 @@ const BYTES_PER_FIELD_ELEMENT = 32 var BLS_MODULUS, _ = new(big.Int).SetString("52435875175126190479447740508185965837690552500527637822603658699938581184513", 10) +var stylusModuleHash = common.HexToHash("a149cf8113ff9c95f2c8c2a1423575367de86dd422d87114bb9ea47baf535dd7") // user.wat + +func callStylusProgram(recurse int) { + evmData := programs.EvmData{} + progParams := programs.ProgParams{ + MaxDepth: 10000, + InkPrice: 1, + DebugMode: true, + } + reqHandler := func(req programs.RequestType, input []byte) ([]byte, []byte, uint64) { + fmt.Printf("got request type %d req %v\n", req, input) + if req == programs.GetBytes32 { + if recurse > 0 { + callStylusProgram(recurse - 1) + } + answer := common.Hash{} + return answer[:], nil, 1 + } + + panic("unsupported call") + } + calldata := common.Hash{}.Bytes() + _, _, err := programs.CallProgramLoop( + stylusModuleHash, + calldata, + 160000000, + &evmData, + &progParams, + reqHandler) + if err != nil { + panic(err) + } +} + func main() { fmt.Printf("starting executable with %v arg(s): %v\n", len(os.Args), os.Args) runtime.GC() time.Sleep(time.Second) + fmt.Printf("Stylus test\n") + + callStylusProgram(5) + + fmt.Printf("Stylus test done!\n") + // Data for the tree data := [][]byte{ []byte("Foo"), @@ -79,34 +125,59 @@ func main() { []byte("Baz"), } - verified, err := MerkleSample(data, 0) - if err != nil { - panic(err) - } - if !verified { - panic("failed to verify proof for Baz") - } - verified, err = MerkleSample(data, 1) - if err != nil { - panic(err) - } - if !verified { - panic("failed to verify proof for Baz") - } + var wg sync.WaitGroup - verified, err = MerkleSample(data, -1) - if err != nil { - if verified { - panic("succeeded to verify proof invalid") + wg.Add(1) + go func() { + verified, err := MerkleSample(data, 0) + if err != nil { + panic(err) } - } + if !verified { + panic("failed to verify proof for Baz") + } + wg.Done() + }() + wg.Add(1) + go func() { + verified, err := MerkleSample(data, 1) + if err != nil { + panic(err) + } + if !verified { + panic("failed to verify proof for Baz") + } + wg.Done() + }() + wg.Add(1) + go func() { + verified, err := MerkleSample(data, -1) + if err != nil { + if verified { + panic("succeeded to verify proof invalid") + } + } + wg.Done() + }() + wg.Wait() + println("verified proofs with waitgroup!\n") - println("verified both proofs!\n") + doneChan1 := make(chan struct{}) + doneChan2 := make(chan struct{}) + go testCompression([]byte{}, doneChan1) + go testCompression([]byte("This is a test string la la la la la la la la la la"), doneChan2) + <-doneChan2 + <-doneChan1 - testCompression([]byte{}) - testCompression([]byte("This is a test string la la la la la la la la la la")) + println("compression + chan test passed!\n") - println("test compression passed!\n") + if wavmio.GetInboxPosition() != 0 { + panic("unexpected inbox pos") + } + if wavmio.GetLastBlockHash() != (common.Hash{}) { + panic("unexpected lastblock hash") + } + println("wavmio test passed!\n") checkPreimage := func(ty arbutil.PreimageType, hash common.Hash) { preimage, err := wavmio.ResolveTypedPreimage(ty, hash) diff --git a/arbitrator/prover/test-cases/if-else.wat b/arbitrator/prover/test-cases/if-else.wat index 252a3be752..6a2d3a5bc8 100644 --- a/arbitrator/prover/test-cases/if-else.wat +++ b/arbitrator/prover/test-cases/if-else.wat @@ -18,4 +18,9 @@ (drop) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/iops.wat b/arbitrator/prover/test-cases/iops.wat index 7ec8ab945d..906ae43622 100644 --- a/arbitrator/prover/test-cases/iops.wat +++ b/arbitrator/prover/test-cases/iops.wat @@ -80,4 +80,9 @@ (drop) ) -(start 0) \ No newline at end of file +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + +(start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/link.txt b/arbitrator/prover/test-cases/link.txt new file mode 100644 index 0000000000..368e40b40b --- /dev/null +++ b/arbitrator/prover/test-cases/link.txt @@ -0,0 +1,13 @@ +block +call +call-indirect +const +div-overflow +globals +if-else +locals +loop +math +iops +user +return diff --git a/arbitrator/prover/test-cases/link.wat b/arbitrator/prover/test-cases/link.wat new file mode 100644 index 0000000000..ef15326481 --- /dev/null +++ b/arbitrator/prover/test-cases/link.wat @@ -0,0 +1,89 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "hostio" "wavm_link_module" (func $link (param i32) (result i32))) + (import "hostio" "wavm_unlink_module" (func $unlink (param) (result))) + (import "env" "wavm_halt_and_set_finished" (func $halt )) + + ;; WAVM module hashes + (data (i32.const 0x000) + "\eb\12\b0\76\57\15\ad\16\0a\78\54\4d\c7\8d\d4\86\1c\58\a3\ee\77\f9\4a\4e\61\a3\f1\7f\d9\d2\be\8a") ;; block + (data (i32.const 0x020) + "\01\90\21\0c\1d\c8\45\9c\32\ef\a6\00\44\3b\e0\b6\31\70\1f\ce\7a\38\90\1c\e0\c5\40\6d\d8\ce\30\a6") ;; call + (data (i32.const 0x040) + "\e1\a2\fa\8e\81\2a\34\2e\cf\0f\62\46\ba\a4\45\8e\2d\95\2f\ec\1e\79\8f\dc\1b\1c\b8\15\cf\26\02\6c") ;; indirect + (data (i32.const 0x060) + "\ae\cb\eb\e9\0b\5e\1f\78\1b\66\5b\ff\8a\a4\18\a1\a2\e9\90\26\8b\df\df\95\64\54\82\07\6a\d4\e6\20") ;; const + (data (i32.const 0x080) + "\8b\7b\7e\a8\b8\21\c8\d0\2a\80\7c\1e\4b\6d\0d\07\f3\2d\8b\4e\f1\6b\e4\44\03\cf\05\66\9b\09\be\6d") ;; div + (data (i32.const 0x0a0) + "\da\4a\41\74\d6\2e\20\36\8e\cb\8e\5d\45\12\1c\28\1d\c4\8f\1d\77\92\9f\07\a8\6b\35\ea\89\2e\f9\72") ;; globals + (data (i32.const 0x0c0) + "\3f\ec\7c\06\04\b2\0d\99\bb\10\85\61\91\ea\b6\97\a7\a2\d1\19\67\2e\7c\d9\17\d4\6b\45\e8\4b\83\4b") ;; if-else + (data (i32.const 0x0e0) + "\30\12\24\71\df\9f\a9\f8\9c\33\9b\37\a7\08\f5\aa\5f\53\68\b4\e4\de\66\bb\73\ff\30\29\47\5f\50\e5") ;; locals + (data (i32.const 0x100) + "\f3\95\dd\a7\e1\d7\df\94\06\ca\93\0f\53\bf\66\ce\1a\aa\b2\30\68\08\64\b5\5b\61\54\2c\1d\62\e8\25") ;; loop + (data (i32.const 0x120) + "\8c\a3\63\7c\4e\70\f7\79\13\0c\9a\94\5e\63\3b\a9\06\80\9f\a6\51\0e\32\34\e0\9d\78\05\6a\30\98\0f") ;; math + (data (i32.const 0x140) + "\47\f7\4f\9c\21\51\4f\52\24\ea\d3\37\5c\bf\a9\1b\1a\5f\ef\22\a5\2a\60\30\c5\52\18\90\6b\b1\51\e5") ;; iops + (data (i32.const 0x160) + "\a1\49\cf\81\13\ff\9c\95\f2\c8\c2\a1\42\35\75\36\7d\e8\6d\d4\22\d8\71\14\bb\9e\a4\7b\af\53\5d\d7") ;; user + (data (i32.const 0x180) + "\ee\47\08\f6\47\b2\10\88\1f\89\86\e7\e3\79\6b\b2\77\43\f1\4e\ee\cf\45\4a\9b\7c\d7\c4\5b\63\b6\d7") ;; return + + (func $start (local $counter i32) + + ;; add modules + (loop $top + ;; increment counter + local.get $counter + local.get $counter + i32.const 1 + i32.add + local.set $counter + + ;; link module with unique hash + i32.const 32 + i32.mul + call $link + + ;; loop until 12 modules + i32.const 12 + i32.le_s + br_if $top + ) + + ;; reset counter + i32.const 0 + local.set $counter + + ;; link and unlink modules + (loop $top + ;; increment counter + local.get $counter + local.get $counter + i32.const 1 + i32.add + local.set $counter + + ;; unlink 2 modules + call $unlink + call $unlink + + ;; link module with unique hash + i32.const 32 + i32.mul + call $link + + ;; loop until most are gone + i32.const 3 + i32.ge_s + br_if $top) + + call $halt + ) + (memory 1) + (start $start)) diff --git a/arbitrator/prover/test-cases/locals.wat b/arbitrator/prover/test-cases/locals.wat index 3e41faa27e..01b91937c1 100644 --- a/arbitrator/prover/test-cases/locals.wat +++ b/arbitrator/prover/test-cases/locals.wat @@ -16,5 +16,9 @@ (drop) ) -(start 0) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) +(start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/loop.wat b/arbitrator/prover/test-cases/loop.wat index 34cdb77daf..4c32d6a5bc 100644 --- a/arbitrator/prover/test-cases/loop.wat +++ b/arbitrator/prover/test-cases/loop.wat @@ -29,6 +29,9 @@ (unreachable) ) -(start 0) - +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) +(start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/math.wat b/arbitrator/prover/test-cases/math.wat index 7315e2d71b..2d78dbeb57 100644 --- a/arbitrator/prover/test-cases/math.wat +++ b/arbitrator/prover/test-cases/math.wat @@ -81,4 +81,9 @@ (drop) ) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) + (start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/read-inboxmsg-10.wat b/arbitrator/prover/test-cases/read-inboxmsg-10.wat index a7977e8e7c..3c1badc442 100644 --- a/arbitrator/prover/test-cases/read-inboxmsg-10.wat +++ b/arbitrator/prover/test-cases/read-inboxmsg-10.wat @@ -1,7 +1,7 @@ -(import "env" "wavm_set_globalstate_u64" (func $set (param i32) (param i64))) -(import "env" "wavm_get_globalstate_u64" (func $get (param i32) (result i64))) -(import "env" "wavm_read_inbox_message" (func $readinbox (param i64) (param i32) (param i32) (result i32))) -(import "env" "wavm_halt_and_set_finished" (func $halt)) +(import "wrapper" "set_globalstate_u64" (func $set (param i32) (param i64))) +(import "wrapper" "get_globalstate_u64" (func $get (param i32) (result i64))) +(import "wrapper" "read_inbox_message" (func $readinbox (param i64) (param i32) (param i32) (result i32))) +(import "wrapper" "halt_and_set_finished" (func $halt)) (memory 1) diff --git a/arbitrator/prover/test-cases/return.wat b/arbitrator/prover/test-cases/return.wat index f2d36f8e84..278b1651f6 100644 --- a/arbitrator/prover/test-cases/return.wat +++ b/arbitrator/prover/test-cases/return.wat @@ -20,5 +20,9 @@ ) ) -(start 0) +(func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) +) +(start 0) +(memory (export "memory") 0 0) diff --git a/arbitrator/prover/test-cases/user.wat b/arbitrator/prover/test-cases/user.wat new file mode 100644 index 0000000000..9ecb4dcc45 --- /dev/null +++ b/arbitrator/prover/test-cases/user.wat @@ -0,0 +1,53 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "storage_load_bytes32" (func $storage_load_bytes32 (param i32 i32))) + + (func $storage_load (result i32) + i32.const 0 + i32.const 32 + call $storage_load_bytes32 + i32.const 0 + ) + (func $safe (result i32) + i32.const 5 + ) + (func $unreachable (result i32) + i32.const 0 + i64.const 4 + unreachable + ) + (func $out_of_bounds (result i32) + i32.const 0xFFFFFF + i32.load + ) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + ;; this func uses $args_len to select which func to call + + ;; only call that succeeds + (i32.eq (local.get $args_len) (i32.const 1)) + (if + (then (call $safe) (return)) + ) + + ;; reverts due to an unreachable + (i32.eq (local.get $args_len) (i32.const 2)) + (if + (then (call $unreachable) (return)) + ) + + ;; reverts due to an out of bounds memory access + (i32.eq (local.get $args_len) (i32.const 3)) + (if + (then (call $out_of_bounds) (return)) + ) + + (i32.eq (local.get $args_len) (i32.const 32)) + (if + (then (call $storage_load) (return)) + ) + + unreachable + ) + (memory (export "memory") 1 1)) diff --git a/arbitrator/stylus/Cargo.toml b/arbitrator/stylus/Cargo.toml new file mode 100644 index 0000000000..4717bd631a --- /dev/null +++ b/arbitrator/stylus/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "stylus" +version = "0.1.0" +edition = "2021" + +[dependencies] +arbutil = { path = "../arbutil/" } +brotli = { path = "../brotli" } +caller-env = { path = "../caller-env", features = ["wasmer_traits"] } +prover = { path = "../prover/", default-features = false, features = ["native"] } +wasmer = { path = "../tools/wasmer/lib/api" } +wasmer-vm = { path = "../tools/wasmer/lib/vm/" } +wasmer-types = { path = "../tools/wasmer/lib/types/" } +wasmer-compiler-singlepass = { path = "../tools/wasmer/lib/compiler-singlepass", default-features = false, features = ["std", "unwind", "avx"] } +wasmer-compiler-cranelift = { path = "../tools/wasmer/lib/compiler-cranelift" } +wasmer-compiler-llvm = { path = "../tools/wasmer/lib/compiler-llvm", optional = true } +user-host-trait = { path = "../wasm-libraries/user-host-trait/" } +derivative = "2.2.0" +parking_lot = "0.12.1" +thiserror = "1.0.33" +bincode = "1.3.3" +lazy_static.workspace = true +libc = "0.2.108" +lru.workspace = true +eyre = "0.6.5" +rand = "0.8.5" +fnv = "1.0.7" +hex = "0.4.3" + +[dev-dependencies] +num-bigint = "0.4.4" + +[features] +default = ["rayon", "singlepass_rayon"] +llvm = ["dep:wasmer-compiler-llvm"] +benchmark = [] +timings = [] +singlepass_rayon = ["prover/singlepass_rayon", "wasmer-compiler-singlepass/rayon"] +rayon = ["prover/rayon"] + +[lib] +crate-type = ["lib", "staticlib"] diff --git a/arbitrator/stylus/cbindgen.toml b/arbitrator/stylus/cbindgen.toml new file mode 100644 index 0000000000..95adfd462b --- /dev/null +++ b/arbitrator/stylus/cbindgen.toml @@ -0,0 +1,13 @@ +language = "C" +include_guard = "arbitrator_bindings" + +[parse] +parse_deps = true +include = ["arbutil", "prover", "brotli"] +extra_bindings = ["arbutil", "prover", "brotli"] + +[enum] +prefix_with_name = true + +[export] +include = ["EvmApiMethod", "EvmApiStatus"] diff --git a/arbitrator/stylus/src/benchmarks.rs b/arbitrator/stylus/src/benchmarks.rs new file mode 100644 index 0000000000..d8d558d9e2 --- /dev/null +++ b/arbitrator/stylus/src/benchmarks.rs @@ -0,0 +1,92 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use crate::{env::WasmEnv, native::NativeInstance}; +use arbutil::{crypto, format}; +use eyre::Result; +use prover::programs::{config::StylusConfig, STYLUS_ENTRY_POINT}; +use std::time::{Duration, Instant}; +use wasmer::{CompilerConfig, Imports, Instance, Module, Store}; +use wasmer_compiler_cranelift::{Cranelift, CraneliftOptLevel}; +use wasmer_compiler_singlepass::Singlepass; + +#[cfg(feature = "llvm")] +use wasmer_compiler_llvm::{LLVMOptLevel, LLVM}; + +#[test] +fn benchmark_wasmer() -> Result<()> { + // benchmarks wasmer across all compiler backends + + fn single() -> Store { + let mut compiler = Singlepass::new(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + Store::new(compiler) + } + + fn cranelift() -> Store { + let mut compiler = Cranelift::new(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + compiler.opt_level(CraneliftOptLevel::Speed); + Store::new(compiler) + } + + #[cfg(feature = "llvm")] + fn llvm() -> Store { + let mut compiler = LLVM::new(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + compiler.opt_level(LLVMOptLevel::Aggressive); + Store::new(compiler) + } + + fn emulated(mut store: Store) -> Result { + let file = "tests/keccak-100/target/wasm32-unknown-unknown/release/keccak-100.wasm"; + let wasm = std::fs::read(file)?; + let module = Module::new(&mut store, &wasm)?; + let instance = Instance::new(&mut store, &module, &Imports::new())?; + + let exports = instance.exports; + let main = exports.get_typed_function::<(i32, i32), i32>(&store, "main")?; + + let time = Instant::now(); + main.call(&mut store, 0, 0)?; + Ok(time.elapsed()) + } + + fn stylus() -> Result { + let mut args = vec![100]; // 100 keccaks + args.extend([0; 32]); + + let config = StylusConfig::default(); + let env = WasmEnv::new(config, args); + + let file = "tests/keccak/target/wasm32-unknown-unknown/release/keccak.wasm"; + let mut instance = NativeInstance::from_path(file, env)?; + let exports = &instance.exports; + let main = exports.get_typed_function::(&instance.store, STYLUS_ENTRY_POINT)?; + + let time = Instant::now(); + main.call(&mut instance.store, 1)?; + Ok(time.elapsed()) + } + + fn native() -> Duration { + let time = Instant::now(); + let mut data = [0; 32]; + for _ in 0..100 { + data = crypto::keccak(&data); + } + assert_ne!(data, [0; 32]); // keeps the optimizer from pruning `data` + time.elapsed() + } + + println!("Native: {}", format::time(native())); + #[cfg(feature = "llvm")] + println!("LLVM: {}", format::time(emulated(llvm())?)); + println!("Crane: {}", format::time(emulated(cranelift())?)); + println!("Single: {}", format::time(emulated(single())?)); + println!("Stylus: {}", format::time(stylus()?)); + Ok(()) +} diff --git a/arbitrator/stylus/src/cache.rs b/arbitrator/stylus/src/cache.rs new file mode 100644 index 0000000000..06739f2219 --- /dev/null +++ b/arbitrator/stylus/src/cache.rs @@ -0,0 +1,159 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use arbutil::Bytes32; +use eyre::Result; +use lazy_static::lazy_static; +use lru::LruCache; +use parking_lot::Mutex; +use prover::programs::config::CompileConfig; +use std::{collections::HashMap, num::NonZeroUsize}; +use wasmer::{Engine, Module, Store}; + +lazy_static! { + static ref INIT_CACHE: Mutex = Mutex::new(InitCache::new(256)); +} + +macro_rules! cache { + () => { + INIT_CACHE.lock() + }; +} + +pub struct InitCache { + long_term: HashMap, + lru: LruCache, +} + +#[derive(Clone, Copy, Hash, PartialEq, Eq)] +struct CacheKey { + module_hash: Bytes32, + version: u16, + debug: bool, +} + +impl CacheKey { + fn new(module_hash: Bytes32, version: u16, debug: bool) -> Self { + Self { + module_hash, + version, + debug, + } + } +} + +#[derive(Clone)] +struct CacheItem { + module: Module, + engine: Engine, +} + +impl CacheItem { + fn new(module: Module, engine: Engine) -> Self { + Self { module, engine } + } + + fn data(&self) -> (Module, Store) { + (self.module.clone(), Store::new(self.engine.clone())) + } +} + +impl InitCache { + // current implementation only has one tag that stores to the long_term + // future implementations might have more, but 0 is a reserved tag + // that will never modify long_term state + const ARBOS_TAG: u32 = 1; + + fn new(size: usize) -> Self { + Self { + long_term: HashMap::new(), + lru: LruCache::new(NonZeroUsize::new(size).unwrap()), + } + } + + pub fn set_lru_size(size: u32) { + cache!() + .lru + .resize(NonZeroUsize::new(size.try_into().unwrap()).unwrap()) + } + + /// Retrieves a cached value, updating items as necessary. + pub fn get(module_hash: Bytes32, version: u16, debug: bool) -> Option<(Module, Store)> { + let mut cache = cache!(); + let key = CacheKey::new(module_hash, version, debug); + + // See if the item is in the long term cache + if let Some(item) = cache.long_term.get(&key) { + return Some(item.data()); + } + + // See if the item is in the LRU cache, promoting if so + if let Some(item) = cache.lru.get(&key) { + return Some(item.data()); + } + None + } + + /// Inserts an item into the long term cache, cloning from the LRU cache if able. + /// If long_term_tag is 0 will only insert to LRU + pub fn insert( + module_hash: Bytes32, + module: &[u8], + version: u16, + long_term_tag: u32, + debug: bool, + ) -> Result<(Module, Store)> { + let key = CacheKey::new(module_hash, version, debug); + + // if in LRU, add to ArbOS + let mut cache = cache!(); + if let Some(item) = cache.long_term.get(&key) { + return Ok(item.data()); + } + if let Some(item) = cache.lru.peek(&key).cloned() { + if long_term_tag == Self::ARBOS_TAG { + cache.long_term.insert(key, item.clone()); + } else { + cache.lru.promote(&key) + } + return Ok(item.data()); + } + drop(cache); + + let engine = CompileConfig::version(version, debug).engine(); + let module = unsafe { Module::deserialize_unchecked(&engine, module)? }; + + let item = CacheItem::new(module, engine); + let data = item.data(); + let mut cache = cache!(); + if long_term_tag != Self::ARBOS_TAG { + cache.lru.put(key, item); + } else { + cache.long_term.insert(key, item); + } + Ok(data) + } + + /// Evicts an item in the long-term cache. + pub fn evict(module_hash: Bytes32, version: u16, long_term_tag: u32, debug: bool) { + if long_term_tag != Self::ARBOS_TAG { + return; + } + let key = CacheKey::new(module_hash, version, debug); + let mut cache = cache!(); + if let Some(item) = cache.long_term.remove(&key) { + cache.lru.put(key, item); + } + } + + pub fn clear_long_term(long_term_tag: u32) { + if long_term_tag != Self::ARBOS_TAG { + return; + } + let mut cache = cache!(); + let cache = &mut *cache; + for (key, item) in cache.long_term.drain() { + cache.lru.put(key, item); // not all will fit, just a heuristic + } + } +} diff --git a/arbitrator/stylus/src/env.rs b/arbitrator/stylus/src/env.rs new file mode 100644 index 0000000000..69d542070d --- /dev/null +++ b/arbitrator/stylus/src/env.rs @@ -0,0 +1,259 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use arbutil::{ + evm::{ + api::{DataReader, EvmApi}, + EvmData, + }, + pricing, +}; +use caller_env::GuestPtr; +use derivative::Derivative; +use eyre::{eyre, ErrReport}; +use prover::programs::{config::PricingParams, meter::OutOfInkError, prelude::*}; +use std::{ + fmt::Debug, + io, + marker::PhantomData, + mem::MaybeUninit, + ops::{Deref, DerefMut}, + ptr::NonNull, +}; +use thiserror::Error; +use wasmer::{FunctionEnvMut, Memory, MemoryAccessError, MemoryView, Pages, StoreMut}; +use wasmer_types::RawValue; +use wasmer_vm::VMGlobalDefinition; + +pub type WasmEnvMut<'a, D, E> = FunctionEnvMut<'a, WasmEnv>; + +#[derive(Derivative)] +#[derivative(Debug)] +pub struct WasmEnv> { + /// The instance's arguments + #[derivative(Debug(format_with = "arbutil::format::hex_fmt"))] + pub args: Vec, + /// The instance's return data + #[derivative(Debug(format_with = "arbutil::format::hex_fmt"))] + pub outs: Vec, + /// Mechanism for reading and writing the module's memory + pub memory: Option, + /// Mechanism for accessing metering-specific global state + pub meter: Option, + /// Mechanism for reading and writing permanent storage, and doing calls + pub evm_api: E, + /// Mechanism for reading EVM context data + pub evm_data: EvmData, + /// The compile time config + pub compile: CompileConfig, + /// The runtime config + pub config: Option, + // Using the unused generic parameter D in a PhantomData field + _data_reader_marker: PhantomData, +} + +impl> WasmEnv { + pub fn new( + compile: CompileConfig, + config: Option, + evm_api: E, + evm_data: EvmData, + ) -> Self { + Self { + compile, + config, + evm_api, + evm_data, + args: vec![], + outs: vec![], + memory: None, + meter: None, + _data_reader_marker: PhantomData, + } + } + + pub fn start<'a>( + env: &'a mut WasmEnvMut<'_, D, E>, + ink: u64, + ) -> Result, Escape> { + let mut info = Self::program(env)?; + info.buy_ink(pricing::HOSTIO_INK + ink)?; + Ok(info) + } + + pub fn program<'a>(env: &'a mut WasmEnvMut<'_, D, E>) -> Result, Escape> { + let (env, store) = env.data_and_store_mut(); + let memory = env.memory.clone().unwrap(); + let mut info = HostioInfo { + env, + memory, + store, + start_ink: 0, + }; + if info.env.evm_data.tracing { + info.start_ink = info.ink_ready()?; + } + Ok(info) + } + + pub fn meter_mut(&mut self) -> &mut MeterData { + self.meter.as_mut().expect("not metered") + } + + pub fn meter(&self) -> &MeterData { + self.meter.as_ref().expect("not metered") + } +} + +#[derive(Clone, Copy, Debug)] +pub struct MeterData { + /// The amount of ink left + pub ink_left: NonNull, + /// Whether the instance has run out of ink + pub ink_status: NonNull, +} + +impl MeterData { + pub fn ink(&self) -> u64 { + unsafe { self.ink_left.as_ref().val.u64 } + } + + pub fn status(&self) -> u32 { + unsafe { self.ink_status.as_ref().val.u32 } + } + + pub fn set_ink(&mut self, ink: u64) { + unsafe { self.ink_left.as_mut().val = RawValue { u64: ink } } + } + + pub fn set_status(&mut self, status: u32) { + unsafe { self.ink_status.as_mut().val = RawValue { u32: status } } + } +} + +/// The data we're pointing to is owned by the `NativeInstance`. +/// These are simple integers whose lifetime is that of the instance. +/// Stylus is also single-threaded. +unsafe impl Send for MeterData {} + +pub struct HostioInfo<'a, D: DataReader, E: EvmApi> { + pub env: &'a mut WasmEnv, + pub memory: Memory, + pub store: StoreMut<'a>, + pub start_ink: u64, +} + +impl<'a, D: DataReader, E: EvmApi> HostioInfo<'a, D, E> { + pub fn config(&self) -> StylusConfig { + self.config.expect("no config") + } + + pub fn pricing(&self) -> PricingParams { + self.config().pricing + } + + pub fn view(&self) -> MemoryView { + self.memory.view(&self.store) + } + + pub fn memory_size(&self) -> Pages { + self.memory.ty(&self.store).minimum + } + + // TODO: use the unstable array_assum_init + pub fn read_fixed(&self, ptr: GuestPtr) -> Result<[u8; N], MemoryAccessError> { + let mut data = [MaybeUninit::uninit(); N]; + self.view().read_uninit(ptr.into(), &mut data)?; + Ok(data.map(|x| unsafe { x.assume_init() })) + } +} + +impl<'a, D: DataReader, E: EvmApi> MeteredMachine for HostioInfo<'a, D, E> { + fn ink_left(&self) -> MachineMeter { + let vm = self.env.meter(); + match vm.status() { + 0_u32 => MachineMeter::Ready(vm.ink()), + _ => MachineMeter::Exhausted, + } + } + + fn set_meter(&mut self, meter: MachineMeter) { + let vm = self.env.meter_mut(); + vm.set_ink(meter.ink()); + vm.set_status(meter.status()); + } +} + +impl<'a, D: DataReader, E: EvmApi> GasMeteredMachine for HostioInfo<'a, D, E> { + fn pricing(&self) -> PricingParams { + self.config().pricing + } +} + +impl<'a, D: DataReader, E: EvmApi> Deref for HostioInfo<'a, D, E> { + type Target = WasmEnv; + + fn deref(&self) -> &Self::Target { + self.env + } +} + +impl<'a, D: DataReader, E: EvmApi> DerefMut for HostioInfo<'a, D, E> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.env + } +} + +pub type MaybeEscape = Result<(), Escape>; + +#[derive(Error, Debug)] +pub enum Escape { + #[error("failed to access memory: `{0}`")] + Memory(MemoryAccessError), + #[error("internal error: `{0}`")] + Internal(ErrReport), + #[error("logic error: `{0}`")] + Logical(ErrReport), + #[error("out of ink")] + OutOfInk, + #[error("exit early: `{0}`")] + Exit(u32), +} + +impl Escape { + pub fn _internal(error: &'static str) -> Result { + Err(Self::Internal(eyre!(error))) + } + + pub fn logical(error: &'static str) -> Result { + Err(Self::Logical(eyre!(error))) + } + + pub fn out_of_ink() -> Result { + Err(Self::OutOfInk) + } +} + +impl From for Escape { + fn from(_: OutOfInkError) -> Self { + Self::OutOfInk + } +} + +impl From for Escape { + fn from(err: MemoryAccessError) -> Self { + Self::Memory(err) + } +} + +impl From for Escape { + fn from(err: io::Error) -> Self { + Self::Internal(eyre!(err)) + } +} + +impl From for Escape { + fn from(err: ErrReport) -> Self { + Self::Internal(err) + } +} diff --git a/arbitrator/stylus/src/evm_api.rs b/arbitrator/stylus/src/evm_api.rs new file mode 100644 index 0000000000..d267372827 --- /dev/null +++ b/arbitrator/stylus/src/evm_api.rs @@ -0,0 +1,50 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{GoSliceData, RustSlice}; +use arbutil::evm::{ + api::{EvmApiMethod, EVM_API_METHOD_REQ_OFFSET}, + req::RequestHandler, +}; + +#[repr(C)] +pub struct NativeRequestHandler { + pub handle_request_fptr: unsafe extern "C" fn( + id: usize, + req_type: u32, + data: *mut RustSlice, + gas_cost: *mut u64, + result: *mut GoSliceData, + raw_data: *mut GoSliceData, + ), + pub id: usize, +} + +macro_rules! ptr { + ($expr:expr) => { + &mut $expr as *mut _ + }; +} + +impl RequestHandler for NativeRequestHandler { + fn request( + &mut self, + req_type: EvmApiMethod, + req_data: impl AsRef<[u8]>, + ) -> (Vec, GoSliceData, u64) { + let mut result = GoSliceData::null(); + let mut raw_data = GoSliceData::null(); + let mut cost = 0; + unsafe { + (self.handle_request_fptr)( + self.id, + req_type as u32 + EVM_API_METHOD_REQ_OFFSET, + ptr!(RustSlice::new(req_data.as_ref())), + ptr!(cost), + ptr!(result), + ptr!(raw_data), + ) + }; + (result.slice().to_vec(), raw_data, cost) + } +} diff --git a/arbitrator/stylus/src/host.rs b/arbitrator/stylus/src/host.rs new file mode 100644 index 0000000000..7854386e23 --- /dev/null +++ b/arbitrator/stylus/src/host.rs @@ -0,0 +1,466 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +#![allow(clippy::too_many_arguments)] + +use crate::env::{Escape, HostioInfo, MaybeEscape, WasmEnv, WasmEnvMut}; +use arbutil::{ + evm::{ + api::{DataReader, EvmApi}, + EvmData, + }, + Color, +}; +use caller_env::GuestPtr; +use eyre::Result; +use prover::value::Value; +use std::{ + fmt::Display, + mem::{self, MaybeUninit}, +}; +use user_host_trait::UserHost; +use wasmer::{MemoryAccessError, WasmPtr}; + +impl<'a, DR, A> UserHost for HostioInfo<'a, DR, A> +where + DR: DataReader, + A: EvmApi, +{ + type Err = Escape; + type MemoryErr = MemoryAccessError; + type A = A; + + fn args(&self) -> &[u8] { + &self.args + } + + fn outs(&mut self) -> &mut Vec { + &mut self.outs + } + + fn evm_api(&mut self) -> &mut Self::A { + &mut self.evm_api + } + + fn evm_data(&self) -> &EvmData { + &self.evm_data + } + + fn evm_return_data_len(&mut self) -> &mut u32 { + &mut self.evm_data.return_data_len + } + + fn read_fixed( + &self, + ptr: GuestPtr, + ) -> std::result::Result<[u8; N], Self::MemoryErr> { + HostioInfo::read_fixed(self, ptr) + } + + fn read_slice(&self, ptr: GuestPtr, len: u32) -> Result, Self::MemoryErr> { + let len = len as usize; + let mut data: Vec> = Vec::with_capacity(len); + // SAFETY: read_uninit fills all available space + unsafe { + data.set_len(len); + self.view().read_uninit(ptr.into(), &mut data)?; + Ok(mem::transmute(data)) + } + } + + fn write_u32(&mut self, ptr: GuestPtr, x: u32) -> Result<(), Self::MemoryErr> { + let ptr: WasmPtr = WasmPtr::new(ptr.into()); + ptr.deref(&self.view()).write(x)?; + Ok(()) + } + + fn write_slice(&self, ptr: GuestPtr, src: &[u8]) -> Result<(), Self::MemoryErr> { + self.view().write(ptr.into(), src) + } + + fn say(&self, text: D) { + println!("{} {text}", "Stylus says:".yellow()); + } + + fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], end_ink: u64) { + let start_ink = self.start_ink; + self.evm_api + .capture_hostio(name, args, outs, start_ink, end_ink); + } +} + +macro_rules! hostio { + ($env:expr, $($func:tt)*) => { + WasmEnv::program(&mut $env)?.$($func)* + }; +} + +pub(crate) fn read_args>( + mut env: WasmEnvMut, + ptr: GuestPtr, +) -> MaybeEscape { + hostio!(env, read_args(ptr)) +} + +pub(crate) fn write_result>( + mut env: WasmEnvMut, + ptr: GuestPtr, + len: u32, +) -> MaybeEscape { + hostio!(env, write_result(ptr, len)) +} + +pub(crate) fn exit_early>( + mut env: WasmEnvMut, + status: u32, +) -> MaybeEscape { + hostio!(env, exit_early(status))?; + Err(Escape::Exit(status)) +} + +pub(crate) fn storage_load_bytes32>( + mut env: WasmEnvMut, + key: GuestPtr, + dest: GuestPtr, +) -> MaybeEscape { + hostio!(env, storage_load_bytes32(key, dest)) +} + +pub(crate) fn storage_cache_bytes32>( + mut env: WasmEnvMut, + key: GuestPtr, + value: GuestPtr, +) -> MaybeEscape { + hostio!(env, storage_cache_bytes32(key, value)) +} + +pub(crate) fn storage_flush_cache>( + mut env: WasmEnvMut, + clear: u32, +) -> MaybeEscape { + hostio!(env, storage_flush_cache(clear != 0)) +} + +pub(crate) fn transient_load_bytes32>( + mut env: WasmEnvMut, + key: GuestPtr, + dest: GuestPtr, +) -> MaybeEscape { + hostio!(env, transient_load_bytes32(key, dest)) +} + +pub(crate) fn transient_store_bytes32>( + mut env: WasmEnvMut, + key: GuestPtr, + value: GuestPtr, +) -> MaybeEscape { + hostio!(env, transient_store_bytes32(key, value)) +} + +pub(crate) fn call_contract>( + mut env: WasmEnvMut, + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + value: GuestPtr, + gas: u64, + ret_len: GuestPtr, +) -> Result { + hostio!( + env, + call_contract(contract, data, data_len, value, gas, ret_len) + ) +} + +pub(crate) fn delegate_call_contract>( + mut env: WasmEnvMut, + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + gas: u64, + ret_len: GuestPtr, +) -> Result { + hostio!( + env, + delegate_call_contract(contract, data, data_len, gas, ret_len) + ) +} + +pub(crate) fn static_call_contract>( + mut env: WasmEnvMut, + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + gas: u64, + ret_len: GuestPtr, +) -> Result { + hostio!( + env, + static_call_contract(contract, data, data_len, gas, ret_len) + ) +} + +pub(crate) fn create1>( + mut env: WasmEnvMut, + code: GuestPtr, + code_len: u32, + endowment: GuestPtr, + contract: GuestPtr, + revert_len: GuestPtr, +) -> MaybeEscape { + hostio!( + env, + create1(code, code_len, endowment, contract, revert_len) + ) +} + +pub(crate) fn create2>( + mut env: WasmEnvMut, + code: GuestPtr, + code_len: u32, + endowment: GuestPtr, + salt: GuestPtr, + contract: GuestPtr, + revert_len: GuestPtr, +) -> MaybeEscape { + hostio!( + env, + create2(code, code_len, endowment, salt, contract, revert_len) + ) +} + +pub(crate) fn read_return_data>( + mut env: WasmEnvMut, + dest: GuestPtr, + offset: u32, + size: u32, +) -> Result { + hostio!(env, read_return_data(dest, offset, size)) +} + +pub(crate) fn return_data_size>( + mut env: WasmEnvMut, +) -> Result { + hostio!(env, return_data_size()) +} + +pub(crate) fn emit_log>( + mut env: WasmEnvMut, + data: GuestPtr, + len: u32, + topics: u32, +) -> MaybeEscape { + hostio!(env, emit_log(data, len, topics)) +} + +pub(crate) fn account_balance>( + mut env: WasmEnvMut, + address: GuestPtr, + ptr: GuestPtr, +) -> MaybeEscape { + hostio!(env, account_balance(address, ptr)) +} + +pub(crate) fn account_code>( + mut env: WasmEnvMut, + address: GuestPtr, + offset: u32, + size: u32, + code: GuestPtr, +) -> Result { + hostio!(env, account_code(address, offset, size, code)) +} + +pub(crate) fn account_code_size>( + mut env: WasmEnvMut, + address: GuestPtr, +) -> Result { + hostio!(env, account_code_size(address)) +} + +pub(crate) fn account_codehash>( + mut env: WasmEnvMut, + address: GuestPtr, + ptr: GuestPtr, +) -> MaybeEscape { + hostio!(env, account_codehash(address, ptr)) +} + +pub(crate) fn block_basefee>( + mut env: WasmEnvMut, + ptr: GuestPtr, +) -> MaybeEscape { + hostio!(env, block_basefee(ptr)) +} + +pub(crate) fn block_coinbase>( + mut env: WasmEnvMut, + ptr: GuestPtr, +) -> MaybeEscape { + hostio!(env, block_coinbase(ptr)) +} + +pub(crate) fn block_gas_limit>( + mut env: WasmEnvMut, +) -> Result { + hostio!(env, block_gas_limit()) +} + +pub(crate) fn block_number>( + mut env: WasmEnvMut, +) -> Result { + hostio!(env, block_number()) +} + +pub(crate) fn block_timestamp>( + mut env: WasmEnvMut, +) -> Result { + hostio!(env, block_timestamp()) +} + +pub(crate) fn chainid>( + mut env: WasmEnvMut, +) -> Result { + hostio!(env, chainid()) +} + +pub(crate) fn contract_address>( + mut env: WasmEnvMut, + ptr: GuestPtr, +) -> MaybeEscape { + hostio!(env, contract_address(ptr)) +} + +pub(crate) fn evm_gas_left>( + mut env: WasmEnvMut, +) -> Result { + hostio!(env, evm_gas_left()) +} + +pub(crate) fn evm_ink_left>( + mut env: WasmEnvMut, +) -> Result { + hostio!(env, evm_ink_left()) +} + +pub(crate) fn math_div>( + mut env: WasmEnvMut, + value: GuestPtr, + divisor: GuestPtr, +) -> MaybeEscape { + hostio!(env, math_div(value, divisor)) +} + +pub(crate) fn math_mod>( + mut env: WasmEnvMut, + value: GuestPtr, + modulus: GuestPtr, +) -> MaybeEscape { + hostio!(env, math_mod(value, modulus)) +} + +pub(crate) fn math_pow>( + mut env: WasmEnvMut, + value: GuestPtr, + exponent: GuestPtr, +) -> MaybeEscape { + hostio!(env, math_pow(value, exponent)) +} + +pub(crate) fn math_add_mod>( + mut env: WasmEnvMut, + value: GuestPtr, + addend: GuestPtr, + modulus: GuestPtr, +) -> MaybeEscape { + hostio!(env, math_add_mod(value, addend, modulus)) +} + +pub(crate) fn math_mul_mod>( + mut env: WasmEnvMut, + value: GuestPtr, + multiplier: GuestPtr, + modulus: GuestPtr, +) -> MaybeEscape { + hostio!(env, math_mul_mod(value, multiplier, modulus)) +} + +pub(crate) fn msg_reentrant>( + mut env: WasmEnvMut, +) -> Result { + hostio!(env, msg_reentrant()) +} + +pub(crate) fn msg_sender>( + mut env: WasmEnvMut, + ptr: GuestPtr, +) -> MaybeEscape { + hostio!(env, msg_sender(ptr)) +} + +pub(crate) fn msg_value>( + mut env: WasmEnvMut, + ptr: GuestPtr, +) -> MaybeEscape { + hostio!(env, msg_value(ptr)) +} + +pub(crate) fn native_keccak256>( + mut env: WasmEnvMut, + input: GuestPtr, + len: u32, + output: GuestPtr, +) -> MaybeEscape { + hostio!(env, native_keccak256(input, len, output)) +} + +pub(crate) fn tx_gas_price>( + mut env: WasmEnvMut, + ptr: GuestPtr, +) -> MaybeEscape { + hostio!(env, tx_gas_price(ptr)) +} + +pub(crate) fn tx_ink_price>( + mut env: WasmEnvMut, +) -> Result { + hostio!(env, tx_ink_price()) +} + +pub(crate) fn tx_origin>( + mut env: WasmEnvMut, + ptr: GuestPtr, +) -> MaybeEscape { + hostio!(env, tx_origin(ptr)) +} + +pub(crate) fn pay_for_memory_grow>( + mut env: WasmEnvMut, + pages: u16, +) -> MaybeEscape { + hostio!(env, pay_for_memory_grow(pages)) +} + +pub(crate) fn console_log_text>( + mut env: WasmEnvMut, + ptr: GuestPtr, + len: u32, +) -> MaybeEscape { + hostio!(env, console_log_text(ptr, len)) +} + +pub(crate) fn console_log, T: Into>( + mut env: WasmEnvMut, + value: T, +) -> MaybeEscape { + hostio!(env, console_log(value)) +} + +pub(crate) fn console_tee, T: Into + Copy>( + mut env: WasmEnvMut, + value: T, +) -> Result { + hostio!(env, console_tee(value)) +} + +pub(crate) fn null_host>(_: WasmEnvMut) {} diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs new file mode 100644 index 0000000000..3c53359f8b --- /dev/null +++ b/arbitrator/stylus/src/lib.rs @@ -0,0 +1,276 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use arbutil::{ + evm::{ + api::DataReader, + req::EvmApiRequestor, + user::{UserOutcome, UserOutcomeKind}, + EvmData, + }, + format::DebugBytes, + Bytes32, +}; +use cache::InitCache; +use evm_api::NativeRequestHandler; +use eyre::ErrReport; +use native::NativeInstance; +use prover::programs::{prelude::*, StylusData}; +use run::RunProgram; +use std::{marker::PhantomData, mem, ptr}; + +pub use brotli; +pub use prover; + +pub mod env; +pub mod host; +pub mod native; +pub mod run; + +mod cache; +mod evm_api; +mod util; + +#[cfg(test)] +mod test; + +#[cfg(all(test, feature = "benchmark"))] +mod benchmarks; + +#[derive(Clone, Copy)] +#[repr(C)] +pub struct GoSliceData { + /// Points to data owned by Go. + ptr: *const u8, + /// The length in bytes. + len: usize, +} + +/// The data we're pointing to is owned by Go and has a lifetime no shorter than the current program. +unsafe impl Send for GoSliceData {} + +impl GoSliceData { + pub fn null() -> Self { + Self { + ptr: ptr::null(), + len: 0, + } + } + + fn slice(&self) -> &[u8] { + if self.len == 0 { + return &[]; + } + unsafe { std::slice::from_raw_parts(self.ptr, self.len) } + } +} + +impl DataReader for GoSliceData { + fn slice(&self) -> &[u8] { + if self.len == 0 { + return &[]; + } + unsafe { std::slice::from_raw_parts(self.ptr, self.len) } + } +} + +#[repr(C)] +pub struct RustSlice<'a> { + ptr: *const u8, + len: usize, + phantom: PhantomData<&'a [u8]>, +} + +impl<'a> RustSlice<'a> { + fn new(slice: &'a [u8]) -> Self { + Self { + ptr: slice.as_ptr(), + len: slice.len(), + phantom: PhantomData, + } + } +} + +#[repr(C)] +pub struct RustBytes { + ptr: *mut u8, + len: usize, + cap: usize, +} + +impl RustBytes { + unsafe fn into_vec(self) -> Vec { + Vec::from_raw_parts(self.ptr, self.len, self.cap) + } + + unsafe fn write(&mut self, mut vec: Vec) { + self.ptr = vec.as_mut_ptr(); + self.len = vec.len(); + self.cap = vec.capacity(); + mem::forget(vec); + } + + unsafe fn write_err(&mut self, err: ErrReport) -> UserOutcomeKind { + self.write(err.debug_bytes()); + UserOutcomeKind::Failure + } + + unsafe fn write_outcome(&mut self, outcome: UserOutcome) -> UserOutcomeKind { + let (status, outs) = outcome.into_data(); + self.write(outs); + status + } +} + +/// Instruments and "activates" a user wasm. +/// +/// The `output` is either the serialized asm & module pair or an error string. +/// Returns consensus info such as the module hash and footprint on success. +/// +/// Note that this operation costs gas and is limited by the amount supplied via the `gas` pointer. +/// The amount left is written back at the end of the call. +/// +/// # Safety +/// +/// `output`, `asm_len`, `module_hash`, `footprint`, and `gas` must not be null. +#[no_mangle] +pub unsafe extern "C" fn stylus_activate( + wasm: GoSliceData, + page_limit: u16, + version: u16, + debug: bool, + output: *mut RustBytes, + asm_len: *mut usize, + codehash: *const Bytes32, + module_hash: *mut Bytes32, + stylus_data: *mut StylusData, + gas: *mut u64, +) -> UserOutcomeKind { + let wasm = wasm.slice(); + let output = &mut *output; + let module_hash = &mut *module_hash; + let codehash = &*codehash; + let gas = &mut *gas; + + let (asm, module, info) = + match native::activate(wasm, codehash, version, page_limit, debug, gas) { + Ok(val) => val, + Err(err) => return output.write_err(err), + }; + *asm_len = asm.len(); + *module_hash = module.hash(); + *stylus_data = info; + + let mut data = asm; + data.extend(&*module.into_bytes()); + output.write(data); + UserOutcomeKind::Success +} + +/// Calls an activated user program. +/// +/// # Safety +/// +/// `module` must represent a valid module produced from `stylus_activate`. +/// `output` and `gas` must not be null. +#[no_mangle] +pub unsafe extern "C" fn stylus_call( + module: GoSliceData, + calldata: GoSliceData, + config: StylusConfig, + req_handler: NativeRequestHandler, + evm_data: EvmData, + debug_chain: bool, + output: *mut RustBytes, + gas: *mut u64, + long_term_tag: u32, +) -> UserOutcomeKind { + let module = module.slice(); + let calldata = calldata.slice().to_vec(); + let evm_api = EvmApiRequestor::new(req_handler); + let pricing = config.pricing; + let output = &mut *output; + let ink = pricing.gas_to_ink(*gas); + + // Safety: module came from compile_user_wasm and we've paid for memory expansion + let instance = unsafe { + NativeInstance::deserialize_cached( + module, + config.version, + evm_api, + evm_data, + long_term_tag, + debug_chain, + ) + }; + let mut instance = match instance { + Ok(instance) => instance, + Err(error) => util::panic_with_wasm(module, error.wrap_err("init failed")), + }; + + let status = match instance.run_main(&calldata, config, ink) { + Err(e) | Ok(UserOutcome::Failure(e)) => output.write_err(e.wrap_err("call failed")), + Ok(outcome) => output.write_outcome(outcome), + }; + let ink_left = match status { + UserOutcomeKind::OutOfStack => 0, // take all gas when out of stack + _ => instance.ink_left().into(), + }; + *gas = pricing.ink_to_gas(ink_left); + status +} + +/// resize lru +#[no_mangle] +pub extern "C" fn stylus_cache_lru_resize(size: u32) { + InitCache::set_lru_size(size); +} + +/// Caches an activated user program. +/// +/// # Safety +/// +/// `module` must represent a valid module produced from `stylus_activate`. +/// arbos_tag: a tag for arbos cache. 0 won't affect real caching +/// currently only if tag==1 caching will be affected +#[no_mangle] +pub unsafe extern "C" fn stylus_cache_module( + module: GoSliceData, + module_hash: Bytes32, + version: u16, + arbos_tag: u32, + debug: bool, +) { + if let Err(error) = InitCache::insert(module_hash, module.slice(), version, arbos_tag, debug) { + panic!("tried to cache invalid asm!: {error}"); + } +} + +/// Evicts an activated user program from the init cache. +#[no_mangle] +pub extern "C" fn stylus_evict_module( + module_hash: Bytes32, + version: u16, + arbos_tag: u32, + debug: bool, +) { + InitCache::evict(module_hash, version, arbos_tag, debug); +} + +/// Reorgs the init cache. This will likely never happen. +#[no_mangle] +pub extern "C" fn stylus_reorg_vm(_block: u64, arbos_tag: u32) { + InitCache::clear_long_term(arbos_tag); +} + +/// Frees the vector. Does nothing when the vector is null. +/// +/// # Safety +/// +/// Must only be called once per vec. +#[no_mangle] +pub unsafe extern "C" fn stylus_drop_vec(vec: RustBytes) { + if !vec.ptr.is_null() { + mem::drop(vec.into_vec()) + } +} diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs new file mode 100644 index 0000000000..2858d59fdc --- /dev/null +++ b/arbitrator/stylus/src/native.rs @@ -0,0 +1,454 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{ + cache::InitCache, + env::{MeterData, WasmEnv}, + host, util, +}; +use arbutil::{ + evm::{ + api::{DataReader, EvmApi}, + EvmData, + }, + operator::OperatorCode, + Bytes32, Color, +}; +use eyre::{bail, eyre, ErrReport, Result}; +use prover::{ + machine::Module as ProverModule, + programs::{ + config::PricingParams, + counter::{Counter, CountingMachine, OP_OFFSETS}, + depth::STYLUS_STACK_LEFT, + meter::{STYLUS_INK_LEFT, STYLUS_INK_STATUS}, + prelude::*, + start::StartMover, + StylusData, + }, +}; +use std::{ + collections::BTreeMap, + fmt::Debug, + ops::{Deref, DerefMut}, +}; +use wasmer::{ + imports, AsStoreMut, Function, FunctionEnv, Instance, Memory, Module, Pages, Store, + TypedFunction, Value, WasmTypeList, +}; +use wasmer_vm::VMExtern; + +#[derive(Debug)] +pub struct NativeInstance> { + pub instance: Instance, + pub store: Store, + pub env: FunctionEnv>, +} + +impl> NativeInstance { + pub fn new(instance: Instance, store: Store, env: FunctionEnv>) -> Self { + let mut native = Self { + instance, + store, + env, + }; + if let Some(config) = native.env().config { + native.set_stack(config.max_depth); + } + native + } + + pub fn env(&self) -> &WasmEnv { + self.env.as_ref(&self.store) + } + + pub fn env_mut(&mut self) -> &mut WasmEnv { + self.env.as_mut(&mut self.store) + } + + pub fn config(&self) -> StylusConfig { + self.env().config.expect("no config") + } + + pub fn memory(&self) -> Memory { + self.env().memory.as_ref().unwrap().clone() + } + + pub fn memory_size(&self) -> Pages { + self.memory().ty(&self.store).minimum + } + + pub fn read_slice(&self, mem: &str, ptr: usize, len: usize) -> Result> { + let memory = self.exports.get_memory(mem)?; + let memory = memory.view(&self.store); + let mut data = vec![0; len]; + memory.read(ptr as u64, &mut data)?; + Ok(data) + } + + /// Creates a `NativeInstance` from a serialized module. + /// + /// # Safety + /// + /// `module` must represent a valid module. + pub unsafe fn deserialize( + module: &[u8], + compile: CompileConfig, + evm: E, + evm_data: EvmData, + ) -> Result { + let env = WasmEnv::new(compile, None, evm, evm_data); + let store = env.compile.store(); + let module = unsafe { Module::deserialize_unchecked(&store, module)? }; + Self::from_module(module, store, env) + } + + /// Creates a `NativeInstance` from a serialized module, or from a cached one if known. + /// + /// # Safety + /// + /// `module` must represent a valid module. + pub unsafe fn deserialize_cached( + module: &[u8], + version: u16, + evm: E, + evm_data: EvmData, + mut long_term_tag: u32, + debug: bool, + ) -> Result { + let compile = CompileConfig::version(version, debug); + let env = WasmEnv::new(compile, None, evm, evm_data); + let module_hash = env.evm_data.module_hash; + + if let Some((module, store)) = InitCache::get(module_hash, version, debug) { + return Self::from_module(module, store, env); + } + if !env.evm_data.cached { + long_term_tag = 0; + } + let (module, store) = + InitCache::insert(module_hash, module, version, long_term_tag, debug)?; + Self::from_module(module, store, env) + } + + pub fn from_path( + path: &str, + evm_api: E, + evm_data: EvmData, + compile: &CompileConfig, + config: StylusConfig, + ) -> Result { + let env = WasmEnv::new(compile.clone(), Some(config), evm_api, evm_data); + let store = env.compile.store(); + let wat_or_wasm = std::fs::read(path)?; + let module = Module::new(&store, wat_or_wasm)?; + Self::from_module(module, store, env) + } + + fn from_module(module: Module, mut store: Store, env: WasmEnv) -> Result { + let debug_funcs = env.compile.debug.debug_funcs; + let func_env = FunctionEnv::new(&mut store, env); + macro_rules! func { + ($func:expr) => { + Function::new_typed_with_env(&mut store, &func_env, $func) + }; + } + let mut imports = imports! { + "vm_hooks" => { + "read_args" => func!(host::read_args), + "write_result" => func!(host::write_result), + "exit_early" => func!(host::exit_early), + "storage_load_bytes32" => func!(host::storage_load_bytes32), + "storage_cache_bytes32" => func!(host::storage_cache_bytes32), + "storage_flush_cache" => func!(host::storage_flush_cache), + "transient_load_bytes32" => func!(host::transient_load_bytes32), + "transient_store_bytes32" => func!(host::transient_store_bytes32), + "call_contract" => func!(host::call_contract), + "delegate_call_contract" => func!(host::delegate_call_contract), + "static_call_contract" => func!(host::static_call_contract), + "create1" => func!(host::create1), + "create2" => func!(host::create2), + "read_return_data" => func!(host::read_return_data), + "return_data_size" => func!(host::return_data_size), + "emit_log" => func!(host::emit_log), + "account_balance" => func!(host::account_balance), + "account_code" => func!(host::account_code), + "account_codehash" => func!(host::account_codehash), + "account_code_size" => func!(host::account_code_size), + "evm_gas_left" => func!(host::evm_gas_left), + "evm_ink_left" => func!(host::evm_ink_left), + "block_basefee" => func!(host::block_basefee), + "chainid" => func!(host::chainid), + "block_coinbase" => func!(host::block_coinbase), + "block_gas_limit" => func!(host::block_gas_limit), + "block_number" => func!(host::block_number), + "block_timestamp" => func!(host::block_timestamp), + "contract_address" => func!(host::contract_address), + "math_div" => func!(host::math_div), + "math_mod" => func!(host::math_mod), + "math_pow" => func!(host::math_pow), + "math_add_mod" => func!(host::math_add_mod), + "math_mul_mod" => func!(host::math_mul_mod), + "msg_reentrant" => func!(host::msg_reentrant), + "msg_sender" => func!(host::msg_sender), + "msg_value" => func!(host::msg_value), + "tx_gas_price" => func!(host::tx_gas_price), + "tx_ink_price" => func!(host::tx_ink_price), + "tx_origin" => func!(host::tx_origin), + "pay_for_memory_grow" => func!(host::pay_for_memory_grow), + "native_keccak256" => func!(host::native_keccak256), + }, + }; + if debug_funcs { + imports.define("console", "log_txt", func!(host::console_log_text)); + imports.define("console", "log_i32", func!(host::console_log::)); + imports.define("console", "log_i64", func!(host::console_log::)); + imports.define("console", "log_f32", func!(host::console_log::)); + imports.define("console", "log_f64", func!(host::console_log::)); + imports.define("console", "tee_i32", func!(host::console_tee::)); + imports.define("console", "tee_i64", func!(host::console_tee::)); + imports.define("console", "tee_f32", func!(host::console_tee::)); + imports.define("console", "tee_f64", func!(host::console_tee::)); + imports.define("debug", "null_host", func!(host::null_host)); + } + let instance = Instance::new(&mut store, &module, &imports)?; + let exports = &instance.exports; + let memory = exports.get_memory("memory")?.clone(); + + let env = func_env.as_mut(&mut store); + env.memory = Some(memory); + + let mut native = Self::new(instance, store, func_env); + native.set_meter_data(); + Ok(native) + } + + pub fn set_meter_data(&mut self) { + let store = &mut self.store; + let exports = &self.instance.exports; + + let mut expect_global = |name| { + let VMExtern::Global(sh) = exports.get_extern(name).unwrap().to_vm_extern() else { + panic!("name not found global"); + }; + sh.get(store.objects_mut()).vmglobal() + }; + let ink_left = expect_global(STYLUS_INK_LEFT); + let ink_status = expect_global(STYLUS_INK_STATUS); + + self.env_mut().meter = Some(MeterData { + ink_left, + ink_status, + }); + } + + pub fn get_global(&mut self, name: &str) -> Result + where + T: TryFrom, + T::Error: Debug, + { + let store = &mut self.store.as_store_mut(); + let Ok(global) = self.instance.exports.get_global(name) else { + bail!("global {} does not exist", name.red()) + }; + let ty = global.get(store); + + ty.try_into() + .map_err(|_| eyre!("global {} has the wrong type", name.red())) + } + + pub fn set_global(&mut self, name: &str, value: T) -> Result<()> + where + T: Into, + { + let store = &mut self.store.as_store_mut(); + let Ok(global) = self.instance.exports.get_global(name) else { + bail!("global {} does not exist", name.red()) + }; + global.set(store, value.into()).map_err(ErrReport::msg) + } + + pub fn call_func(&mut self, func: TypedFunction<(), R>, ink: u64) -> Result + where + R: WasmTypeList, + { + self.set_ink(ink); + Ok(func.call(&mut self.store)?) + } +} + +impl> Deref for NativeInstance { + type Target = Instance; + + fn deref(&self) -> &Self::Target { + &self.instance + } +} + +impl> DerefMut for NativeInstance { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.instance + } +} + +impl> MeteredMachine for NativeInstance { + fn ink_left(&self) -> MachineMeter { + let vm = self.env().meter(); + match vm.status() { + 0 => MachineMeter::Ready(vm.ink()), + _ => MachineMeter::Exhausted, + } + } + + fn set_meter(&mut self, meter: MachineMeter) { + let vm = self.env_mut().meter_mut(); + vm.set_ink(meter.ink()); + vm.set_status(meter.status()); + } +} + +impl> GasMeteredMachine for NativeInstance { + fn pricing(&self) -> PricingParams { + self.env().config.unwrap().pricing + } +} + +impl> CountingMachine for NativeInstance { + fn operator_counts(&mut self) -> Result> { + let mut counts = BTreeMap::new(); + + for (&op, &offset) in OP_OFFSETS.lock().iter() { + let count: u64 = self.get_global(&Counter::global_name(offset))?; + if count != 0 { + counts.insert(op, count); + } + } + Ok(counts) + } +} + +impl> DepthCheckedMachine for NativeInstance { + fn stack_left(&mut self) -> u32 { + self.get_global(STYLUS_STACK_LEFT).unwrap() + } + + fn set_stack(&mut self, size: u32) { + self.set_global(STYLUS_STACK_LEFT, size).unwrap() + } +} + +impl> StartlessMachine for NativeInstance { + fn get_start(&self) -> Result> { + let store = &self.store; + let exports = &self.instance.exports; + exports + .get_typed_function(store, StartMover::NAME) + .map_err(ErrReport::new) + } +} + +pub fn module(wasm: &[u8], compile: CompileConfig) -> Result> { + let mut store = compile.store(); + let module = Module::new(&store, wasm)?; + macro_rules! stub { + (u8 <- $($types:tt)+) => { + Function::new_typed(&mut store, $($types)+ -> u8 { panic!("incomplete import") }) + }; + (u32 <- $($types:tt)+) => { + Function::new_typed(&mut store, $($types)+ -> u32 { panic!("incomplete import") }) + }; + (u64 <- $($types:tt)+) => { + Function::new_typed(&mut store, $($types)+ -> u64 { panic!("incomplete import") }) + }; + (f32 <- $($types:tt)+) => { + Function::new_typed(&mut store, $($types)+ -> f32 { panic!("incomplete import") }) + }; + (f64 <- $($types:tt)+) => { + Function::new_typed(&mut store, $($types)+ -> f64 { panic!("incomplete import") }) + }; + ($($types:tt)+) => { + Function::new_typed(&mut store, $($types)+ panic!("incomplete import")) + }; + } + let mut imports = imports! { + "vm_hooks" => { + "read_args" => stub!(|_: u32|), + "write_result" => stub!(|_: u32, _: u32|), + "exit_early" => stub!(|_: u32|), + "storage_load_bytes32" => stub!(|_: u32, _: u32|), + "storage_cache_bytes32" => stub!(|_: u32, _: u32|), + "storage_flush_cache" => stub!(|_: u32|), + "transient_load_bytes32" => stub!(|_: u32, _: u32|), + "transient_store_bytes32" => stub!(|_: u32, _: u32|), + "call_contract" => stub!(u8 <- |_: u32, _: u32, _: u32, _: u32, _: u64, _: u32|), + "delegate_call_contract" => stub!(u8 <- |_: u32, _: u32, _: u32, _: u64, _: u32|), + "static_call_contract" => stub!(u8 <- |_: u32, _: u32, _: u32, _: u64, _: u32|), + "create1" => stub!(|_: u32, _: u32, _: u32, _: u32, _: u32|), + "create2" => stub!(|_: u32, _: u32, _: u32, _: u32, _: u32, _: u32|), + "read_return_data" => stub!(u32 <- |_: u32, _: u32, _: u32|), + "return_data_size" => stub!(u32 <- ||), + "emit_log" => stub!(|_: u32, _: u32, _: u32|), + "account_balance" => stub!(|_: u32, _: u32|), + "account_code" => stub!(u32 <- |_: u32, _: u32, _: u32, _: u32|), + "account_codehash" => stub!(|_: u32, _: u32|), + "account_code_size" => stub!(u32 <- |_: u32|), + "evm_gas_left" => stub!(u64 <- ||), + "evm_ink_left" => stub!(u64 <- ||), + "block_basefee" => stub!(|_: u32|), + "chainid" => stub!(u64 <- ||), + "block_coinbase" => stub!(|_: u32|), + "block_gas_limit" => stub!(u64 <- ||), + "block_number" => stub!(u64 <- ||), + "block_timestamp" => stub!(u64 <- ||), + "contract_address" => stub!(|_: u32|), + "math_div" => stub!(|_: u32, _: u32|), + "math_mod" => stub!(|_: u32, _: u32|), + "math_pow" => stub!(|_: u32, _: u32|), + "math_add_mod" => stub!(|_: u32, _: u32, _: u32|), + "math_mul_mod" => stub!(|_: u32, _: u32, _: u32|), + "msg_reentrant" => stub!(u32 <- ||), + "msg_sender" => stub!(|_: u32|), + "msg_value" => stub!(|_: u32|), + "tx_gas_price" => stub!(|_: u32|), + "tx_ink_price" => stub!(u32 <- ||), + "tx_origin" => stub!(|_: u32|), + "pay_for_memory_grow" => stub!(|_: u16|), + "native_keccak256" => stub!(|_: u32, _: u32, _: u32|), + }, + }; + if compile.debug.debug_funcs { + imports.define("console", "log_txt", stub!(|_: u32, _: u32|)); + imports.define("console", "log_i32", stub!(|_: u32|)); + imports.define("console", "log_i64", stub!(|_: u64|)); + imports.define("console", "log_f32", stub!(|_: f32|)); + imports.define("console", "log_f64", stub!(|_: f64|)); + imports.define("console", "tee_i32", stub!(u32 <- |_: u32|)); + imports.define("console", "tee_i64", stub!(u64 <- |_: u64|)); + imports.define("console", "tee_f32", stub!(f32 <- |_: f32|)); + imports.define("console", "tee_f64", stub!(f64 <- |_: f64|)); + imports.define("debug", "null_host", stub!(||)); + } + Instance::new(&mut store, &module, &imports)?; + + let module = module.serialize()?; + Ok(module.to_vec()) +} + +pub fn activate( + wasm: &[u8], + codehash: &Bytes32, + version: u16, + page_limit: u16, + debug: bool, + gas: &mut u64, +) -> Result<(Vec, ProverModule, StylusData)> { + let compile = CompileConfig::version(version, debug); + let (module, stylus_data) = + ProverModule::activate(wasm, codehash, version, page_limit, debug, gas)?; + + let asm = match self::module(wasm, compile) { + Ok(asm) => asm, + Err(err) => util::panic_with_wasm(wasm, err), + }; + Ok((asm, module, stylus_data)) +} diff --git a/arbitrator/stylus/src/run.rs b/arbitrator/stylus/src/run.rs new file mode 100644 index 0000000000..8e673a25e5 --- /dev/null +++ b/arbitrator/stylus/src/run.rs @@ -0,0 +1,123 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +#![allow(clippy::redundant_closure_call)] + +use crate::{env::Escape, native::NativeInstance}; +use arbutil::evm::api::{DataReader, EvmApi}; +use arbutil::evm::user::UserOutcome; +use eyre::{eyre, Result}; +use prover::machine::Machine; +use prover::programs::{prelude::*, STYLUS_ENTRY_POINT}; + +pub trait RunProgram { + fn run_main(&mut self, args: &[u8], config: StylusConfig, ink: u64) -> Result; +} + +impl RunProgram for Machine { + fn run_main(&mut self, args: &[u8], config: StylusConfig, ink: u64) -> Result { + macro_rules! call { + ($module:expr, $func:expr, $args:expr) => { + call!($module, $func, $args, |error| UserOutcome::Failure(error)) + }; + ($module:expr, $func:expr, $args:expr, $error:expr) => {{ + match self.call_function($module, $func, $args) { + Ok(value) => value[0].try_into().unwrap(), + Err(error) => return Ok($error(error)), + } + }}; + } + + // push the args + let args_len = (args.len() as u32).into(); + let push_vec = vec![ + args_len, + config.version.into(), + config.max_depth.into(), + config.pricing.ink_price.into(), + ]; + let args_ptr = call!("user_test", "prepare", push_vec); + let user_host = self.find_module("user_test")?; + self.write_memory(user_host, args_ptr, args)?; + + self.set_ink(ink); + self.set_stack(config.max_depth); + + let status: u32 = call!("user", STYLUS_ENTRY_POINT, vec![args_len], |error| { + if self.stack_left() == 0 { + return UserOutcome::OutOfStack; + } + if self.ink_left() == MachineMeter::Exhausted { + return UserOutcome::OutOfInk; + } + UserOutcome::Failure(error) + }); + + let outs_ptr = call!("user_test", "get_outs_ptr", vec![]); + let outs_len = call!("user_test", "get_outs_len", vec![]); + let outs = self.read_memory(user_host, outs_ptr, outs_len)?.to_vec(); + + Ok(match status { + 0 => UserOutcome::Success(outs), + _ => UserOutcome::Revert(outs), + }) + } +} + +impl> RunProgram for NativeInstance { + fn run_main(&mut self, args: &[u8], config: StylusConfig, ink: u64) -> Result { + use UserOutcome::*; + + self.set_ink(ink); + self.set_stack(config.max_depth); + + let store = &mut self.store; + let env = self.env.as_mut(store); + env.args = args.to_owned(); + env.outs.clear(); + env.config = Some(config); + + if env.evm_data.tracing { + let args_len = args.len() as u32; + env.evm_api + .capture_hostio(STYLUS_ENTRY_POINT, &args_len.to_be_bytes(), &[], ink, ink); + } + + let exports = &self.instance.exports; + let main = exports.get_typed_function::(store, STYLUS_ENTRY_POINT)?; + let status = match main.call(store, args.len() as u32) { + Ok(status) => status, + Err(outcome) => { + if self.stack_left() == 0 { + return Ok(OutOfStack); + } + if self.ink_left() == MachineMeter::Exhausted { + return Ok(OutOfInk); + } + + let escape: Escape = match outcome.downcast() { + Ok(escape) => escape, + Err(error) => return Ok(Failure(eyre!(error).wrap_err("hard user error"))), + }; + match escape { + Escape::OutOfInk => return Ok(OutOfInk), + Escape::Memory(error) => return Ok(Failure(error.into())), + Escape::Internal(error) | Escape::Logical(error) => return Ok(Failure(error)), + Escape::Exit(status) => status, + } + } + }; + + let env = self.env_mut(); + if env.evm_data.tracing { + env.evm_api + .capture_hostio("user_returned", &[], &status.to_be_bytes(), ink, ink); + } + + let outs = env.outs.clone(); + Ok(match status { + 0 => UserOutcome::Success(outs), + _ => UserOutcome::Revert(outs), + }) + } +} diff --git a/arbitrator/stylus/src/test/api.rs b/arbitrator/stylus/src/test/api.rs new file mode 100644 index 0000000000..92d7317918 --- /dev/null +++ b/arbitrator/stylus/src/test/api.rs @@ -0,0 +1,210 @@ +// Copyright 2022, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{native, run::RunProgram}; +use arbutil::{ + evm::{ + api::{EvmApi, VecReader}, + user::UserOutcomeKind, + EvmData, + }, + Bytes20, Bytes32, +}; +use eyre::Result; +use parking_lot::Mutex; +use prover::programs::{memory::MemoryModel, prelude::*}; +use std::{collections::HashMap, sync::Arc}; + +use super::TestInstance; + +#[derive(Clone, Debug)] +pub(crate) struct TestEvmApi { + contracts: Arc>>>, + storage: Arc>>>, + program: Bytes20, + write_result: Arc>>, + compile: CompileConfig, + configs: Arc>>, + evm_data: EvmData, + pages: Arc>, +} + +impl TestEvmApi { + pub fn new(compile: CompileConfig) -> (TestEvmApi, EvmData) { + let program = Bytes20::default(); + let evm_data = EvmData::default(); + + let mut storage = HashMap::new(); + storage.insert(program, HashMap::new()); + + let api = TestEvmApi { + contracts: Arc::new(Mutex::new(HashMap::new())), + storage: Arc::new(Mutex::new(storage)), + program, + write_result: Arc::new(Mutex::new(vec![])), + compile, + configs: Arc::new(Mutex::new(HashMap::new())), + evm_data, + pages: Arc::new(Mutex::new((0, 0))), + }; + (api, evm_data) + } + + pub fn deploy(&mut self, address: Bytes20, config: StylusConfig, name: &str) -> Result<()> { + let file = format!("tests/{name}/target/wasm32-unknown-unknown/release/{name}.wasm"); + let wasm = std::fs::read(file)?; + let module = native::module(&wasm, self.compile.clone())?; + self.contracts.lock().insert(address, module); + self.configs.lock().insert(address, config); + Ok(()) + } + + pub fn set_pages(&mut self, open: u16) { + let mut pages = self.pages.lock(); + pages.0 = open; + pages.1 = open.max(pages.1); + } +} + +impl EvmApi for TestEvmApi { + fn get_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64) { + let storage = &mut self.storage.lock(); + let storage = storage.get_mut(&self.program).unwrap(); + let value = storage.get(&key).cloned().unwrap_or_default(); + (value, 2100) // pretend worst case + } + + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64 { + let storage = &mut self.storage.lock(); + let storage = storage.get_mut(&self.program).unwrap(); + storage.insert(key, value); + 0 + } + + fn flush_storage_cache(&mut self, _clear: bool, _gas_left: u64) -> Result { + let storage = &mut self.storage.lock(); + let storage = storage.get_mut(&self.program).unwrap(); + Ok(22100 * storage.len() as u64) // pretend worst case + } + + fn get_transient_bytes32(&mut self, _key: Bytes32) -> Bytes32 { + unimplemented!("tload not supported") + } + + fn set_transient_bytes32(&mut self, _key: Bytes32, _value: Bytes32) -> Result<()> { + unimplemented!("tstore not supported") + } + + /// Simulates a contract call. + /// Note: this call function is for testing purposes only and deviates from onchain behavior. + fn contract_call( + &mut self, + contract: Bytes20, + calldata: &[u8], + _gas_left: u64, + gas_req: u64, + _value: Bytes32, + ) -> (u32, u64, UserOutcomeKind) { + let compile = self.compile.clone(); + let evm_data = self.evm_data; + let config = *self.configs.lock().get(&contract).unwrap(); + let gas = gas_req; // Not consensus behavior + + let mut native = unsafe { + let contracts = self.contracts.lock(); + let module = contracts.get(&contract).unwrap(); + TestInstance::deserialize(module, compile, self.clone(), evm_data).unwrap() + }; + + let ink = config.pricing.gas_to_ink(gas); + let outcome = native.run_main(calldata, config, ink).unwrap(); + let (status, outs) = outcome.into_data(); + let outs_len = outs.len() as u32; + + let ink_left: u64 = native.ink_left().into(); + let gas_left = config.pricing.ink_to_gas(ink_left); + *self.write_result.lock() = outs; + (outs_len, gas - gas_left, status) + } + + fn delegate_call( + &mut self, + _contract: Bytes20, + _calldata: &[u8], + _gas_left: u64, + _gas_req: u64, + ) -> (u32, u64, UserOutcomeKind) { + todo!("delegate call not yet supported") + } + + fn static_call( + &mut self, + contract: Bytes20, + calldata: &[u8], + gas_left: u64, + gas_req: u64, + ) -> (u32, u64, UserOutcomeKind) { + println!("note: overriding static call with call"); + self.contract_call(contract, calldata, gas_left, gas_req, Bytes32::default()) + } + + fn create1( + &mut self, + _code: Vec, + _endowment: Bytes32, + _gas: u64, + ) -> (Result, u32, u64) { + unimplemented!("create1 not supported") + } + + fn create2( + &mut self, + _code: Vec, + _endowment: Bytes32, + _salt: Bytes32, + _gas: u64, + ) -> (Result, u32, u64) { + unimplemented!("create2 not supported") + } + + fn get_return_data(&self) -> VecReader { + VecReader::new(self.write_result.lock().clone()) + } + + fn emit_log(&mut self, _data: Vec, _topics: u32) -> Result<()> { + Ok(()) // pretend a log was emitted + } + + fn account_balance(&mut self, _address: Bytes20) -> (Bytes32, u64) { + unimplemented!() + } + + fn account_code(&mut self, _address: Bytes20, _gas_left: u64) -> (VecReader, u64) { + unimplemented!() + } + + fn account_codehash(&mut self, _address: Bytes20) -> (Bytes32, u64) { + unimplemented!() + } + + fn add_pages(&mut self, new: u16) -> u64 { + let model = MemoryModel::new(2, 1000); + let (open, ever) = *self.pages.lock(); + + let mut pages = self.pages.lock(); + pages.0 = pages.0.saturating_add(new); + pages.1 = pages.1.max(pages.0); + model.gas_cost(new, open, ever) + } + + fn capture_hostio( + &mut self, + _name: &str, + _args: &[u8], + _outs: &[u8], + _start_ink: u64, + _end_ink: u64, + ) { + unimplemented!() + } +} diff --git a/arbitrator/stylus/src/test/misc.rs b/arbitrator/stylus/src/test/misc.rs new file mode 100644 index 0000000000..ae44a885f0 --- /dev/null +++ b/arbitrator/stylus/src/test/misc.rs @@ -0,0 +1,82 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use super::test_configs; +use crate::{ + env::{Escape, MaybeEscape}, + native::NativeInstance, + test::{check_instrumentation, new_test_machine}, +}; +use eyre::Result; +use prover::programs::{prelude::*, start::StartMover}; +use wasmer::{imports, Function}; + +#[test] +fn test_bulk_memory() -> Result<()> { + let (compile, config, ink) = test_configs(); + let mut store = compile.store(); + let filename = "../prover/test-cases/bulk-memory.wat"; + let imports = imports! { + "env" => { + "wavm_halt_and_set_finished" => Function::new_typed(&mut store, || -> MaybeEscape { Escape::logical("done") }), + }, + }; + + let mut native = NativeInstance::new_from_store(filename, store, imports)?; + native.set_meter_data(); + + let starter = native.get_start()?; + native.set_stack(config.max_depth); + native.set_ink(ink); + starter.call(&mut native.store).unwrap_err(); + assert_ne!(native.ink_left(), MachineMeter::Exhausted); + + let expected = "0000080808050205000002020500020508000000000000000000000000000000"; + let data = native.read_slice("memory", 0x1000, 32)?; + assert_eq!(expected, hex::encode(data)); + + let mut machine = new_test_machine(filename, &compile)?; + let module = machine.find_module("user")?; + drop(machine.call_user_func("start", vec![], ink).unwrap_err()); // should halt + let data = machine.read_memory(module, 0x1000, 32)?; + assert_eq!(expected, hex::encode(data)); + + check_instrumentation(native, machine) +} + +#[test] +fn test_bulk_memory_oob() -> Result<()> { + let filename = "tests/bulk-memory-oob.wat"; + let (compile, _, ink) = test_configs(); + + let mut machine = new_test_machine(filename, &compile)?; + let mut native = NativeInstance::new_test(filename, compile)?; + let module = machine.find_module("user")?; + + let oobs = ["fill", "copy_left", "copy_right", "copy_same"]; + for oob in &oobs { + drop(machine.call_user_func(oob, vec![], ink).unwrap_err()); + + let exports = &native.instance.exports; + let oob = exports.get_typed_function::<(), ()>(&native.store, oob)?; + let err = format!("{}", native.call_func(oob, ink).unwrap_err()); + assert!(err.contains("out of bounds memory access")); + } + assert_eq!("0102", hex::encode(native.read_slice("memory", 0xfffe, 2)?)); + assert_eq!("0102", hex::encode(machine.read_memory(module, 0xfffe, 2)?)); + check_instrumentation(native, machine) +} + +#[test] +fn test_console() -> Result<()> { + let filename = "tests/console.wat"; + let (compile, config, ink) = test_configs(); + + let mut native = NativeInstance::new_linked(filename, &compile, config)?; + let starter = native.get_start()?; + native.call_func(starter, ink)?; + + let mut machine = new_test_machine(filename, &compile)?; + machine.call_user_func(StartMover::NAME, vec![], ink)?; + check_instrumentation(native, machine) +} diff --git a/arbitrator/stylus/src/test/mod.rs b/arbitrator/stylus/src/test/mod.rs new file mode 100644 index 0000000000..d7f3248d31 --- /dev/null +++ b/arbitrator/stylus/src/test/mod.rs @@ -0,0 +1,196 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{env::WasmEnv, native::NativeInstance, run::RunProgram, test::api::TestEvmApi}; +use arbutil::{ + evm::{api::VecReader, user::UserOutcome}, + Bytes20, Bytes32, Color, +}; +use eyre::{bail, Result}; +use prover::{ + machine::GlobalState, + programs::{config::SigMap, prelude::*}, + Machine, +}; +use rand::prelude::*; +use std::{collections::HashMap, path::Path, sync::Arc}; +use wasmer::{ + imports, wasmparser::Operator, CompilerConfig, Function, FunctionEnv, Imports, Instance, + Module, Store, +}; +use wasmer_compiler_singlepass::Singlepass; + +mod api; +mod misc; +mod native; +mod sdk; +mod wavm; + +#[cfg(feature = "timings")] +mod timings; + +type TestInstance = NativeInstance; + +impl TestInstance { + fn new_test(path: &str, compile: CompileConfig) -> Result { + let mut store = compile.store(); + let imports = imports! { + "test" => { + "noop" => Function::new_typed(&mut store, || {}), + }, + }; + let mut native = Self::new_from_store(path, store, imports)?; + native.set_meter_data(); + native.set_ink(u64::MAX); + native.set_stack(u32::MAX); + Ok(native) + } + + fn new_from_store(path: &str, mut store: Store, imports: Imports) -> Result { + let wat = std::fs::read(path)?; + let module = Module::new(&store, wat)?; + let native = Instance::new(&mut store, &module, &imports)?; + Ok(Self::new_sans_env(native, store)) + } + + fn new_vanilla(path: &str) -> Result { + let mut compiler = Singlepass::new(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + + let mut store = Store::new(compiler); + let wat = std::fs::read(path)?; + let module = Module::new(&store, wat)?; + let instance = Instance::new(&mut store, &module, &Imports::new())?; + Ok(Self::new_sans_env(instance, store)) + } + + fn new_sans_env(instance: Instance, mut store: Store) -> Self { + let compile = CompileConfig::default(); + let (evm, evm_data) = TestEvmApi::new(compile.clone()); + let env = FunctionEnv::new(&mut store, WasmEnv::new(compile, None, evm, evm_data)); + Self::new(instance, store, env) + } + + fn new_linked( + path: impl AsRef, + compile: &CompileConfig, + config: StylusConfig, + ) -> Result { + Self::new_with_evm(path.as_ref(), compile, config).map(|x| x.0) + } + + fn new_with_evm( + path: &str, + compile: &CompileConfig, + config: StylusConfig, + ) -> Result<(Self, TestEvmApi)> { + let (mut evm, evm_data) = TestEvmApi::new(compile.clone()); + let native = Self::from_path(path, evm.clone(), evm_data, compile, config)?; + let footprint = native.memory().ty(&native.store).minimum.0 as u16; + evm.set_pages(footprint); + Ok((native, evm)) + } +} + +fn expensive_add(op: &Operator, _tys: &SigMap) -> u64 { + match op { + Operator::I32Add => 100, + _ => 0, + } +} + +pub fn random_ink(min: u64) -> u64 { + rand::thread_rng().gen_range(min..=u64::MAX) +} + +pub fn random_bytes20() -> Bytes20 { + let mut data = [0; 20]; + rand::thread_rng().fill_bytes(&mut data); + data.into() +} + +fn random_bytes32() -> Bytes32 { + let mut data = [0; 32]; + rand::thread_rng().fill_bytes(&mut data); + data.into() +} + +fn test_compile_config() -> CompileConfig { + let mut compile_config = CompileConfig::version(0, true); + compile_config.debug.count_ops = true; + compile_config +} + +fn uniform_cost_config() -> StylusConfig { + let mut stylus_config = StylusConfig::default(); + stylus_config.pricing.ink_price = 10000; + stylus_config +} + +fn test_configs() -> (CompileConfig, StylusConfig, u64) { + ( + test_compile_config(), + uniform_cost_config(), + random_ink(1_000_000), + ) +} + +fn new_test_machine(path: &str, compile: &CompileConfig) -> Result { + let wat = std::fs::read(path)?; + let wasm = wasmer::wat2wasm(&wat)?; + let mut bin = prover::binary::parse(&wasm, Path::new("user"))?; + let stylus_data = bin.instrument(compile, &Bytes32::default())?; + + let wat = std::fs::read("tests/test.wat")?; + let wasm = wasmer::wat2wasm(&wat)?; + let lib = prover::binary::parse(&wasm, Path::new("test"))?; + + let mut mach = Machine::from_binaries( + &[lib], + bin, + false, + false, + true, + compile.debug.debug_funcs, + true, + GlobalState::default(), + HashMap::default(), + Arc::new(|_, _, _| panic!("tried to read preimage")), + Some(stylus_data), + )?; + mach.set_ink(u64::MAX); + mach.set_stack(u32::MAX); + Ok(mach) +} + +fn run_native(native: &mut TestInstance, args: &[u8], ink: u64) -> Result> { + let config = native.env().config.expect("no config"); + match native.run_main(args, config, ink)? { + UserOutcome::Success(output) => Ok(output), + err => bail!("user program failure: {}", err.red()), + } +} + +fn run_machine( + machine: &mut Machine, + args: &[u8], + config: StylusConfig, + ink: u64, +) -> Result> { + match machine.run_main(args, config, ink)? { + UserOutcome::Success(output) => Ok(output), + err => bail!("user program failure: {}", err.red()), + } +} + +fn check_instrumentation(mut native: TestInstance, mut machine: Machine) -> Result<()> { + assert_eq!(native.ink_left(), machine.ink_left()); + assert_eq!(native.stack_left(), machine.stack_left()); + + let native_counts = native.operator_counts()?; + let machine_counts = machine.operator_counts()?; + assert_eq!(native_counts.get(&Operator::Unreachable.into()), None); + assert_eq!(native_counts, machine_counts); + Ok(()) +} diff --git a/arbitrator/stylus/src/test/native.rs b/arbitrator/stylus/src/test/native.rs new file mode 100644 index 0000000000..503e5875fe --- /dev/null +++ b/arbitrator/stylus/src/test/native.rs @@ -0,0 +1,499 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![allow( + clippy::field_reassign_with_default, + clippy::inconsistent_digit_grouping +)] + +use crate::{ + run::RunProgram, + test::{ + check_instrumentation, random_bytes20, random_bytes32, random_ink, run_machine, run_native, + test_compile_config, test_configs, TestInstance, + }, +}; +use arbutil::{ + crypto, + evm::{ + api::EvmApi, + user::{UserOutcome, UserOutcomeKind}, + }, + format, Bytes20, Bytes32, Color, +}; +use eyre::{bail, ensure, Result}; +use prover::{ + binary, + programs::{ + counter::{Counter, CountingMachine}, + prelude::*, + start::StartMover, + MiddlewareWrapper, ModuleMod, + }, + Machine, +}; +use std::{collections::HashMap, path::Path, sync::Arc, time::Instant}; +use wasmer::wasmparser::Operator; +use wasmer::{CompilerConfig, ExportIndex, Imports, Pages, Store}; +use wasmer_compiler_singlepass::Singlepass; + +#[test] +fn test_ink() -> Result<()> { + let mut compile = test_compile_config(); + compile.pricing.costs = super::expensive_add; + + let mut native = TestInstance::new_test("tests/add.wat", compile)?; + let exports = &native.exports; + let add_one = exports.get_typed_function::(&native.store, "add_one")?; + + macro_rules! exhaust { + ($ink:expr) => { + native.set_ink($ink); + assert_eq!(native.ink_left(), MachineMeter::Ready($ink)); + assert!(add_one.call(&mut native.store, 32).is_err()); + assert_eq!(native.ink_left(), MachineMeter::Exhausted); + }; + } + + exhaust!(0); + exhaust!(50); + exhaust!(99); + + let mut ink_left = 500; + native.set_ink(ink_left); + while ink_left > 0 { + assert_eq!(native.ink_left(), MachineMeter::Ready(ink_left)); + assert_eq!(add_one.call(&mut native.store, 64)?, 65); + ink_left -= 100; + } + assert!(add_one.call(&mut native.store, 32).is_err()); + assert_eq!(native.ink_left(), MachineMeter::Exhausted); + Ok(()) +} + +#[test] +fn test_depth() -> Result<()> { + // in depth.wat + // the `depth` global equals the number of times `recurse` is called + // the `recurse` function calls itself + // the `recurse` function has 1 parameter and 2 locals + // comments show that the max depth is 3 words + + let mut native = TestInstance::new_test("tests/depth.wat", test_compile_config())?; + let exports = &native.exports; + let recurse = exports.get_typed_function::(&native.store, "recurse")?; + + let program_depth: u32 = native.get_global("depth")?; + assert_eq!(program_depth, 0); + + let mut check = |space: u32, expected: u32| -> Result<()> { + native.set_global("depth", 0)?; + native.set_stack(space); + assert_eq!(native.stack_left(), space); + + assert!(recurse.call(&mut native.store, 0).is_err()); + assert_eq!(native.stack_left(), 0); + + let program_depth: u32 = native.get_global("depth")?; + assert_eq!(program_depth, expected); + Ok(()) + }; + + let locals = 2; + let depth = 3; + let fixed = 4; + + let frame_size = locals + depth + fixed; + + check(frame_size, 0)?; // should immediately exhaust (space left <= frame) + check(frame_size + 1, 1)?; + check(2 * frame_size, 1)?; + check(2 * frame_size + 1, 2)?; + check(4 * frame_size, 3)?; + check(4 * frame_size + frame_size / 2, 4) +} + +#[test] +fn test_start() -> Result<()> { + // in start.wat + // the `status` global equals 10 at initialization + // the `start` function increments `status` + // by the spec, `start` must run at initialization + + fn check(native: &mut TestInstance, value: i32) -> Result<()> { + let status: i32 = native.get_global("status")?; + assert_eq!(status, value); + Ok(()) + } + + let mut native = TestInstance::new_vanilla("tests/start.wat")?; + check(&mut native, 11)?; + + let mut native = TestInstance::new_test("tests/start.wat", test_compile_config())?; + check(&mut native, 10)?; + + let exports = &native.exports; + let move_me = exports.get_typed_function::<(), ()>(&native.store, "move_me")?; + let starter = native.get_start()?; + let ink = random_ink(100_000); + + native.call_func(move_me, ink)?; + native.call_func(starter, ink)?; + check(&mut native, 12)?; + Ok(()) +} + +#[test] +fn test_count() -> Result<()> { + let mut compiler = Singlepass::new(); + compiler.canonicalize_nans(true); + compiler.enable_verifier(); + + let starter = StartMover::new(true); + let counter = Counter::new(); + compiler.push_middleware(Arc::new(MiddlewareWrapper::new(starter))); + compiler.push_middleware(Arc::new(MiddlewareWrapper::new(counter))); + + let mut instance = + TestInstance::new_from_store("tests/clz.wat", Store::new(compiler), Imports::new())?; + + let starter = instance.get_start()?; + starter.call(&mut instance.store)?; + + let counts = instance.operator_counts()?; + let check = |value: Operator<'_>| counts.get(&value.into()); + + use Operator::*; + assert_eq!(check(Unreachable), None); + assert_eq!(check(Drop), Some(&1)); + assert_eq!(check(I64Clz), Some(&1)); + + // test the instrumentation's contribution + assert_eq!(check(GlobalGet { global_index: 0 }), Some(&8)); // one in clz.wat + assert_eq!(check(GlobalSet { global_index: 0 }), Some(&7)); + assert_eq!(check(I64Add), Some(&7)); + assert_eq!(check(I64Const { value: 0 }), Some(&7)); + Ok(()) +} + +#[test] +fn test_import_export_safety() -> Result<()> { + // test wasms + // bad-export.wat there's a global named `stylus_ink_left` + // bad-export2.wat there's a func named `stylus_global_with_random_name` + // bad-import.wat there's an import named `stylus_global_with_random_name` + + fn check(file: &str, both: bool, instrument: bool) -> Result<()> { + let path = &Path::new(file); + let wat = std::fs::read(path)?; + let wasm = wasmer::wat2wasm(&wat)?; + let bin = binary::parse(&wasm, path); + if !instrument { + assert!(bin.is_err()); + return Ok(()); + } + + let codehash = &Bytes32::default(); + let mut compile = test_compile_config(); + let mut bin = bin?; + assert!(bin.clone().instrument(&compile, codehash).is_err()); + compile.debug.debug_info = false; + assert!(bin.instrument(&compile, &codehash).is_err()); + + if both { + assert!(TestInstance::new_test(file, compile).is_err()); + } + Ok(()) + } + + // TODO: perform all the same checks in instances + check("tests/bad-mods/bad-export.wat", true, false)?; + check("tests/bad-mods/bad-export2.wat", true, false)?; + check("tests/bad-mods/bad-export3.wat", true, true)?; + check("tests/bad-mods/bad-export4.wat", false, true)?; + check("tests/bad-mods/bad-import.wat", true, false) +} + +#[test] +fn test_module_mod() -> Result<()> { + // in module-mod.wat + // the func `void` has the signature λ() + // the func `more` has the signature λ(i32, i64) -> f32 + // the func `noop` is imported + + let file = "tests/module-mod.wat"; + let wat = std::fs::read(file)?; + let wasm = wasmer::wat2wasm(&wat)?; + let binary = binary::parse(&wasm, Path::new(file))?; + + let native = TestInstance::new_test(file, test_compile_config())?; + let module = native.module().info(); + + assert_eq!(module.all_functions()?, binary.all_functions()?); + assert_eq!(module.all_signatures()?, binary.all_signatures()?); + + let check = |name: &str| { + let Some(ExportIndex::Function(func)) = module.exports.get(name) else { + bail!("no func named {}", name.red()) + }; + let wasmer_ty = module.get_function(*func)?; + let binary_ty = binary.get_function(*func)?; + assert_eq!(wasmer_ty, binary_ty); + println!("{} {}", func.as_u32(), binary_ty.blue()); + Ok(()) + }; + + check("void")?; + check("more") +} + +#[test] +fn test_heap() -> Result<()> { + // in memory.wat + // the input is the target size and amount to step each `memory.grow` + // the output is the memory size in pages + + let (mut compile, config, _) = test_configs(); + compile.bounds.heap_bound = Pages(128); + compile.pricing.costs = |_, _| 0; + + let extra: u8 = rand::random::() % 128; + + for step in 1..128 { + let (mut native, _) = TestInstance::new_with_evm("tests/memory.wat", &compile, config)?; + let ink = random_ink(32_000_000); + let args = vec![128, step]; + + let pages = run_native(&mut native, &args, ink)?[0]; + assert_eq!(pages, 128); + + let used = config.pricing.ink_to_gas(ink - native.ink_ready()?); + ensure!((used as i64 - 32_000_000).abs() < 3_000, "wrong ink"); + assert_eq!(native.memory_size(), Pages(128)); + + if step == extra { + let mut machine = Machine::from_user_path(Path::new("tests/memory.wat"), &compile)?; + run_machine(&mut machine, &args, config, ink)?; + check_instrumentation(native, machine)?; + } + } + + // in memory2.wat + // the user program calls pay_for_memory_grow directly with malicious arguments + // the cost should exceed a maximum u32, consuming more gas than can ever be bought + + let (mut native, _) = TestInstance::new_with_evm("tests/memory2.wat", &compile, config)?; + let outcome = native.run_main(&[], config, config.pricing.ink_to_gas(u32::MAX.into()))?; + assert_eq!(outcome.kind(), UserOutcomeKind::OutOfInk); + + // ensure we reject programs with excessive footprints + compile.bounds.heap_bound = Pages(0); + _ = TestInstance::new_with_evm("tests/memory.wat", &compile, config).unwrap_err(); + _ = Machine::from_user_path(Path::new("tests/memory.wat"), &compile).unwrap_err(); + Ok(()) +} + +#[test] +fn test_rust() -> Result<()> { + // in keccak.rs + // the input is the # of hashings followed by a preimage + // the output is the iterated hash of the preimage + + let filename = "tests/keccak/target/wasm32-unknown-unknown/release/keccak.wasm"; + let preimage = "°º¤ø,¸,ø¤°º¤ø,¸,ø¤°º¤ø,¸ nyan nyan ~=[,,_,,]:3 nyan nyan"; + let preimage = preimage.as_bytes().to_vec(); + let hash = hex::encode(crypto::keccak(&preimage)); + let (compile, config, ink) = test_configs(); + + let mut args = vec![0x01]; + args.extend(preimage); + + let mut native = TestInstance::new_linked(filename, &compile, config)?; + let start = Instant::now(); + let output = run_native(&mut native, &args, ink)?; + println!("Exec {}", format::time(start.elapsed())); + assert_eq!(hex::encode(output), hash); + + let mut machine = Machine::from_user_path(Path::new(filename), &compile)?; + let start = Instant::now(); + let output = run_machine(&mut machine, &args, config, ink)?; + assert_eq!(hex::encode(output), hash); + println!("Exec {}", format::time(start.elapsed())); + + check_instrumentation(native, machine) +} + +#[test] +fn test_fallible() -> Result<()> { + // in fallible.rs + // an input starting with 0x00 will execute an unreachable + // an empty input induces a panic + + let filename = "tests/fallible/target/wasm32-unknown-unknown/release/fallible.wasm"; + let (compile, config, ink) = test_configs(); + + let mut native = TestInstance::new_linked(filename, &compile, config)?; + match native.run_main(&[0x00], config, ink)? { + UserOutcome::Failure(err) => println!("{}", format!("{err:?}").grey()), + err => bail!("expected hard error: {}", err.red()), + } + match native.run_main(&[], config, ink)? { + UserOutcome::Failure(err) => println!("{}", format!("{err:?}").grey()), + err => bail!("expected hard error: {}", err.red()), + } + + let mut machine = Machine::from_user_path(Path::new(filename), &compile)?; + match machine.run_main(&[0x00], config, ink)? { + UserOutcome::Failure(err) => println!("{}", format!("{err:?}").grey()), + err => bail!("expected hard error: {}", err.red()), + } + match machine.run_main(&[], config, ink)? { + UserOutcome::Failure(err) => println!("{}", format!("{err:?}").grey()), + err => bail!("expected hard error: {}", err.red()), + } + + let native_counts = native.operator_counts()?; + let machine_counts = machine.operator_counts()?; + assert_eq!(native_counts, machine_counts); + assert_eq!(native.ink_left(), machine.ink_left()); + assert_eq!(native.stack_left(), machine.stack_left()); + Ok(()) +} + +#[test] +fn test_storage() -> Result<()> { + // in storage.rs + // an input starting with 0x00 will induce a storage read + // all other inputs induce a storage write + + let filename = "tests/storage/target/wasm32-unknown-unknown/release/storage.wasm"; + let (compile, config, ink) = test_configs(); + + let key = crypto::keccak(filename.as_bytes()); + let value = crypto::keccak("value".as_bytes()); + + let mut store_args = vec![0x01]; + store_args.extend(key); + store_args.extend(value); + + let mut load_args = vec![0x00]; + load_args.extend(key); + + let (mut native, mut evm) = TestInstance::new_with_evm(filename, &compile, config)?; + run_native(&mut native, &store_args, ink)?; + assert_eq!(evm.get_bytes32(key.into()).0, Bytes32(value)); + assert_eq!(run_native(&mut native, &load_args, ink)?, value); + + let mut machine = Machine::from_user_path(Path::new(filename), &compile)?; + run_machine(&mut machine, &store_args, config, ink)?; + assert_eq!(run_machine(&mut machine, &load_args, config, ink)?, value); + + check_instrumentation(native, machine) +} + +#[test] +fn test_calls() -> Result<()> { + // in call.rs + // the first bytes determines the number of calls to make + // each call starts with a length specifying how many input bytes it constitutes + // the first byte determines the kind of call to be made (normal, delegate, or static) + // the next 20 bytes select the address you want to call, with the rest being calldata + // + // in storage.rs + // an input starting with 0x00 will induce a storage read + // all other inputs induce a storage write + + let calls_addr = random_bytes20(); + let store_addr = random_bytes20(); + println!("calls.wasm {}", calls_addr); + println!("store.wasm {}", store_addr); + + let mut slots = HashMap::new(); + + /// Forms a 2ary call tree where each leaf writes a random storage cell. + fn nest( + level: usize, + calls: Bytes20, + store: Bytes20, + slots: &mut HashMap, + ) -> Vec { + let mut args = vec![]; + + if level == 0 { + // call storage.wasm + args.push(0x00); + args.extend(Bytes32::default()); + args.extend(store); + + let key = random_bytes32(); + let value = random_bytes32(); + slots.insert(key, value); + + // insert value @ key + args.push(0x01); + args.extend(key); + args.extend(value); + return args; + } + + // do the two following calls + args.push(0x00); + args.extend(Bytes32::default()); + args.extend(calls); + args.push(2); + + for _ in 0..2 { + let inner = nest(level - 1, calls, store, slots); + args.extend(u32::to_be_bytes(inner.len() as u32)); + args.extend(inner); + } + args + } + + // drop the first address to start the call tree + let tree = nest(3, calls_addr, store_addr, &mut slots); + let args = tree[53..].to_vec(); + println!("ARGS {}", hex::encode(&args)); + + let filename = "tests/multicall/target/wasm32-unknown-unknown/release/multicall.wasm"; + let (compile, config, ink) = test_configs(); + + let (mut native, mut evm) = TestInstance::new_with_evm(filename, &compile, config)?; + evm.deploy(calls_addr, config, "multicall")?; + evm.deploy(store_addr, config, "storage")?; + + run_native(&mut native, &args, ink)?; + + for (key, value) in slots { + assert_eq!(evm.get_bytes32(key).0, value); + } + Ok(()) +} + +#[test] +fn test_exit_early() -> Result<()> { + // in exit-early.wat + // the input is returned as the output + // the status code is the first byte + // + // in panic-after-write.wat + // the program writes a result but then panics + + let file = |f: &str| format!("tests/exit-early/{f}.wat"); + let (compile, config, ink) = test_configs(); + let args = &[0x01; 32]; + + let mut native = TestInstance::new_linked(file("exit-early"), &compile, config)?; + let output = match native.run_main(args, config, ink)? { + UserOutcome::Revert(output) => output, + err => bail!("expected revert: {}", err.red()), + }; + assert_eq!(hex::encode(output), hex::encode(args)); + + let mut native = TestInstance::new_linked(file("panic-after-write"), &compile, config)?; + match native.run_main(args, config, ink)? { + UserOutcome::Failure(error) => println!("{error:?}"), + err => bail!("expected hard error: {}", err.red()), + } + Ok(()) +} diff --git a/arbitrator/stylus/src/test/sdk.rs b/arbitrator/stylus/src/test/sdk.rs new file mode 100644 index 0000000000..1f5f4b1c7f --- /dev/null +++ b/arbitrator/stylus/src/test/sdk.rs @@ -0,0 +1,65 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![allow(clippy::field_reassign_with_default)] + +use crate::test::{random_bytes20, random_bytes32, run_native, test_configs, TestInstance}; +use eyre::Result; +use num_bigint::BigUint; + +#[test] +fn test_sdk_routes() -> Result<()> { + let filename = "tests/erc20/target/wasm32-unknown-unknown/release/erc20.wasm"; + + macro_rules! hex { + ($($hex:expr),+) => { + hex::decode(&format!($($hex),+))? + }; + } + + let (compile, config, ink) = test_configs(); + let (mut native, mut evm) = TestInstance::new_with_evm(filename, &compile, config)?; + + // deploy a copy to another address + let imath = random_bytes20(); + evm.deploy(imath, config, "erc20")?; + + // call balanceOf(0x000..000) + let calldata = hex!("70a082310000000000000000000000000000000000000000000000000000000000000000"); + let output = run_native(&mut native, &calldata, ink)?; + assert_eq!(output, [0; 32]); + + // call mint() + let calldata = hex!("1249c58b"); + let output = run_native(&mut native, &calldata, ink)?; + assert!(output.is_empty()); + + macro_rules! big { + ($int:expr) => { + &format!("{:0>64}", $int.to_str_radix(16)) + }; + } + + // sumWithHelper(imath, values) + let imath = BigUint::from_bytes_be(&imath.0); + let count = 10_u8; + let method = "168261a9"; // sumWithHelper + let mut calldata = hex!( + "{method}{}{}{}", + big!(imath), + big!(BigUint::from(64_u8)), + big!(BigUint::from(count)) + ); + + let mut sum = BigUint::default(); + for _ in 0..count { + let value = BigUint::from_bytes_be(&random_bytes32().0); + calldata.extend(hex!("{}", big!(value))); + sum += value; + } + sum %= BigUint::from(2_u8).pow(256); + + let output = run_native(&mut native, &calldata, ink)?; + assert_eq!(&hex::encode(output), big!(sum)); + Ok(()) +} diff --git a/arbitrator/stylus/src/test/timings.rs b/arbitrator/stylus/src/test/timings.rs new file mode 100644 index 0000000000..5419dd5dd9 --- /dev/null +++ b/arbitrator/stylus/src/test/timings.rs @@ -0,0 +1,73 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::test::{run_native, test_configs, TestInstance}; +use arbutil::{color::Color, format}; +use eyre::Result; +use std::time::Instant; + +#[test] +fn test_timings() -> Result<()> { + let (mut compile, config, ink) = test_configs(); + compile.debug.count_ops = false; + + #[rustfmt::skip] + let basic = [ + // simple returns + "null_host", "return_data_size", "block_gas_limit", "block_timestamp", "tx_ink_price", + + // gas left + "evm_gas_left", "evm_ink_left", + + // evm data + "block_basefee", "chainid", "block_coinbase", "block_number", "contract_address", + "msg_sender", "msg_value", "tx_gas_price", "tx_origin", + ]; + + let loops = ["read_args", "write_result", "keccak"]; + + macro_rules! run { + ($rounds:expr, $args:expr, $file:expr) => {{ + let mut native = TestInstance::new_linked(&$file, &compile, config)?; + let before = Instant::now(); + run_native(&mut native, &$args, ink)?; + let time = before.elapsed() / $rounds; + let cost = time.as_nanos() as f64 / 10.39; // 10.39 from Rachel's desktop + let ink = format!("{}", (cost * 10000.).ceil() as usize).grey(); + (format::time(time), format!("{cost:.4}").grey(), ink) + }}; + } + + macro_rules! args { + ($rounds:expr, $len:expr) => {{ + let mut args = $rounds.to_le_bytes().to_vec(); + args.extend(vec![1; $len - 4]); + args + }}; + } + + println!("Timings hostios. Please note the values derived are machine dependent.\n"); + + println!("\n{}", format!("Hostio timings").pink()); + for name in basic { + let file = format!("tests/timings/{name}.wat"); + let rounds: u32 = 50_000_000; + let (time, cost, ink) = run!(rounds, rounds.to_le_bytes(), file); + println!("{} {time} {cost} {ink}", format!("{name:16}").grey()); + } + + for name in loops { + println!("\n{}", format!("{name} timings").pink()); + for i in 2..10 { + let file = format!("tests/timings/{name}.wat"); + let rounds: u32 = 10_000_000; + let size = 1 << i; + let args = args!(rounds, size); + + let (time, cost, ink) = run!(rounds, args, file); + let name = format!("{name}({size:03})").grey(); + println!("{name} {time} {cost} {ink}",); + } + } + Ok(()) +} diff --git a/arbitrator/stylus/src/test/wavm.rs b/arbitrator/stylus/src/test/wavm.rs new file mode 100644 index 0000000000..e707cf490a --- /dev/null +++ b/arbitrator/stylus/src/test/wavm.rs @@ -0,0 +1,104 @@ +// Copyright 2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use crate::test::{new_test_machine, test_compile_config}; +use eyre::Result; +use prover::{programs::prelude::*, Machine}; + +#[test] +fn test_ink() -> Result<()> { + let mut compile = test_compile_config(); + compile.pricing.costs = super::expensive_add; + + let machine = &mut new_test_machine("tests/add.wat", &compile)?; + let call = |mech: &mut Machine, v: u32| mech.call_function("user", "add_one", vec![v.into()]); + + macro_rules! exhaust { + ($ink:expr) => { + machine.set_ink($ink); + assert_eq!(machine.ink_left(), MachineMeter::Ready($ink)); + assert!(call(machine, 32).is_err()); + assert_eq!(machine.ink_left(), MachineMeter::Exhausted); + }; + } + + exhaust!(0); + exhaust!(50); + exhaust!(99); + + let mut ink_left = 500; + machine.set_ink(ink_left); + while ink_left > 0 { + assert_eq!(machine.ink_left(), MachineMeter::Ready(ink_left)); + assert_eq!(call(machine, 64)?, vec![65_u32.into()]); + ink_left -= 100; + } + assert!(call(machine, 32).is_err()); + assert_eq!(machine.ink_left(), MachineMeter::Exhausted); + Ok(()) +} + +#[test] +fn test_depth() -> Result<()> { + // in depth.wat + // the `depth` global equals the number of times `recurse` is called + // the `recurse` function calls itself + // the `recurse` function has 1 parameter and 2 locals + // comments show that the max depth is 3 words + + let machine = &mut new_test_machine("tests/depth.wat", &test_compile_config())?; + let call = |mech: &mut Machine| mech.call_function("user", "recurse", vec![0_u64.into()]); + + let program_depth: u32 = machine.get_global("depth")?.try_into()?; + assert_eq!(program_depth, 0); + + let mut check = |space: u32, expected: u32| -> Result<()> { + machine.set_global("depth", 0_u32.into())?; + machine.set_stack(space); + assert_eq!(machine.stack_left(), space); + + assert!(call(machine).is_err()); + assert_eq!(machine.stack_left(), 0); + + let program_depth: u32 = machine.get_global("depth")?.try_into()?; + assert_eq!(program_depth, expected); + Ok(()) + }; + + let locals = 2; + let depth = 3; + let fixed = 4; + + let frame_size = locals + depth + fixed; + + check(frame_size, 0)?; // should immediately exhaust (space left <= frame) + check(frame_size + 1, 1)?; + check(2 * frame_size, 1)?; + check(2 * frame_size + 1, 2)?; + check(4 * frame_size, 3)?; + check(4 * frame_size + frame_size / 2, 4) +} + +#[test] +fn test_start() -> Result<()> { + // in start.wat + // the `status` global equals 10 at initialization + // the `start` function increments `status` + // by the spec, `start` must run at initialization + + fn check(machine: &mut Machine, value: u32) -> Result<()> { + let status: u32 = machine.get_global("status")?.try_into()?; + assert_eq!(status, value); + Ok(()) + } + + let compile = test_compile_config(); + let machine = &mut new_test_machine("tests/start.wat", &compile)?; + check(machine, 10)?; + + let call = |mech: &mut Machine, name: &str| mech.call_function("user", name, vec![]); + + call(machine, "move_me")?; + call(machine, "stylus_start")?; + check(machine, 12) +} diff --git a/arbitrator/stylus/src/util.rs b/arbitrator/stylus/src/util.rs new file mode 100644 index 0000000000..71a7baf9bd --- /dev/null +++ b/arbitrator/stylus/src/util.rs @@ -0,0 +1,20 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use arbutil::crypto; +use eyre::Report; + +/// This function panics while saving an offending wasm to disk. +pub fn panic_with_wasm(wasm: &[u8], error: Report) -> ! { + // save at a deterministic path + let hash = hex::encode(crypto::keccak(wasm)); + let mut path = std::env::temp_dir(); + path.push(format!("stylus-panic-{hash}.wasm")); + + // try to save to disk, otherwise dump to the console + if let Err(io_error) = std::fs::write(&path, wasm) { + let wasm = hex::encode(wasm); + panic!("failed to write fatal wasm {error:?}: {io_error:?}\nwasm: {wasm}"); + } + panic!("encountered fatal wasm: {error:?}"); +} diff --git a/arbitrator/stylus/tests/.cargo/config.toml b/arbitrator/stylus/tests/.cargo/config.toml new file mode 100644 index 0000000000..ff01b66852 --- /dev/null +++ b/arbitrator/stylus/tests/.cargo/config.toml @@ -0,0 +1,9 @@ +[build] +target = "wasm32-unknown-unknown" + +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "link-arg=-zstack-size=8192", +# "-C", "link-arg=--export=__heap_base", +# "-C", "link-arg=--export=__data_end", +] diff --git a/arbitrator/stylus/tests/add.wat b/arbitrator/stylus/tests/add.wat new file mode 100644 index 0000000000..70aa710787 --- /dev/null +++ b/arbitrator/stylus/tests/add.wat @@ -0,0 +1,14 @@ +;; Copyright 2022, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (memory 0 0) + (export "memory" (memory 0)) + (type $t0 (func (param i32) (result i32))) + (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32) + get_local $p0 + i32.const 1 + i32.add) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + )) diff --git a/arbitrator/stylus/tests/bad-mods/bad-export.wat b/arbitrator/stylus/tests/bad-mods/bad-export.wat new file mode 100644 index 0000000000..80c029166a --- /dev/null +++ b/arbitrator/stylus/tests/bad-mods/bad-export.wat @@ -0,0 +1,5 @@ +;; Copyright 2022-2024, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (global $status (export "stylus_ink_left") (mut i64) (i64.const -1))) diff --git a/arbitrator/stylus/tests/bad-mods/bad-export2.wat b/arbitrator/stylus/tests/bad-mods/bad-export2.wat new file mode 100644 index 0000000000..907cc299ce --- /dev/null +++ b/arbitrator/stylus/tests/bad-mods/bad-export2.wat @@ -0,0 +1,5 @@ +;; Copyright 2022-2024, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (func (export "stylus_global_with_random_name"))) diff --git a/arbitrator/stylus/tests/bad-mods/bad-export3.wat b/arbitrator/stylus/tests/bad-mods/bad-export3.wat new file mode 100644 index 0000000000..30232916ff --- /dev/null +++ b/arbitrator/stylus/tests/bad-mods/bad-export3.wat @@ -0,0 +1,5 @@ +;; Copyright 2024, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (func (export "memory"))) diff --git a/arbitrator/stylus/tests/bad-mods/bad-export4.wat b/arbitrator/stylus/tests/bad-mods/bad-export4.wat new file mode 100644 index 0000000000..47142990a7 --- /dev/null +++ b/arbitrator/stylus/tests/bad-mods/bad-export4.wat @@ -0,0 +1,7 @@ +;; Copyright 2024, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (global (export "user_entrypoint") i32 (i32.const 0)) + (memory (export "memory") 0 0) +) diff --git a/arbitrator/stylus/tests/bad-mods/bad-import.wat b/arbitrator/stylus/tests/bad-mods/bad-import.wat new file mode 100644 index 0000000000..ec2a951fba --- /dev/null +++ b/arbitrator/stylus/tests/bad-mods/bad-import.wat @@ -0,0 +1,5 @@ +;; Copyright 2022-2024, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (import "env" "stylus_global_with_random_name" (func))) diff --git a/arbitrator/stylus/tests/bf/.gitignore b/arbitrator/stylus/tests/bf/.gitignore new file mode 100644 index 0000000000..373b847efe --- /dev/null +++ b/arbitrator/stylus/tests/bf/.gitignore @@ -0,0 +1,2 @@ +**.wat +**.wasm diff --git a/arbitrator/stylus/tests/bf/cat.b b/arbitrator/stylus/tests/bf/cat.b new file mode 100644 index 0000000000..05b49e2646 --- /dev/null +++ b/arbitrator/stylus/tests/bf/cat.b @@ -0,0 +1 @@ +,[.,] diff --git a/arbitrator/stylus/tests/bulk-memory-oob.wat b/arbitrator/stylus/tests/bulk-memory-oob.wat new file mode 100644 index 0000000000..dceba8ec2e --- /dev/null +++ b/arbitrator/stylus/tests/bulk-memory-oob.wat @@ -0,0 +1,17 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (func (export "fill") + (memory.fill (i32.const 0xffff) (i32.const 0) (i32.const 2))) + (func (export "copy_left") + (memory.copy (i32.const 0xffff) (i32.const 0xfffe) (i32.const 2))) + (func (export "copy_right") + (memory.copy (i32.const 0xfffe) (i32.const 0xffff) (i32.const 2))) + (func (export "copy_same") + (memory.copy (i32.const 0xffff) (i32.const 0xffff) (i32.const 2))) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + ) + (data (i32.const 0xfffe) "\01\02") ;; last two bytes shouldn't change + (memory (export "memory") 1 1)) diff --git a/arbitrator/stylus/tests/clz.wat b/arbitrator/stylus/tests/clz.wat new file mode 100644 index 0000000000..4504d7887a --- /dev/null +++ b/arbitrator/stylus/tests/clz.wat @@ -0,0 +1,11 @@ +;; Copyright 2022-2023, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (global $global (mut i64) (i64.const 32)) + (func $start + global.get $global + i64.clz + drop + ) + (start $start)) diff --git a/arbitrator/stylus/tests/console.wat b/arbitrator/stylus/tests/console.wat new file mode 100644 index 0000000000..28162cf2c6 --- /dev/null +++ b/arbitrator/stylus/tests/console.wat @@ -0,0 +1,37 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "console" "log_txt" (func $log_txt (param i32 i32))) + (import "console" "log_i32" (func $log_i32 (param i32))) + (import "console" "log_i64" (func $log_i64 (param i64))) + (import "console" "log_f32" (func $log_f32 (param f32))) + (import "console" "log_f64" (func $log_f64 (param f64))) + (import "console" "tee_i32" (func $tee_i32 (param i32) (result i32))) + (import "console" "tee_i64" (func $tee_i64 (param i64) (result i64))) + (import "console" "tee_f32" (func $tee_f32 (param f32) (result f32))) + (import "console" "tee_f64" (func $tee_f64 (param f64) (result f64))) + (memory (export "memory") 1 1) + (data (i32.const 0xa4b) "\57\65\20\68\61\76\65\20\74\68\65\20\69\6E\6B\21") ;; We have the ink! + (func $start + (call $log_txt (i32.const 0xa4b) (i32.const 16)) + + i32.const 48 + call $tee_i32 + call $log_i32 + + i64.const 96 + call $tee_i64 + call $log_i64 + + f32.const 0.32 + call $tee_f32 + call $log_f32 + + f64.const -64.32 + call $tee_f64 + call $log_f64) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + ) + (start $start)) diff --git a/arbitrator/stylus/tests/create/.cargo/config b/arbitrator/stylus/tests/create/.cargo/config new file mode 100644 index 0000000000..f4e8c002fc --- /dev/null +++ b/arbitrator/stylus/tests/create/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/create/Cargo.lock b/arbitrator/stylus/tests/create/Cargo.lock new file mode 100644 index 0000000000..ca6be1f23d --- /dev/null +++ b/arbitrator/stylus/tests/create/Cargo.lock @@ -0,0 +1,544 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e416903084d3392ebd32d94735c395d6709415b76c7728e594d3f996f2b03e65" +dependencies = [ + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "ruint", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "create" +version = "0.1.0" +dependencies = [ + "hex", + "stylus-sdk", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bitflags", + "byteorder", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/create/Cargo.toml b/arbitrator/stylus/tests/create/Cargo.toml new file mode 100644 index 0000000000..b800033dd4 --- /dev/null +++ b/arbitrator/stylus/tests/create/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "create" +version = "0.1.0" +edition = "2021" + +[dependencies] +stylus-sdk = { path = "../../../langs/rust/stylus-sdk" } +hex = "0.4.3" + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" + +# uncomment to optimize for size +# opt-level = "z" + +[workspace] diff --git a/arbitrator/stylus/tests/create/src/main.rs b/arbitrator/stylus/tests/create/src/main.rs new file mode 100644 index 0000000000..d5ef19af6c --- /dev/null +++ b/arbitrator/stylus/tests/create/src/main.rs @@ -0,0 +1,26 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![no_main] + +use stylus_sdk::{alloy_primitives::{B256, U256}, deploy::RawDeploy, evm, prelude::*}; + +#[entrypoint] +fn user_main(input: Vec) -> Result, Vec> { + let kind = input[0]; + let mut input = &input[1..]; + + let endowment = U256::from_be_bytes::<32>(input[..32].try_into().unwrap()); + input = &input[32..]; + + let mut salt = None; + if kind == 2 { + salt = Some(B256::try_from(&input[..32]).unwrap()); + input = &input[32..]; + } + + let code = input; + let contract = unsafe { RawDeploy::new().salt_option(salt).deploy(code, endowment)? }; + evm::raw_log(&[contract.into_word()], &[]).unwrap(); + Ok(contract.to_vec()) +} diff --git a/arbitrator/stylus/tests/depth.wat b/arbitrator/stylus/tests/depth.wat new file mode 100644 index 0000000000..21ca440665 --- /dev/null +++ b/arbitrator/stylus/tests/depth.wat @@ -0,0 +1,18 @@ +;; Copyright 2022, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (import "test" "noop" (func)) + (memory 0 0) + (export "memory" (memory 0)) + (global $depth (export "depth") (mut i32) (i32.const 0)) + (func $recurse (export "recurse") (param $ignored i64) (local f32 f64) + local.get $ignored ;; push 1 -- 1 on stack + global.get $depth ;; push 1 -- 2 on stack + i32.const 1 ;; push 1 -- 3 on stack <- 3 words max + i32.add ;; pop 2, push 1 -- 2 on stack + global.set $depth ;; pop 1 -- 1 on stack + call $recurse) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + )) diff --git a/arbitrator/stylus/tests/erc20/Cargo.lock b/arbitrator/stylus/tests/erc20/Cargo.lock new file mode 100644 index 0000000000..c3e215978d --- /dev/null +++ b/arbitrator/stylus/tests/erc20/Cargo.lock @@ -0,0 +1,890 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66f73f11dcfbf8bb763d88fb1d082fe7cca0a00d3227d9921bdbd52ce5e013e2" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if 1.0.0", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "proptest", + "ruint", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f938f00332d63a5b0ac687bd6f46d03884638948921d9f8b50c59563d421ae25" +dependencies = [ + "arrayvec", + "bytes", + "smol_str", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "erc20" +version = "0.1.0" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "hex", + "stylus-sdk", + "wee_alloc", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bit-set", + "bitflags", + "byteorder", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax 0.6.29", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax 0.7.4", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if 1.0.0", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if 1.0.0", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "regex", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tempfile" +version = "3.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/erc20/Cargo.toml b/arbitrator/stylus/tests/erc20/Cargo.toml new file mode 100644 index 0000000000..859bfe50a7 --- /dev/null +++ b/arbitrator/stylus/tests/erc20/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "erc20" +version = "0.1.0" +edition = "2021" + +[dependencies] +alloy-primitives = "0.3.1" +alloy-sol-types = "0.3.1" +stylus-sdk = { path = "../../../langs/rust/stylus-sdk", features = ["reentrant"] } +hex = "0.4.3" +wee_alloc = "0.4.5" + +[features] +export-abi = ["stylus-sdk/export-abi"] + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" +opt-level = "s" + +[workspace] diff --git a/arbitrator/stylus/tests/erc20/src/erc20.rs b/arbitrator/stylus/tests/erc20/src/erc20.rs new file mode 100644 index 0000000000..da41fe297c --- /dev/null +++ b/arbitrator/stylus/tests/erc20/src/erc20.rs @@ -0,0 +1,178 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +// Warning: this code is for testing only and has not been audited + +use alloc::{string::String, vec::Vec}; +use core::marker::PhantomData; +use stylus_sdk::{ + alloy_primitives::{Address, U256}, + alloy_sol_types::{sol, SolError}, + evm, msg, + prelude::*, +}; + +pub trait Erc20Params { + const NAME: &'static str; + const SYMBOL: &'static str; + const DECIMALS: u8; +} + +sol_storage! { + /// Erc20 implements all ERC-20 methods. + pub struct Erc20 { + /// Maps users to balances + mapping(address => uint256) balances; + /// Maps users to a mapping of each spender's allowance + mapping(address => mapping(address => uint256)) allowances; + /// The total supply of the token + uint256 total_supply; + /// Used to allow [`Erc20Params`] + PhantomData phantom; + } +} + +// Declare events and Solidity error types +sol! { + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); + + error InsufficientBalance(address from, uint256 have, uint256 want); + error InsufficientAllowance(address owner, address spender, uint256 have, uint256 want); +} + +pub enum Erc20Error { + InsufficientBalance(InsufficientBalance), + InsufficientAllowance(InsufficientAllowance), +} + +// We will soon provide a #[derive(SolidityError)] to clean this up +impl From for Vec { + fn from(err: Erc20Error) -> Vec { + match err { + Erc20Error::InsufficientBalance(e) => e.encode(), + Erc20Error::InsufficientAllowance(e) => e.encode(), + } + } +} + +// These methods aren't exposed to other contracts +// Note: modifying storage will become much prettier soon +impl Erc20 { + pub fn transfer_impl( + &mut self, + from: Address, + to: Address, + value: U256, + ) -> Result<(), Erc20Error> { + let mut sender_balance = self.balances.setter(from); + let old_sender_balance = sender_balance.get(); + if old_sender_balance < value { + return Err(Erc20Error::InsufficientBalance(InsufficientBalance { + from, + have: old_sender_balance, + want: value, + })); + } + sender_balance.set(old_sender_balance - value); + let mut to_balance = self.balances.setter(to); + let new_to_balance = to_balance.get() + value; + to_balance.set(new_to_balance); + evm::log(Transfer { from, to, value }); + Ok(()) + } + + pub fn mint(&mut self, address: Address, value: U256) { + let mut balance = self.balances.setter(address); + let new_balance = balance.get() + value; + balance.set(new_balance); + self.total_supply.set(self.total_supply.get() + value); + evm::log(Transfer { + from: Address::ZERO, + to: address, + value, + }); + } + + pub fn burn(&mut self, address: Address, value: U256) -> Result<(), Erc20Error> { + let mut balance = self.balances.setter(address); + let old_balance = balance.get(); + if old_balance < value { + return Err(Erc20Error::InsufficientBalance(InsufficientBalance { + from: address, + have: old_balance, + want: value, + })); + } + balance.set(old_balance - value); + self.total_supply.set(self.total_supply.get() - value); + evm::log(Transfer { + from: address, + to: Address::ZERO, + value, + }); + Ok(()) + } +} + +// These methods are external to other contracts +// Note: modifying storage will become much prettier soon +#[external] +impl Erc20 { + pub fn name() -> Result { + Ok(T::NAME.into()) + } + + pub fn symbol() -> Result { + Ok(T::SYMBOL.into()) + } + + pub fn decimals() -> Result { + Ok(T::DECIMALS) + } + + pub fn balance_of(&self, address: Address) -> Result { + Ok(self.balances.get(address)) + } + + pub fn transfer(&mut self, to: Address, value: U256) -> Result { + self.transfer_impl(msg::sender(), to, value)?; + Ok(true) + } + + pub fn approve(&mut self, spender: Address, value: U256) -> Result { + self.allowances.setter(msg::sender()).insert(spender, value); + evm::log(Approval { + owner: msg::sender(), + spender, + value, + }); + Ok(true) + } + + pub fn transfer_from( + &mut self, + from: Address, + to: Address, + value: U256, + ) -> Result { + let mut sender_allowances = self.allowances.setter(from); + let mut allowance = sender_allowances.setter(msg::sender()); + let old_allowance = allowance.get(); + if old_allowance < value { + return Err(Erc20Error::InsufficientAllowance(InsufficientAllowance { + owner: from, + spender: msg::sender(), + have: old_allowance, + want: value, + })); + } + allowance.set(old_allowance - value); + self.transfer_impl(from, to, value)?; + Ok(true) + } + + pub fn allowance(&self, owner: Address, spender: Address) -> Result { + Ok(self.allowances.getter(owner).get(spender)) + } +} diff --git a/arbitrator/stylus/tests/erc20/src/main.rs b/arbitrator/stylus/tests/erc20/src/main.rs new file mode 100644 index 0000000000..7cbda7ef3a --- /dev/null +++ b/arbitrator/stylus/tests/erc20/src/main.rs @@ -0,0 +1,70 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +// Warning: this code is for testing only and has not been audited + +#![cfg_attr(not(feature = "export-abi"), no_main, no_std)] +extern crate alloc; + +use crate::erc20::{Erc20, Erc20Params}; +use alloc::{string::String, vec::Vec}; +use stylus_sdk::{alloy_primitives::U256, call, msg, prelude::*}; + +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +mod erc20; + +struct WethParams; + +/// Immutable definitions +impl Erc20Params for WethParams { + const NAME: &'static str = "Wrapped Ether Example"; + const SYMBOL: &'static str = "WETH"; + const DECIMALS: u8 = 18; +} + +// The contract +sol_storage! { + #[entrypoint] // Makes Weth the entrypoint + struct Weth { + #[borrow] // Allows erc20 to access Weth's storage and make calls + Erc20 erc20; + } +} + +// Another contract we'd like to call +sol_interface! { + interface IMath { + function sumValues(uint256[] values) pure returns (string, uint256); + } +} + +#[external] +#[inherit(Erc20)] +impl Weth { + #[payable] + pub fn mint(&mut self) -> Result<(), Vec> { + self.erc20.mint(msg::sender(), msg::value()); + Ok(()) + } + + pub fn burn(&mut self, amount: U256) -> Result<(), Vec> { + self.erc20.burn(msg::sender(), amount)?; + + // send the user their funds + call::transfer_eth(self, msg::sender(), amount) + } + + // sums numbers + pub fn sum_values(values: Vec) -> Result<(String, U256), Vec> { + Ok(("sum".into(), values.iter().sum())) + } + + // calls the sum_values() method from the interface + pub fn sum_with_helper(&self, helper: IMath, values: Vec) -> Result> { + let (text, sum) = helper.sum_values(self, values)?; + assert_eq!(&text, "sum"); + Ok(sum) + } +} diff --git a/arbitrator/stylus/tests/evm-data/.cargo/config b/arbitrator/stylus/tests/evm-data/.cargo/config new file mode 100644 index 0000000000..f4e8c002fc --- /dev/null +++ b/arbitrator/stylus/tests/evm-data/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/evm-data/Cargo.lock b/arbitrator/stylus/tests/evm-data/Cargo.lock new file mode 100644 index 0000000000..c78abc9f1a --- /dev/null +++ b/arbitrator/stylus/tests/evm-data/Cargo.lock @@ -0,0 +1,544 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e416903084d3392ebd32d94735c395d6709415b76c7728e594d3f996f2b03e65" +dependencies = [ + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "ruint", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "evm-data" +version = "0.1.0" +dependencies = [ + "hex", + "stylus-sdk", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bitflags", + "byteorder", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/evm-data/Cargo.toml b/arbitrator/stylus/tests/evm-data/Cargo.toml new file mode 100644 index 0000000000..3549af52ea --- /dev/null +++ b/arbitrator/stylus/tests/evm-data/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "evm-data" +version = "0.1.0" +edition = "2021" + +[dependencies] +stylus-sdk = { path = "../../../langs/rust/stylus-sdk", features = ["debug"] } +hex = "0.4.3" + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" + +# uncomment to optimize for size +# opt-level = "z" + +[workspace] diff --git a/arbitrator/stylus/tests/evm-data/src/main.rs b/arbitrator/stylus/tests/evm-data/src/main.rs new file mode 100644 index 0000000000..850d51176c --- /dev/null +++ b/arbitrator/stylus/tests/evm-data/src/main.rs @@ -0,0 +1,85 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![no_main] + +extern crate alloc; + +use stylus_sdk::{ + alloy_primitives::{Address, B256, U256}, + block, + call::RawCall, + contract, evm, msg, + prelude::*, + tx, +}; + +#[entrypoint] +fn user_main(input: Vec) -> Result, Vec> { + let balance_check_addr = Address::try_from(&input[..20]).unwrap(); + let eth_precompile_addr = Address::try_from(&input[20..40]).unwrap(); + let arb_test_addr = Address::try_from(&input[40..60]).unwrap(); + let contract_addr = Address::try_from(&input[60..80]).unwrap(); + let burn_call_data = &input[80..]; + + let address_balance = balance_check_addr.balance(); + let eth_precompile_codehash = eth_precompile_addr.codehash(); + let arb_precompile_codehash = arb_test_addr.codehash(); + let contract_codehash = contract_addr.codehash(); + + let code = contract_addr.code(); + assert_eq!(code.len(), contract_addr.code_size()); + assert_eq!(arb_test_addr.code_size(), 1); + assert_eq!(arb_test_addr.code(), [0xfe]); + assert_eq!(eth_precompile_addr.code_size(), 0); + assert_eq!(eth_precompile_addr.code(), []); + + let basefee = block::basefee(); + let chainid = block::chainid(); + let coinbase = block::coinbase(); + let gas_limit = block::gas_limit(); + let timestamp = block::timestamp(); + let address = contract::address(); + let sender = msg::sender(); + let value = msg::value(); + let origin = tx::origin(); + let gas_price = tx::gas_price(); + let ink_price = tx::ink_price(); + + let mut block_number = block::number(); + block_number -= 1; + + // Call burnArbGas + let gas_left_before = evm::gas_left(); + let ink_left_before = evm::ink_left(); + RawCall::new().call(arb_test_addr, burn_call_data)?; + let gas_left_after = evm::gas_left(); + let ink_left_after = evm::ink_left(); + + let mut output = vec![]; + output.extend(B256::from(U256::from(block_number))); + output.extend(B256::from(U256::from(chainid))); + output.extend(B256::from(basefee)); + output.extend(B256::from(gas_price)); + output.extend(B256::from(U256::from(gas_limit))); + output.extend(B256::from(value)); + output.extend(B256::from(U256::from(timestamp))); + output.extend(B256::from(address_balance)); + + output.extend(address.into_word()); + output.extend(sender.into_word()); + output.extend(origin.into_word()); + output.extend(coinbase.into_word()); + + output.extend(contract_codehash); + output.extend(arb_precompile_codehash); + output.extend(eth_precompile_codehash); + output.extend(code); + + output.extend(ink_price.to_be_bytes()); + output.extend(gas_left_before.to_be_bytes()); + output.extend(ink_left_before.to_be_bytes()); + output.extend(gas_left_after.to_be_bytes()); + output.extend(ink_left_after.to_be_bytes()); + Ok(output) +} diff --git a/arbitrator/stylus/tests/exit-early/exit-early.wat b/arbitrator/stylus/tests/exit-early/exit-early.wat new file mode 100644 index 0000000000..f5ed37cd64 --- /dev/null +++ b/arbitrator/stylus/tests/exit-early/exit-early.wat @@ -0,0 +1,24 @@ +;; Copyright 2024, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "exit_early" (func $exit (param i32))) + (memory (export "memory") 1 1) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + ;; write args to offset 0 + (call $read_args (i32.const 0)) + + ;; set the args as the result + (call $write_result (i32.const 0) (local.get $args_len)) + + ;; exit with the status code given by the first byte + i32.const 0 + i32.load8_u + call $exit + + ;; unreachable + (i32.const 0xff) + ) +) diff --git a/arbitrator/stylus/tests/exit-early/panic-after-write.wat b/arbitrator/stylus/tests/exit-early/panic-after-write.wat new file mode 100644 index 0000000000..8e993ffc51 --- /dev/null +++ b/arbitrator/stylus/tests/exit-early/panic-after-write.wat @@ -0,0 +1,19 @@ +;; Copyright 2024, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "exit_early" (func $exit (param i32))) + (memory (export "memory") 1 1) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + ;; write args to offset 0 + (call $read_args (i32.const 0)) + + ;; set the args as the result + (call $write_result (i32.const 0) (local.get $args_len)) + + ;; perform a hard revert (results should be discarded) + unreachable + ) +) diff --git a/arbitrator/stylus/tests/fallible/.cargo/config b/arbitrator/stylus/tests/fallible/.cargo/config new file mode 100644 index 0000000000..f4e8c002fc --- /dev/null +++ b/arbitrator/stylus/tests/fallible/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/fallible/Cargo.lock b/arbitrator/stylus/tests/fallible/Cargo.lock new file mode 100644 index 0000000000..252edfbbf0 --- /dev/null +++ b/arbitrator/stylus/tests/fallible/Cargo.lock @@ -0,0 +1,543 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e416903084d3392ebd32d94735c395d6709415b76c7728e594d3f996f2b03e65" +dependencies = [ + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "ruint", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "fallible" +version = "0.1.0" +dependencies = [ + "stylus-sdk", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bitflags", + "byteorder", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/fallible/Cargo.toml b/arbitrator/stylus/tests/fallible/Cargo.toml new file mode 100644 index 0000000000..36d57c3f06 --- /dev/null +++ b/arbitrator/stylus/tests/fallible/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "fallible" +version = "0.1.0" +edition = "2021" + +[dependencies] +stylus-sdk = { path = "../../../langs/rust/stylus-sdk" } + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" + +# uncomment to optimize for size +# opt-level = "z" + +[workspace] diff --git a/arbitrator/stylus/tests/fallible/src/main.rs b/arbitrator/stylus/tests/fallible/src/main.rs new file mode 100644 index 0000000000..a8ccd15cb8 --- /dev/null +++ b/arbitrator/stylus/tests/fallible/src/main.rs @@ -0,0 +1,16 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![no_main] + +use stylus_sdk::stylus_proc::entrypoint; + +/// A program that will fail on certain inputs +#[entrypoint] +fn user_main(input: Vec) -> Result, Vec> { + if input[0] == 0 { + core::arch::wasm32::unreachable() + } else { + Ok(input) + } +} diff --git a/arbitrator/stylus/tests/grow/fixed.wat b/arbitrator/stylus/tests/grow/fixed.wat new file mode 100644 index 0000000000..7d6cc3afff --- /dev/null +++ b/arbitrator/stylus/tests/grow/fixed.wat @@ -0,0 +1,25 @@ +;; Copyright 2023-2024, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "console" "tee_i32" (func $tee_i32 (param i32) (result i32))) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + ;; fail to grow the memory a non-zero number of pages + i32.const -65537 + call $tee_i32 + memory.grow + call $tee_i32 + i32.const -1 + i32.eq + i32.eqz + (if (then unreachable)) + + ;; succeed growing 0 pages + i32.const 0 + memory.grow + call $tee_i32 + i32.eqz + i32.eqz + ) + (memory (export "memory") 0 0) +) diff --git a/arbitrator/stylus/tests/grow/grow-120.wat b/arbitrator/stylus/tests/grow/grow-120.wat new file mode 100644 index 0000000000..a76c420854 --- /dev/null +++ b/arbitrator/stylus/tests/grow/grow-120.wat @@ -0,0 +1,9 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (func (export "user_entrypoint") (param $args_len i32) (result i32) + i32.const 0 + ) + (memory (export "memory") 120 120) +) diff --git a/arbitrator/stylus/tests/grow/grow-and-call.wat b/arbitrator/stylus/tests/grow/grow-and-call.wat new file mode 100644 index 0000000000..6fbe7429f0 --- /dev/null +++ b/arbitrator/stylus/tests/grow/grow-and-call.wat @@ -0,0 +1,37 @@ +;; Copyright 2023-2024, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "pay_for_memory_grow" (func (param i32))) + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "msg_value" (func $msg_value (param i32))) + (import "vm_hooks" "call_contract" (func $call_contract (param i32 i32 i32 i32 i64 i32) (result i32))) + (import "console" "tee_i32" (func $tee (param i32) (result i32))) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + + ;; store the target size argument at offset 0 + i32.const 0 + call $read_args + + ;; grow the extra pages + i32.const 0 + i32.load8_u + memory.grow + drop + + ;; copy the message value + i32.const 0x1000 + call $msg_value + + ;; static call target contract + i32.const 1 ;; address + i32.const 21 ;; calldata + (i32.sub (local.get $args_len) (i32.const 21)) ;; calldata len + i32.const 0x1000 ;; callvalue + i64.const -1 ;; all gas + i32.const 0x2000 ;; return_data_len ptr + call $call_contract + ) + (memory (export "memory") 4) +) diff --git a/arbitrator/stylus/tests/grow/mem-write.wat b/arbitrator/stylus/tests/grow/mem-write.wat new file mode 100644 index 0000000000..ec6efd9739 --- /dev/null +++ b/arbitrator/stylus/tests/grow/mem-write.wat @@ -0,0 +1,45 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "pay_for_memory_grow" (func $pay_for_memory_grow (param i32))) + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "console" "tee_i32" (func $tee_i32 (param i32) (result i32))) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + local.get $args_len + i32.eqz + (if (then + ;; write an empty result to offset 0 + (call $write_result (i32.const 0) (i32.const 0)) + (return (i32.const 0)) + )) + + ;; grow 1 page so that we can read our args + i32.const 1 + memory.grow + drop + + ;; store the size argument at offset 0 + i32.const 0 + call $read_args + + ;; read the argument and grow the remainder + i32.const 0 + i32.load8_u + i32.const 1 + i32.sub + memory.grow + drop + + ;; write a result (should panic if out of bounds) + i32.const 1 + i32.load + i32.const 5 + i32.load + call $write_result + + i32.const 0 + ) + (memory (export "memory") 0) +) diff --git a/arbitrator/stylus/tests/keccak-100/Cargo.lock b/arbitrator/stylus/tests/keccak-100/Cargo.lock new file mode 100644 index 0000000000..d3ff2a09a2 --- /dev/null +++ b/arbitrator/stylus/tests/keccak-100/Cargo.lock @@ -0,0 +1,544 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e416903084d3392ebd32d94735c395d6709415b76c7728e594d3f996f2b03e65" +dependencies = [ + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "ruint", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-100" +version = "0.1.0" +dependencies = [ + "sha3", + "stylus-sdk", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bitflags", + "byteorder", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/keccak-100/Cargo.toml b/arbitrator/stylus/tests/keccak-100/Cargo.toml new file mode 100644 index 0000000000..e63f347673 --- /dev/null +++ b/arbitrator/stylus/tests/keccak-100/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "keccak-100" +version = "0.1.0" +edition = "2021" + +[dependencies] +sha3 = "0.10.5" +stylus-sdk = { path = "../../../langs/rust/stylus-sdk" } + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" + +# uncomment to optimize for size +# opt-level = "z" + +[workspace] diff --git a/arbitrator/stylus/tests/keccak-100/src/main.rs b/arbitrator/stylus/tests/keccak-100/src/main.rs new file mode 100644 index 0000000000..1f0f602859 --- /dev/null +++ b/arbitrator/stylus/tests/keccak-100/src/main.rs @@ -0,0 +1,23 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![no_main] + +use sha3::{Digest, Keccak256}; +use stylus_sdk::stylus_proc::entrypoint; + +#[entrypoint] +fn user_main(_: Vec) -> Result, Vec> { + let mut data = [0; 32]; + for _ in 0..100 { + data = keccak(&data); + } + assert_ne!(data, [0; 32]); + Ok(data.as_ref().into()) +} + +fn keccak(preimage: &[u8]) -> [u8; 32] { + let mut hasher = Keccak256::new(); + hasher.update(preimage); + hasher.finalize().into() +} diff --git a/arbitrator/stylus/tests/keccak/Cargo.lock b/arbitrator/stylus/tests/keccak/Cargo.lock new file mode 100644 index 0000000000..5b5344e941 --- /dev/null +++ b/arbitrator/stylus/tests/keccak/Cargo.lock @@ -0,0 +1,544 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e416903084d3392ebd32d94735c395d6709415b76c7728e594d3f996f2b03e65" +dependencies = [ + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "ruint", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.0" +dependencies = [ + "sha3", + "stylus-sdk", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bitflags", + "byteorder", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak 0.1.4", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/keccak/Cargo.toml b/arbitrator/stylus/tests/keccak/Cargo.toml new file mode 100644 index 0000000000..b9c4baa750 --- /dev/null +++ b/arbitrator/stylus/tests/keccak/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "keccak" +version = "0.1.0" +edition = "2021" + +[dependencies] +sha3 = "0.10.5" +stylus-sdk = { path = "../../../langs/rust/stylus-sdk" } + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" + +# uncomment to optimize for size +# opt-level = "z" + +[workspace] diff --git a/arbitrator/stylus/tests/keccak/src/main.rs b/arbitrator/stylus/tests/keccak/src/main.rs new file mode 100644 index 0000000000..dd14dd30e1 --- /dev/null +++ b/arbitrator/stylus/tests/keccak/src/main.rs @@ -0,0 +1,26 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![no_main] + +use sha3::{Digest, Keccak256}; +use stylus_sdk::{alloy_primitives, crypto, prelude::*}; + +#[entrypoint] +fn user_main(input: Vec) -> Result, Vec> { + let mut data = keccak(&input[1..]); + let rounds = input[0]; + for _ in 1..rounds { + let hash = keccak(&data); + assert_eq!(hash, crypto::keccak(data)); + assert_eq!(hash, alloy_primitives::keccak256(data)); + data = hash; + } + Ok(data.as_ref().into()) +} + +fn keccak(preimage: &[u8]) -> [u8; 32] { + let mut hasher = Keccak256::new(); + hasher.update(preimage); + hasher.finalize().into() +} diff --git a/arbitrator/stylus/tests/log/.cargo/config b/arbitrator/stylus/tests/log/.cargo/config new file mode 100644 index 0000000000..f4e8c002fc --- /dev/null +++ b/arbitrator/stylus/tests/log/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/log/Cargo.lock b/arbitrator/stylus/tests/log/Cargo.lock new file mode 100644 index 0000000000..0bb2ca3330 --- /dev/null +++ b/arbitrator/stylus/tests/log/Cargo.lock @@ -0,0 +1,544 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e416903084d3392ebd32d94735c395d6709415b76c7728e594d3f996f2b03e65" +dependencies = [ + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "ruint", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "log" +version = "0.1.0" +dependencies = [ + "hex", + "stylus-sdk", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bitflags", + "byteorder", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/log/Cargo.toml b/arbitrator/stylus/tests/log/Cargo.toml new file mode 100644 index 0000000000..f3bba1d098 --- /dev/null +++ b/arbitrator/stylus/tests/log/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "log" +version = "0.1.0" +edition = "2021" + +[dependencies] +stylus-sdk = { path = "../../../langs/rust/stylus-sdk" } +hex = "0.4.3" + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" + +# uncomment to optimize for size +# opt-level = "z" + +[workspace] diff --git a/arbitrator/stylus/tests/log/src/main.rs b/arbitrator/stylus/tests/log/src/main.rs new file mode 100644 index 0000000000..0ce6f95fe2 --- /dev/null +++ b/arbitrator/stylus/tests/log/src/main.rs @@ -0,0 +1,20 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![no_main] + +use stylus_sdk::{alloy_primitives::B256, evm, prelude::*}; + +#[entrypoint] +fn user_main(input: Vec) -> Result, Vec> { + let num_topics = input[0]; + let mut input = &input[1..]; + + let mut topics = vec![]; + for _ in 0..num_topics { + topics.push(B256::try_from(&input[..32]).unwrap()); + input = &input[32..]; + } + evm::raw_log(&topics, input).unwrap(); + Ok(vec![]) +} diff --git a/arbitrator/stylus/tests/math/Cargo.lock b/arbitrator/stylus/tests/math/Cargo.lock new file mode 100644 index 0000000000..3bf041ecd5 --- /dev/null +++ b/arbitrator/stylus/tests/math/Cargo.lock @@ -0,0 +1,543 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e416903084d3392ebd32d94735c395d6709415b76c7728e594d3f996f2b03e65" +dependencies = [ + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "ruint", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "math" +version = "0.1.0" +dependencies = [ + "stylus-sdk", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bitflags", + "byteorder", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/math/Cargo.toml b/arbitrator/stylus/tests/math/Cargo.toml new file mode 100644 index 0000000000..5fa060723c --- /dev/null +++ b/arbitrator/stylus/tests/math/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "math" +version = "0.1.0" +edition = "2021" + +[dependencies] +stylus-sdk = { path = "../../../langs/rust/stylus-sdk", features = ["debug"] } + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" + +# uncomment to optimize for size +# opt-level = "z" + +[workspace] diff --git a/arbitrator/stylus/tests/math/src/main.rs b/arbitrator/stylus/tests/math/src/main.rs new file mode 100644 index 0000000000..59e2450d59 --- /dev/null +++ b/arbitrator/stylus/tests/math/src/main.rs @@ -0,0 +1,39 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![no_main] + +use stylus_sdk::{ + alloy_primitives::{b256, B256}, + prelude::*, + ArbResult, +}; +extern crate alloc; + +#[link(wasm_import_module = "vm_hooks")] +extern "C" { + fn math_div(value: *mut u8, divisor: *const u8); + fn math_mod(value: *mut u8, modulus: *const u8); + fn math_pow(value: *mut u8, exponent: *const u8); + fn math_add_mod(value: *mut u8, addend: *const u8, modulus: *const u8); + fn math_mul_mod(value: *mut u8, multiplier: *const u8, modulus: *const u8); +} + +#[entrypoint] +fn user_main(_: Vec) -> ArbResult { + let mut value = b256!("eddecf107b5740cef7f5a01e3ea7e287665c4e75a8eb6afae2fda2e3d4367786"); + let unknown = b256!("c6178c2de1078cd36c3bd302cde755340d7f17fcb3fcc0b9c333ba03b217029f"); + let ed25519 = b256!("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"); + + let part_1 = b256!("000000000000000000000000000000000000000000000000eddecf107b5740ce"); + let part_2 = b256!("000000000000000000000000000000000000000000000000fffffffefffffc2f"); + let part_3 = b256!("000000000000000000000000000000000000000000000000c6178c2de1078cd3"); + unsafe { + math_mul_mod(value.as_mut_ptr(), unknown.as_ptr(), ed25519.as_ptr()); + math_add_mod(value.as_mut_ptr(), ed25519.as_ptr(), unknown.as_ptr()); + math_div(value.as_mut_ptr(), part_1.as_ptr()); + math_pow(value.as_mut_ptr(), part_2.as_ptr()); + math_mod(value.as_mut_ptr(), part_3.as_ptr()); + Ok(value.0.into()) + } +} diff --git a/arbitrator/stylus/tests/memory.wat b/arbitrator/stylus/tests/memory.wat new file mode 100644 index 0000000000..2c867c35b8 --- /dev/null +++ b/arbitrator/stylus/tests/memory.wat @@ -0,0 +1,59 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "pay_for_memory_grow" (func (param i32))) + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (local $size i32) (local $step i32) + + ;; store the target size argument at offset 0 + i32.const 0 + call $read_args + + ;; copy the target size + i32.const 0 + i32.load8_u + local.set $size + + ;; copy the step size + i32.const 1 + i32.load8_u + local.set $step + + ;; grow until equal to the target size + (loop $loop + + ;; grow by $step, shrinking if needed + (i32.add (local.get $step) (memory.size)) + local.get $size + i32.gt_u + (if (then + (i32.sub (local.get $size) (memory.size)) + local.set $step + )) + + (memory.grow (local.get $step)) + drop + + ;; loop if too small + (i32.lt_u (memory.size) (local.get $size)) + br_if $loop + ) + + ;; store the memory size at offset 0 + i32.const 0 + memory.size + i32.store + + ;; make that single byte the return data + i32.const 0 + i32.const 1 + call $write_result + + ;; return success + i32.const 0 + ) + (memory (export "memory") 1 128) +) diff --git a/arbitrator/stylus/tests/memory2.wat b/arbitrator/stylus/tests/memory2.wat new file mode 100644 index 0000000000..f0001ad517 --- /dev/null +++ b/arbitrator/stylus/tests/memory2.wat @@ -0,0 +1,12 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "pay_for_memory_grow" (func $pay_for_memory_grow (param i32))) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (call $pay_for_memory_grow (i32.const 0)) + (call $pay_for_memory_grow (i32.sub (i32.const 0) (i32.const 1))) + i32.const 0 + ) + (memory (export "memory") 0) +) diff --git a/arbitrator/stylus/tests/module-mod.wat b/arbitrator/stylus/tests/module-mod.wat new file mode 100644 index 0000000000..b8e856bab2 --- /dev/null +++ b/arbitrator/stylus/tests/module-mod.wat @@ -0,0 +1,9 @@ +;; Copyright 2022, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (import "test" "noop" (func)) + (memory (export "memory") 0 0) + (func (export "void")) + (func (export "more") (param i32 i64) (result f32) + unreachable)) diff --git a/arbitrator/stylus/tests/multicall/.cargo/config b/arbitrator/stylus/tests/multicall/.cargo/config new file mode 100644 index 0000000000..f4e8c002fc --- /dev/null +++ b/arbitrator/stylus/tests/multicall/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/multicall/Cargo.lock b/arbitrator/stylus/tests/multicall/Cargo.lock new file mode 100644 index 0000000000..ca70689bf7 --- /dev/null +++ b/arbitrator/stylus/tests/multicall/Cargo.lock @@ -0,0 +1,838 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e416903084d3392ebd32d94735c395d6709415b76c7728e594d3f996f2b03e65" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if 1.0.0", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "proptest", + "ruint", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d58d9f5da7b40e9bfff0b7e7816700be4019db97d4b6359fe7f94a9e22e42ac" +dependencies = [ + "arrayvec", + "bytes", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", + "serde", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "mini-alloc" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9993556d3850cdbd0da06a3dc81297edcfa050048952d84d75e8b944e8f5af" +dependencies = [ + "cfg-if 1.0.0", + "wee_alloc", +] + +[[package]] +name = "multicall" +version = "0.1.0" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "hex", + "mini-alloc", + "stylus-sdk", + "wee_alloc", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bit-set", + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax 0.6.29", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax 0.7.4", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.4", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if 1.0.0", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if 1.0.0", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tempfile" +version = "3.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "rustix", + "windows-sys", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/multicall/Cargo.toml b/arbitrator/stylus/tests/multicall/Cargo.toml new file mode 100644 index 0000000000..3bc48c6826 --- /dev/null +++ b/arbitrator/stylus/tests/multicall/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "multicall" +version = "0.1.0" +edition = "2021" + +[dependencies] +alloy-primitives = "0.3.1" +alloy-sol-types = "0.3.1" +mini-alloc = "0.4.2" +stylus-sdk = { path = "../../../langs/rust/stylus-sdk", features = ["reentrant"] } +hex = "0.4.3" +wee_alloc = "0.4.5" + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" + +# uncomment to optimize for size +# opt-level = "z" + +[workspace] diff --git a/arbitrator/stylus/tests/multicall/src/main.rs b/arbitrator/stylus/tests/multicall/src/main.rs new file mode 100644 index 0000000000..fd6929b8f1 --- /dev/null +++ b/arbitrator/stylus/tests/multicall/src/main.rs @@ -0,0 +1,121 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![no_main] + +extern crate alloc; + +use stylus_sdk::{ + storage::{StorageCache, GlobalStorage}, + alloy_primitives::{Address, B256}, + alloy_sol_types::sol, + call::RawCall, + console, + evm, + prelude::*, +}; + +use wee_alloc::WeeAlloc; + +#[global_allocator] +static ALLOC: WeeAlloc = WeeAlloc::INIT; + +sol!{ + event Called(address addr, uint8 count, bool success, bytes return_data); + event Storage(bytes32 slot, bytes32 data, bool write); +} + +#[entrypoint] +fn user_main(input: Vec) -> Result, Vec> { + let mut input = input.as_slice(); + let count = input[0]; + input = &input[1..]; + + // combined output of all calls + let mut output = vec![]; + + console!("Performing {count} action(s)"); + for _ in 0..count { + let length = u32::from_be_bytes(input[..4].try_into().unwrap()) as usize; + input = &input[4..]; + + let next = &input[length..]; + let mut curr = &input[..length]; + + let kind = curr[0]; + curr = &curr[1..]; + + if kind & 0xf0 == 0 { + // caller + let mut value = None; + if kind & 0x3 == 0 { + value = Some(B256::try_from(&curr[..32]).unwrap()); + curr = &curr[32..]; + }; + + let addr = Address::try_from(&curr[..20]).unwrap(); + let data = &curr[20..]; + match value { + Some(value) if !value.is_zero() => console!( + "Calling {addr} with {} bytes and value {} {kind}", + data.len(), + hex::encode(value) + ), + _ => console!("Calling {addr} with {} bytes {kind}", curr.len()), + } + + let raw_call = match kind & 0x3 { + 0 => RawCall::new_with_value(value.unwrap_or_default().into()), + 1 => RawCall::new_delegate(), + 2 => RawCall::new_static(), + x => panic!("unknown call kind {x}"), + }; + let (success, return_data) = match unsafe { raw_call.call(addr, data) } { + Ok(return_data) => (true, return_data), + Err(revert_data) => { + if kind & 0x4 == 0 { + return Err(revert_data) + } + (false, vec![]) + }, + }; + + if !return_data.is_empty() { + console!("Contract {addr} returned {} bytes", return_data.len()); + } + if kind & 0x8 != 0 { + evm::log(Called { addr, count, success, return_data: return_data.clone() }) + } + output.extend(return_data); + } else if kind & 0xf0 == 0x10 { + // storage + let slot = B256::try_from(&curr[..32]).unwrap(); + curr = &curr[32..]; + let data; + let write; + if kind & 0x7 == 0 { + console!("writing slot {}", curr.len()); + data = B256::try_from(&curr[..32]).unwrap(); + write = true; + unsafe { StorageCache::set_word(slot.into(), data.into()) }; + StorageCache::flush(); + } else if kind & 0x7 == 1{ + console!("reading slot"); + write = false; + data = StorageCache::get_word(slot.into()); + output.extend(data.clone()); + } else { + panic!("unknown storage kind {kind}") + } + if kind & 0x8 != 0 { + console!("slot: {}, data: {}, write {write}", slot, data); + evm::log(Storage { slot: slot.into(), data: data.into(), write }) + } + } else { + panic!("unknown action {kind}") + } + input = next; + } + + Ok(output) +} diff --git a/arbitrator/stylus/tests/read-return-data/.cargo/config b/arbitrator/stylus/tests/read-return-data/.cargo/config new file mode 100644 index 0000000000..aa59d2ee1c --- /dev/null +++ b/arbitrator/stylus/tests/read-return-data/.cargo/config @@ -0,0 +1,3 @@ +[build] +target = "wasm32-unknown-unknown" + diff --git a/arbitrator/stylus/tests/read-return-data/Cargo.lock b/arbitrator/stylus/tests/read-return-data/Cargo.lock new file mode 100644 index 0000000000..2d551af6ef --- /dev/null +++ b/arbitrator/stylus/tests/read-return-data/Cargo.lock @@ -0,0 +1,544 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8f9420f797f2d9e935edf629310eb938a0d839f984e25327f3c7eed22300c" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e416903084d3392ebd32d94735c395d6709415b76c7728e594d3f996f2b03e65" +dependencies = [ + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "ruint", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bitflags", + "byteorder", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "read-return-data" +version = "0.1.0" +dependencies = [ + "hex", + "stylus-sdk", +] + +[[package]] +name = "regex" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/read-return-data/Cargo.toml b/arbitrator/stylus/tests/read-return-data/Cargo.toml new file mode 100644 index 0000000000..e011ba8c38 --- /dev/null +++ b/arbitrator/stylus/tests/read-return-data/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "read-return-data" +version = "0.1.0" +edition = "2021" + +[dependencies] +stylus-sdk = { path = "../../../langs/rust/stylus-sdk" } +hex = "0.4.3" + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" + +# uncomment to optimize for size +# opt-level = "z" + +[workspace] + diff --git a/arbitrator/stylus/tests/read-return-data/src/main.rs b/arbitrator/stylus/tests/read-return-data/src/main.rs new file mode 100644 index 0000000000..3f13deff15 --- /dev/null +++ b/arbitrator/stylus/tests/read-return-data/src/main.rs @@ -0,0 +1,65 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![no_main] + +use stylus_sdk::{ + alloy_primitives::{b256, Address}, + call::RawCall, + console, contract, + prelude::*, +}; + +macro_rules! error { + ($($msg:tt)*) => {{ + console!($($msg)*); + panic!("invalid data") + }}; +} + +#[entrypoint] +fn user_main(input: Vec) -> Result, Vec> { + let mut call_data = input.as_slice(); + let mut read = || { + let x = usize::from_be_bytes(call_data[..4].try_into().unwrap()); + call_data = &call_data[4..]; + x + }; + + let call_type = read(); + let offset = read(); + let size = read(); + let expected_size = read(); + let count = read(); + + // Call identity precompile to test return data + let precompile: Address = Address::from_word(b256!( + "0000000000000000000000000000000000000000000000000000000000000004" + )); + + let safe_offset = offset.min(call_data.len()); + + if call_type == 2 { + RawCall::new() + .limit_return_data(offset, size) + .call(precompile, call_data)?; + } + + for _ in 0..count { + let data = match call_type { + 0 => RawCall::new().call(precompile, call_data)?, + 1 => RawCall::new() + .limit_return_data(offset, size) + .call(precompile, call_data)?, + 2 => contract::read_return_data(offset, Some(size)), + _ => error!("unknown call_type {call_type}"), + }; + + let expected_data = &call_data[safe_offset..][..expected_size]; + if data != expected_data { + error!("call_type: {call_type}, calldata: {call_data:?}, offset: {offset}, size: {size} → {data:?} {expected_data:?}"); + } + } + + Ok(vec![]) +} diff --git a/arbitrator/stylus/tests/sdk-storage/.cargo/config b/arbitrator/stylus/tests/sdk-storage/.cargo/config new file mode 100644 index 0000000000..f4e8c002fc --- /dev/null +++ b/arbitrator/stylus/tests/sdk-storage/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/sdk-storage/Cargo.lock b/arbitrator/stylus/tests/sdk-storage/Cargo.lock new file mode 100644 index 0000000000..778a091be7 --- /dev/null +++ b/arbitrator/stylus/tests/sdk-storage/Cargo.lock @@ -0,0 +1,600 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e416903084d3392ebd32d94735c395d6709415b76c7728e594d3f996f2b03e65" +dependencies = [ + "bytes", + "cfg-if 1.0.0", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "ruint", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "mini-alloc" +version = "0.4.2" +dependencies = [ + "cfg-if 1.0.0", + "wee_alloc", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bitflags", + "byteorder", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "sdk-storage" +version = "0.1.0" +dependencies = [ + "hex", + "mini-alloc", + "stylus-sdk", + "wee_alloc", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if 1.0.0", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if 1.0.0", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/sdk-storage/Cargo.toml b/arbitrator/stylus/tests/sdk-storage/Cargo.toml new file mode 100644 index 0000000000..c136762b5f --- /dev/null +++ b/arbitrator/stylus/tests/sdk-storage/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "sdk-storage" +version = "0.1.0" +edition = "2021" + +[dependencies] +stylus-sdk.path = "../../../langs/rust/stylus-sdk" +mini-alloc.path = "../../../langs/rust/mini-alloc" +hex = "0.4.3" +wee_alloc = "0.4.5" + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" +opt-level = "s" + +[workspace] diff --git a/arbitrator/stylus/tests/sdk-storage/src/main.rs b/arbitrator/stylus/tests/sdk-storage/src/main.rs new file mode 100644 index 0000000000..4bfe8b6020 --- /dev/null +++ b/arbitrator/stylus/tests/sdk-storage/src/main.rs @@ -0,0 +1,323 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![no_main] + +use stylus_sdk::{ + alloy_primitives::{Address, Signed, Uint, B256, I32, U16, U256, U64, U8}, + prelude::*, +}; +use mini_alloc::MiniAlloc; + +#[global_allocator] +static ALLOC: MiniAlloc = MiniAlloc::INIT; + +sol_storage! { + pub struct Contract { + bool flag; + address owner; + address other; + Struct sub; + Struct[] structs; + uint64[] vector; + uint40[][] nested; + bytes bytes_full; + bytes bytes_long; + string chars; + Maps maps; + Arrays arrays; + } + + #[derive(Erase)] + pub struct Struct { + uint16 num; + int32 other; + bytes32 word; + } + + pub struct Maps { + mapping(uint256 => address) basic; + mapping(address => bool[]) vects; + mapping(int32 => address)[] array; + mapping(bytes1 => mapping(bool => uint256)) nested; + mapping(string => Struct) structs; + } + + pub struct Arrays { + string[4] strings; + + uint8 spacer; + uint24[5] packed; + uint8 trail; + + address[2] spill; + uint8[2][4] matrix; + int96[4][] vector; + int96[][4] vectors; + Struct[3] structs; + } +} + +#[entrypoint] +fn user_main(input: Vec) -> Result, Vec> { + let contract = unsafe { Contract::new(U256::ZERO, 0) }; + let selector = u32::from_be_bytes(input[0..4].try_into().unwrap()); + match selector { + 0xf809f205 => populate(contract), + 0xa7f43779 => remove(contract), + _ => panic!("unknown method"), + } + Ok(vec![]) +} + +fn populate(mut contract: Contract) { + // test primitives + let owner = Address::with_last_byte(0x70); + contract.flag.set(true); + contract.owner.set(owner); + assert_eq!(contract.owner.get(), owner); + + let mut setter = contract.other.load_mut(); + setter.set(Address::with_last_byte(0x30)); + + // test structs + contract.sub.num.set(U16::from(32)); + contract.sub.other.set(I32::MAX); + contract.sub.word.set(U256::from(64).into()); + assert_eq!(contract.sub.num.get(), U16::from(32)); + assert_eq!(contract.sub.other.get(), I32::MAX); + assert_eq!(contract.sub.word.get(), B256::from(U256::from(64))); + + // test primitive vectors + let mut vector = contract.vector; + for i in (0..32).map(U64::from) { + vector.push(i); + } + for i in (0..32).map(U64::from) { + assert_eq!(vector.get(i), Some(i)); + } + let value = U64::from(77); + let mut setter = vector.get_mut(7).unwrap(); + setter.set(value); + assert_eq!(setter.get(), value); + + // test nested vectors + let mut nested = contract.nested; + for w in 0..10 { + let mut inner = nested.grow(); + for i in 0..w { + inner.push(Uint::from(i)); + } + assert_eq!(inner.len(), w); + assert_eq!(nested.len(), w + 1); + } + for w in 0..10 { + let mut inner = nested.get_mut(w).unwrap(); + + for i in 0..w { + let value = inner.get(i).unwrap() * Uint::from(2); + let mut setter = inner.get_mut(i).unwrap(); + setter.set(value); + assert_eq!(inner.get(i), Some(value)); + } + } + + // test bytes + let mut bytes_full = contract.bytes_full; + let mut bytes_long = contract.bytes_long; + + for i in 0..31 { + bytes_full.push(i); + } + for i in 0..80 { + bytes_long.push(i); + } + for i in 0..31 { + assert_eq!(bytes_full.get(i), Some(i)); + } + for i in 0..80 { + let setter = bytes_long.get_mut(i).unwrap(); + assert_eq!(setter.get()[0], i); + } + assert_eq!(bytes_full.get(32), None); + assert_eq!(bytes_long.get(80), None); + + // test strings + let mut chars = contract.chars; + assert!(chars.is_empty() && chars.len() == 0); + assert_eq!(chars.get_string(), ""); + for c in "arbitrum stylus".chars() { + chars.push(c); + } + assert_eq!(chars.get_string(), "arbitrum stylus"); + + // test basic maps + let maps = contract.maps; + let mut basic = maps.basic; + for i in (0..16).map(U256::from) { + basic.insert(i, Address::from_word(B256::from(i))); + } + for i in 0..16 { + assert_eq!(basic.get(U256::from(i)), Address::with_last_byte(i)); + } + assert_eq!(basic.get(U256::MAX), Address::ZERO); + + // test map of vectors + let mut vects = maps.vects; + for a in 0..4 { + let mut bools = vects.setter(Address::with_last_byte(a)); + for _ in 0..=a { + bools.push(true) + } + } + + // test vector of maps + let mut array = maps.array; + for i in 0..4 { + let mut map = array.grow(); + let value = I32::from_le_bytes::<4>((i as u32).to_le_bytes()); + map.insert(value, Address::with_last_byte(i)); + } + + // test maps of maps + let mut nested = maps.nested; + for i in 0..4 { + let mut inner = nested.setter(U8::from(i).into()); + let mut value = inner.setter(U8::from((i % 2 == 0) as u8)); + value.set(Uint::from(i + 1)); + } + + // test map of structs (TODO: direct assignment) + let mut structs = maps.structs; + let mut entry = structs.setter("stylus".to_string()); + entry.num.set(contract.sub.num.get()); + entry.other.set(contract.sub.other.get()); + entry.word.set(contract.sub.word.get()); + + // test vec of structs + let mut structs = contract.structs; + for _ in 0..4 { + let mut entry = structs.grow(); + entry.num.set(contract.sub.num.get()); + entry.other.set(contract.sub.other.get()); + entry.word.set(contract.sub.word.get()); + } + + // test fixed arrays + let mut arrays = contract.arrays; + let mut slot = arrays.strings.setter(2).unwrap(); + slot.set_str("L2 is for you!"); + + // test packed arrays + for i in 0..5 { + let mut slot = arrays.packed.get_mut(i).unwrap(); + slot.set(Uint::from(i)); + } + + // test arrays that don't fit into a single word + for i in 0..2 { + let mut slot = arrays.spill.get_mut(i).unwrap(); + slot.set(Address::with_last_byte(i as u8)); + } + + // test 2d arrays + let mut matrix = arrays.matrix; + for i in 0..4 { + let mut inner = matrix.get_mut(i).unwrap(); + let mut slot = inner.get_mut(0).unwrap(); + slot.set(U8::from(i)); + + let value = slot.get(); + let mut slot = inner.get_mut(1).unwrap(); + slot.set(value + U8::from(1)); + } + + // test vector of arrays + for _ in 0..3 { + let mut fixed = arrays.vector.grow(); + for i in 0..4 { + let mut slot = fixed.get_mut(i).unwrap(); + slot.set(Signed::from_raw(Uint::from(i))); + } + } + + // test array of vectors + for w in 0..4 { + let mut vector = arrays.vectors.setter(w).unwrap(); + for i in 0..4 { + vector.push(Signed::from_raw(Uint::from(i))); + } + } + + // test array of structs + for i in 0..3 { + let mut entry = arrays.structs.get_mut(i).unwrap(); + + entry.num.set(contract.sub.num.get()); + entry.other.set(contract.sub.other.get()); + entry.word.set(contract.sub.word.get()); + } +} + +fn remove(mut contract: Contract) { + // pop all elements + let mut bytes_full = contract.bytes_full; + while let Some(value) = bytes_full.pop() { + assert_eq!(value as usize, bytes_full.len()); + } + assert!(bytes_full.is_empty()); + + // pop until representation change + let mut bytes_long = contract.bytes_long; + while bytes_long.len() > 16 { + assert!(bytes_long.pop().is_some()); + } + + // overwrite strings + let mut chars = contract.chars; + let spiders = r"/\oo/\ //\\(oo)//\\ /\oo/\"; + chars.set_str(spiders.repeat(6)); + chars.set_str("wasm is cute <3"); + + // pop all elements + let mut vector = contract.vector; + while let Some(x) = vector.pop() { + assert!(x == U64::from(vector.len()) || x == U64::from(77)); + } + assert!(vector.is_empty() && vector.len() == 0); + + // erase inner vectors + let mut nested = contract.nested; + while nested.len() > 2 { + nested.erase_last(); + } + nested.shrink().map(|mut x| x.erase()); + + // erase map elements + let maps = contract.maps; + let mut basic = maps.basic; + for i in 0..7 { + basic.delete(Uint::from(i)); + } + let value = basic.take(Uint::from(7)); + assert_eq!(value, Address::with_last_byte(7)); + let value = basic.replace(Uint::from(8), Address::with_last_byte(32)); + assert_eq!(value, Address::with_last_byte(8)); + + // erase vectors in map + let mut vects = maps.vects; + for a in 0..3 { + let mut bools = vects.setter(Address::with_last_byte(a)); + bools.erase(); + } + vects.delete(Address::with_last_byte(3)); + + // erase a struct + contract.structs.erase_last(); + + // erase fixed arrays + contract.arrays.matrix.erase(); + contract.arrays.vector.erase(); + contract.arrays.vectors.erase(); + contract.arrays.structs.erase(); +} diff --git a/arbitrator/stylus/tests/start.wat b/arbitrator/stylus/tests/start.wat new file mode 100644 index 0000000000..9d74df4c18 --- /dev/null +++ b/arbitrator/stylus/tests/start.wat @@ -0,0 +1,18 @@ +;; Copyright 2022, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (global $status (export "status") (mut i32) (i32.const 10)) + (memory 0 0) + (export "memory" (memory 0)) + (type $void (func (param) (result))) + (func $start (export "move_me") (type $void) + get_global $status + i32.const 1 + i32.add + set_global $status ;; increment the global + ) + (func (export "user_entrypoint") (param $args_len i32) (result i32) + (i32.const 0) + ) + (start $start)) diff --git a/arbitrator/stylus/tests/storage/.cargo/config b/arbitrator/stylus/tests/storage/.cargo/config new file mode 100644 index 0000000000..f4e8c002fc --- /dev/null +++ b/arbitrator/stylus/tests/storage/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/storage/Cargo.lock b/arbitrator/stylus/tests/storage/Cargo.lock new file mode 100644 index 0000000000..a686950b2f --- /dev/null +++ b/arbitrator/stylus/tests/storage/Cargo.lock @@ -0,0 +1,543 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-primitives" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e416903084d3392ebd32d94735c395d6709415b76c7728e594d3f996f2b03e65" +dependencies = [ + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "ruint", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-macro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a74ceeffdacf9dd0910404d743d07273776fd17b85f9cb17b49a97e5c6055ce9" +dependencies = [ + "dunce", + "heck", + "proc-macro2", + "quote", + "syn 2.0.25", + "syn-solidity", + "tiny-keccak", +] + +[[package]] +name = "alloy-sol-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f347cb6bb307b3802ec455ef43ce00f5e590e0ceca3d2f3b070f5ee367e235" +dependencies = [ + "alloy-primitives", + "alloy-sol-macro", + "const-hex", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case 0.4.0", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bitflags", + "byteorder", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "unarray", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "regex" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" + +[[package]] +name = "ruint" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95294d6e3a6192f3aabf91c38f56505a625aa495533442744185a36d75a790c4" +dependencies = [ + "proptest", + "rand", + "ruint-macro", + "serde", + "valuable", + "zeroize", +] + +[[package]] +name = "ruint-macro" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e666a5496a0b2186dbcd0ff6106e29e093c15591bde62c20d3842007c6978a09" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "storage" +version = "0.1.0" +dependencies = [ + "stylus-sdk", +] + +[[package]] +name = "stylus-proc" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "convert_case 0.6.0", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "sha3", + "syn 1.0.109", + "syn-solidity", +] + +[[package]] +name = "stylus-sdk" +version = "0.4.2" +dependencies = [ + "alloy-primitives", + "alloy-sol-types", + "cfg-if", + "derivative", + "hex", + "keccak-const", + "lazy_static", + "stylus-proc", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn-solidity" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f995d2140b0f751dbe94365be2591edbf3d1b75dcfaeac14183abbd2ff07bd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/arbitrator/stylus/tests/storage/Cargo.toml b/arbitrator/stylus/tests/storage/Cargo.toml new file mode 100644 index 0000000000..0137fbd272 --- /dev/null +++ b/arbitrator/stylus/tests/storage/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "storage" +version = "0.1.0" +edition = "2021" + +[dependencies] +stylus-sdk = { path = "../../../langs/rust/stylus-sdk" } + +[profile.release] +codegen-units = 1 +strip = true +lto = true +panic = "abort" + +[workspace] diff --git a/arbitrator/stylus/tests/storage/src/main.rs b/arbitrator/stylus/tests/storage/src/main.rs new file mode 100644 index 0000000000..e19462991a --- /dev/null +++ b/arbitrator/stylus/tests/storage/src/main.rs @@ -0,0 +1,48 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![no_main] + +use stylus_sdk::{ + alloy_primitives::B256, + console, + storage::{StorageCache, GlobalStorage}, + stylus_proc::entrypoint, +}; + +#[link(wasm_import_module = "vm_hooks")] +extern "C" { + fn transient_load_bytes32(key: *const u8, dest: *mut u8); + fn transient_store_bytes32(key: *const u8, value: *const u8); +} + +#[entrypoint] +fn user_main(input: Vec) -> Result, Vec> { + let slot = B256::try_from(&input[1..33]).unwrap(); + + Ok(match input[0] { + 0 => { + console!("read {slot}"); + let data = StorageCache::get_word(slot.into()); + console!("value {data}"); + data.0.into() + } + 1 => { + console!("write {slot}"); + let data = B256::try_from(&input[33..]).unwrap(); + unsafe { StorageCache::set_word(slot.into(), data) }; + console!(("value {data}")); + vec![] + } + 2 => unsafe { + let mut data = [0; 32]; + transient_load_bytes32(slot.as_ptr(), data.as_mut_ptr()); + data.into() + } + _ => unsafe { + let data = B256::try_from(&input[33..]).unwrap(); + transient_store_bytes32(slot.as_ptr(), data.as_ptr()); + vec![] + } + }) +} diff --git a/arbitrator/stylus/tests/test.wat b/arbitrator/stylus/tests/test.wat new file mode 100644 index 0000000000..caef666ea6 --- /dev/null +++ b/arbitrator/stylus/tests/test.wat @@ -0,0 +1,5 @@ +;; Copyright 2022, Offchain Labs, Inc. +;; For license information, see https://github.com/nitro/blob/master/LICENSE + +(module + (func (export "test__noop"))) diff --git a/arbitrator/stylus/tests/timings/Cargo.lock b/arbitrator/stylus/tests/timings/Cargo.lock new file mode 100644 index 0000000000..61e05834be --- /dev/null +++ b/arbitrator/stylus/tests/timings/Cargo.lock @@ -0,0 +1,746 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "alloy-primitives" +version = "0.2.0" +source = "git+https://github.com/rachel-bousfield/alloy-core.git?branch=native-keccak#fd40d0a68a538a7583c8094c97e77fa2d9e49eb0" +dependencies = [ + "alloy-rlp", + "bytes", + "cfg-if", + "const-hex", + "derive_more", + "hex-literal", + "itoa", + "proptest", + "ruint2", + "serde", + "tiny-keccak", +] + +[[package]] +name = "alloy-rlp" +version = "0.2.0" +source = "git+https://github.com/rachel-bousfield/alloy-core.git?branch=native-keccak#fd40d0a68a538a7583c8094c97e77fa2d9e49eb0" +dependencies = [ + "arrayvec", + "bytes", + "smol_str", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-hex" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "268f52aae268980d03dd9544c1ea591965b2735b038d6998d6e4ab37c8c24445" +dependencies = [ + "cfg-if", + "cpufeatures", + "hex", + "serde", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cpufeatures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" +dependencies = [ + "libc", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" +dependencies = [ + "bit-set", + "bitflags", + "byteorder", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "ruint2" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b066b8e4fcea7fae86b6932d2449670b6b5545b7e8407841b2d3a916ff2a9f86" +dependencies = [ + "derive_more", + "ruint2-macro", + "rustc_version", + "thiserror", +] + +[[package]] +name = "ruint2-macro" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89dc553bc0cf4512a8b96caa2e21ed5f6e4b66bf28a1bd08fd9eb07c0b39b28c" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys 0.45.0", +] + +[[package]] +name = "rusty-fork" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "semver" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" + +[[package]] +name = "serde" +version = "1.0.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" + +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "smol_str" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74212e6bbe9a4352329b2f68ba3130c15a3f26fe88ff22dbdc6cdd58fa85e99c" +dependencies = [ + "serde", +] + +[[package]] +name = "stylus-sdk" +version = "0.1.0" +dependencies = [ + "alloy-primitives", + "hex", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys 0.45.0", +] + +[[package]] +name = "thiserror" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.25", +] + +[[package]] +name = "timings" +version = "0.1.0" +dependencies = [ + "sha3", + "stylus-sdk", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/arbitrator/stylus/tests/timings/block_basefee.wat b/arbitrator/stylus/tests/timings/block_basefee.wat new file mode 100644 index 0000000000..ef5e66b015 --- /dev/null +++ b/arbitrator/stylus/tests/timings/block_basefee.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "block_basefee" (func $test (param i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + i32.const 0 + call $test + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/block_coinbase.wat b/arbitrator/stylus/tests/timings/block_coinbase.wat new file mode 100644 index 0000000000..f7d6649978 --- /dev/null +++ b/arbitrator/stylus/tests/timings/block_coinbase.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "block_coinbase" (func $test (param i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + i32.const 0 + call $test + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/block_gas_limit.wat b/arbitrator/stylus/tests/timings/block_gas_limit.wat new file mode 100644 index 0000000000..2f5fc093bb --- /dev/null +++ b/arbitrator/stylus/tests/timings/block_gas_limit.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "block_gas_limit" (func $test (result i64))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + call $test + drop + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/block_number.wat b/arbitrator/stylus/tests/timings/block_number.wat new file mode 100644 index 0000000000..b16de481ac --- /dev/null +++ b/arbitrator/stylus/tests/timings/block_number.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "block_number" (func $test (result i64))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + call $test + drop + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/block_timestamp.wat b/arbitrator/stylus/tests/timings/block_timestamp.wat new file mode 100644 index 0000000000..164011bea9 --- /dev/null +++ b/arbitrator/stylus/tests/timings/block_timestamp.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "block_timestamp" (func $test (result i64))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + call $test + drop + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/chainid.wat b/arbitrator/stylus/tests/timings/chainid.wat new file mode 100644 index 0000000000..e9a4a02057 --- /dev/null +++ b/arbitrator/stylus/tests/timings/chainid.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "chainid" (func $test (result i64))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + call $test + drop + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/contract_address.wat b/arbitrator/stylus/tests/timings/contract_address.wat new file mode 100644 index 0000000000..8efd43998a --- /dev/null +++ b/arbitrator/stylus/tests/timings/contract_address.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "contract_address" (func $test (param i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + i32.const 0 + call $test + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/evm_gas_left.wat b/arbitrator/stylus/tests/timings/evm_gas_left.wat new file mode 100644 index 0000000000..c0a123752c --- /dev/null +++ b/arbitrator/stylus/tests/timings/evm_gas_left.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "evm_gas_left" (func $test (result i64))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + call $test + drop + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/evm_ink_left.wat b/arbitrator/stylus/tests/timings/evm_ink_left.wat new file mode 100644 index 0000000000..8b398b2af9 --- /dev/null +++ b/arbitrator/stylus/tests/timings/evm_ink_left.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "evm_ink_left" (func $test (result i64))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + call $test + drop + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/keccak.wat b/arbitrator/stylus/tests/timings/keccak.wat new file mode 100644 index 0000000000..7fee05caa6 --- /dev/null +++ b/arbitrator/stylus/tests/timings/keccak.wat @@ -0,0 +1,35 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "native_keccak256" (func $test (param i32 i32 i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + (call $test (i32.const 0) (local.get $args_len) (i32.const 0)) + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/msg_sender.wat b/arbitrator/stylus/tests/timings/msg_sender.wat new file mode 100644 index 0000000000..e0da7d38dd --- /dev/null +++ b/arbitrator/stylus/tests/timings/msg_sender.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "msg_sender" (func $test (param i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + i32.const 0 + call $test + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/msg_value.wat b/arbitrator/stylus/tests/timings/msg_value.wat new file mode 100644 index 0000000000..d7b73ed449 --- /dev/null +++ b/arbitrator/stylus/tests/timings/msg_value.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "msg_value" (func $test (param i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + i32.const 0 + call $test + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/null_host.wat b/arbitrator/stylus/tests/timings/null_host.wat new file mode 100644 index 0000000000..9c73ddc295 --- /dev/null +++ b/arbitrator/stylus/tests/timings/null_host.wat @@ -0,0 +1,35 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "debug" "null_host" (func $test)) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + call $test + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/read_args.wat b/arbitrator/stylus/tests/timings/read_args.wat new file mode 100644 index 0000000000..d2f76fb89e --- /dev/null +++ b/arbitrator/stylus/tests/timings/read_args.wat @@ -0,0 +1,34 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + (call $read_args (i32.const 0)) + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 1 ;; skip the first + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/return_data_size.wat b/arbitrator/stylus/tests/timings/return_data_size.wat new file mode 100644 index 0000000000..3b8f6ab664 --- /dev/null +++ b/arbitrator/stylus/tests/timings/return_data_size.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "return_data_size" (func $test (result i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + call $test + drop + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/tx_gas_price.wat b/arbitrator/stylus/tests/timings/tx_gas_price.wat new file mode 100644 index 0000000000..460823948b --- /dev/null +++ b/arbitrator/stylus/tests/timings/tx_gas_price.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "tx_gas_price" (func $test (param i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + i32.const 0 + call $test + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/tx_ink_price.wat b/arbitrator/stylus/tests/timings/tx_ink_price.wat new file mode 100644 index 0000000000..5650a8bca3 --- /dev/null +++ b/arbitrator/stylus/tests/timings/tx_ink_price.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "tx_ink_price" (func $test (result i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + call $test + drop + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/tx_origin.wat b/arbitrator/stylus/tests/timings/tx_origin.wat new file mode 100644 index 0000000000..c092b15c48 --- /dev/null +++ b/arbitrator/stylus/tests/timings/tx_origin.wat @@ -0,0 +1,36 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (import "vm_hooks" "tx_origin" (func $test (param i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + i32.const 0 + call $test + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 0 + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/stylus/tests/timings/write_result.wat b/arbitrator/stylus/tests/timings/write_result.wat new file mode 100644 index 0000000000..af2283f391 --- /dev/null +++ b/arbitrator/stylus/tests/timings/write_result.wat @@ -0,0 +1,34 @@ +;; Copyright 2023, Offchain Labs, Inc. +;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +(module + (import "vm_hooks" "read_args" (func $read_args (param i32))) + (import "vm_hooks" "write_result" (func $write_result (param i32 i32))) + (memory (export "memory") 1 1) + (func $main (export "user_entrypoint") (param $args_len i32) (result i32) + (local $i i32) + + ;; write args to 0x0 + i32.const 0 + call $read_args + + ;; treat first 4 bytes as # of iterations + (i32.load (i32.const 0)) + local.set $i + + (loop + ;; call the test function + (call $write_result (i32.const 0) (local.get $args_len)) + + ;; decrement and loop + (i32.sub (local.get $i) (i32.const 1)) + local.tee $i + i32.const 1 ;; skip the last + i32.ne + br_if 0 + ) + + ;; return success + i32.const 0 + ) +) diff --git a/arbitrator/tools/module_roots/Cargo.lock b/arbitrator/tools/module_roots/Cargo.lock new file mode 100644 index 0000000000..248a632d02 --- /dev/null +++ b/arbitrator/tools/module_roots/Cargo.lock @@ -0,0 +1,2232 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli 0.28.1", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "arbutil" +version = "0.1.0" +dependencies = [ + "digest 0.10.7", + "eyre", + "fnv", + "hex", + "num-traits", + "num_enum", + "ruint2", + "serde", + "sha2 0.10.8", + "sha3 0.10.8", + "siphasher", + "tiny-keccak", + "wasmparser", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "blst" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +dependencies = [ + "cc", + "glob", + "threadpool", + "zeroize", +] + +[[package]] +name = "brotli" +version = "0.1.0" +dependencies = [ + "lazy_static", + "num_enum", + "wasmer", + "wee_alloc", +] + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "c-kzg" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94a4bc5367b6284358d2a6a6a1dc2d92ec4b86034561c3b9d3341909752fd848" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "serde", +] + +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "corosensei" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80128832c58ea9cbd041d2a759ec449224487b2c1e400453d99d244eead87a8e" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "libc", + "scopeguard", + "windows-sys", +] + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "cranelift-bforest" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2ab4512dfd3a6f4be184403a195f76e81a8a9f9e6c898e19d2dc3ce20e0115" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98b022ed2a5913a38839dfbafe6cf135342661293b08049843362df4301261dc" +dependencies = [ + "arrayvec", + "bumpalo", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-egraph", + "cranelift-entity", + "cranelift-isle", + "gimli 0.26.2", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "639307b45434ad112a98f8300c0f0ab085cbefcd767efcdef9ef19d4c0756e74" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "278e52e29c53fcf32431ef08406c295699a70306d05a0715c5b1bf50e33a9ab7" + +[[package]] +name = "cranelift-egraph" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624b54323b06e675293939311943ba82d323bb340468ce1889be5da7932c8d73" +dependencies = [ + "cranelift-entity", + "fxhash", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "log", + "smallvec", +] + +[[package]] +name = "cranelift-entity" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a59bcbca89c3f1b70b93ab3cbba5e5e0cbf3e63dadb23c7525cb142e21a9d4c" + +[[package]] +name = "cranelift-frontend" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d70abacb8cfef3dc8ff7e8836e9c1d70f7967dfdac824a4cd5e30223415aca6" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.91.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "393bc73c451830ff8dbb3a07f61843d6cb41a084f9996319917c0b291ed785bb" + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core 0.20.8", + "darling_macro 0.20.8", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core 0.20.8", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if 1.0.0", + "hashbrown 0.14.3", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", +] + +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2 0.5.10", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "enum-iterator" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "enumset" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +dependencies = [ + "darling 0.20.8", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap 1.9.3", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown 0.14.3", +] + +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +dependencies = [ + "adler", +] + +[[package]] +name = "module_roots" +version = "0.1.0" +dependencies = [ + "arbutil", + "eyre", + "prover", + "structopt", +] + +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom-leb128" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a73b6c3a9ecfff12ad50dedba051ef838d2f478d938bb3e6b3842431028e62" +dependencies = [ + "arrayvec", + "nom", + "num-traits", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prover" +version = "0.1.0" +dependencies = [ + "arbutil", + "bincode", + "brotli", + "c-kzg", + "derivative", + "digest 0.9.0", + "eyre", + "fnv", + "hex", + "itertools", + "lazy_static", + "libc", + "lru", + "nom", + "nom-leb128", + "num", + "num-derive", + "num-traits", + "parking_lot", + "rayon", + "rustc-demangle", + "serde", + "serde_json", + "serde_with", + "sha2 0.9.9", + "sha3 0.9.1", + "smallvec", + "static_assertions", + "structopt", + "wasmer", + "wasmer-compiler-singlepass", + "wasmer-types", + "wasmparser", + "wat", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rayon" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regalloc2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300d4fbfb40c1c66a78ba3ddd41c1110247cf52f97b87d0f2fc9209bd49b030c" +dependencies = [ + "fxhash", + "log", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags 1.3.2", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ruint2" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b066b8e4fcea7fae86b6932d2449670b6b5545b7e8407841b2d3a916ff2a9f86" +dependencies = [ + "derive_more", + "ruint2-macro", + "rustc_version", + "thiserror", +] + +[[package]] +name = "ruint2-macro" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89dc553bc0cf4512a8b96caa2e21ed5f6e4b66bf28a1bd08fd9eb07c0b39b28c" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "self_cell" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba" + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-wasm-bindgen" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "shared-buffer" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6c99835bad52957e7aa241d3975ed17c1e5f8c92026377d117a606f36b84b16" +dependencies = [ + "bytes", + "memmap2 0.6.2", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "slice-group-by" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "826167069c09b99d56f31e9ae5c99049e932a98c9dc2dac47645b08dbbf76ba7" + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +dependencies = [ + "serde", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "threadpool" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +dependencies = [ + "num_cpus", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.5", + "toml_datetime", + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "uuid" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "wasm-encoder" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasmer" +version = "4.2.8" +dependencies = [ + "bytes", + "cfg-if 1.0.0", + "derivative", + "indexmap 1.9.3", + "js-sys", + "more-asserts", + "rustc-demangle", + "serde", + "serde-wasm-bindgen", + "shared-buffer", + "target-lexicon", + "thiserror", + "tracing", + "wasm-bindgen", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-derive", + "wasmer-types", + "wasmer-vm", + "wat", + "winapi", +] + +[[package]] +name = "wasmer-compiler" +version = "4.2.8" +dependencies = [ + "backtrace", + "bytes", + "cfg-if 1.0.0", + "enum-iterator", + "enumset", + "lazy_static", + "leb128", + "memmap2 0.5.10", + "more-asserts", + "region", + "rkyv", + "self_cell", + "shared-buffer", + "smallvec", + "thiserror", + "wasmer-types", + "wasmer-vm", + "wasmparser", + "winapi", +] + +[[package]] +name = "wasmer-compiler-cranelift" +version = "4.2.8" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "gimli 0.26.2", + "more-asserts", + "rayon", + "smallvec", + "target-lexicon", + "tracing", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-compiler-singlepass" +version = "4.2.8" +dependencies = [ + "byteorder", + "dynasm", + "dynasmrt", + "enumset", + "gimli 0.26.2", + "lazy_static", + "more-asserts", + "rayon", + "smallvec", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-derive" +version = "4.2.8" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "wasmer-types" +version = "4.2.8" +dependencies = [ + "bytecheck", + "enum-iterator", + "enumset", + "indexmap 1.9.3", + "more-asserts", + "rkyv", + "target-lexicon", + "thiserror", +] + +[[package]] +name = "wasmer-vm" +version = "4.2.8" +dependencies = [ + "backtrace", + "cc", + "cfg-if 1.0.0", + "corosensei", + "crossbeam-queue", + "dashmap", + "derivative", + "enum-iterator", + "fnv", + "indexmap 1.9.3", + "lazy_static", + "libc", + "mach", + "memoffset", + "more-asserts", + "region", + "scopeguard", + "thiserror", + "wasmer-types", + "winapi", +] + +[[package]] +name = "wasmparser" +version = "0.121.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" +dependencies = [ + "bitflags 2.5.0", + "indexmap 2.2.5", + "semver", +] + +[[package]] +name = "wast" +version = "64.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a259b226fd6910225aa7baeba82f9d9933b6d00f2ce1b49b80fa4214328237cc" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder", +] + +[[package]] +name = "wat" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53253d920ab413fca1c7dc2161d601c79b4fdf631d0ba51dd4343bf9b556c3f6" +dependencies = [ + "wast", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" +dependencies = [ + "windows_aarch64_msvc 0.33.0", + "windows_i686_gnu 0.33.0", + "windows_i686_msvc 0.33.0", + "windows_x86_64_gnu 0.33.0", + "windows_x86_64_msvc 0.33.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] diff --git a/arbitrator/tools/module_roots/Cargo.toml b/arbitrator/tools/module_roots/Cargo.toml new file mode 100644 index 0000000000..bb5ab16992 --- /dev/null +++ b/arbitrator/tools/module_roots/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "module_roots" +version = "0.1.0" +edition = "2021" + +[dependencies] +arbutil = { path = "../../arbutil/" } +prover = { path = "../../prover/" } +eyre = "0.6.5" +structopt = "0.3.23" + +[workspace] diff --git a/arbitrator/tools/module_roots/src/main.rs b/arbitrator/tools/module_roots/src/main.rs new file mode 100644 index 0000000000..32e4764847 --- /dev/null +++ b/arbitrator/tools/module_roots/src/main.rs @@ -0,0 +1,76 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use arbutil::Bytes32; +use eyre::{Result, WrapErr}; +use prover::{machine::GlobalState, utils::file_bytes, Machine}; +use std::{collections::HashMap, fmt::Display, path::PathBuf, sync::Arc}; +use structopt::StructOpt; + +#[derive(StructOpt)] +#[structopt(name = "module-roots")] +struct Opts { + #[structopt(long)] + binary: PathBuf, + #[structopt(long)] + stylus_modules: Vec, +} + +fn main() -> Result<()> { + let mut opts = Opts::from_args(); + + macro_rules! relocate { + ($file:expr) => { + let mut path = PathBuf::from("../../../"); + path.push(&$file); + *$file = path; + }; + } + relocate!(&mut opts.binary); + + let mut mach = Machine::from_paths( + &[], + &opts.binary, + true, + true, + true, + true, + true, + GlobalState::default(), + HashMap::default(), + Arc::new(|_, _, _| panic!("tried to read preimage")), + )?; + + let mut stylus = vec![]; + for module in &mut opts.stylus_modules { + relocate!(module); + let error = || format!("failed to read module at {}", module.to_string_lossy()); + let wasm = file_bytes(&module).wrap_err_with(error)?; + let code = &Bytes32::default(); + let hash = mach + .add_program(&wasm, code, 1, true) + .wrap_err_with(error)?; + let name = module.file_stem().unwrap().to_string_lossy(); + stylus.push((name.to_owned(), hash)); + println!("{} {}", name, hash); + } + + let mut segment = 0; + for (name, root) in stylus { + println!(" (data (i32.const 0x{:03x})", segment); + println!(" \"{}\") ;; {}", pairs(root), name); + segment += 32; + } + Ok(()) +} + +fn pairs(text: D) -> String { + let mut out = String::new(); + let text = format!("{text}"); + let mut chars = text.chars(); + while let Some(a) = chars.next() { + let b = chars.next().unwrap(); + out += &format!("\\{a}{b}") + } + out +} diff --git a/arbitrator/tools/wasmer b/arbitrator/tools/wasmer new file mode 160000 index 0000000000..6b15433d83 --- /dev/null +++ b/arbitrator/tools/wasmer @@ -0,0 +1 @@ +Subproject commit 6b15433d83f951555c24f0c56dc05e4751b0cc76 diff --git a/arbitrator/wasm-libraries/Cargo.lock b/arbitrator/wasm-libraries/Cargo.lock index b50aebb959..bea50f6592 100644 --- a/arbitrator/wasm-libraries/Cargo.lock +++ b/arbitrator/wasm-libraries/Cargo.lock @@ -2,14 +2,136 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "arbcompress" +version = "0.1.0" +dependencies = [ + "brotli", + "caller-env", + "paste", +] + [[package]] name = "arbutil" version = "0.1.0" dependencies = [ - "digest", + "digest 0.10.7", + "eyre", + "fnv", + "hex", + "num-traits", "num_enum", - "sha2", - "sha3", + "ruint2", + "serde", + "sha2 0.10.8", + "sha3 0.10.8", + "siphasher", + "tiny-keccak", + "wasmparser", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", ] [[package]] @@ -21,28 +143,113 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + [[package]] name = "brotli" version = "0.1.0" dependencies = [ - "go-abi", + "lazy_static", + "num_enum", + "wee_alloc", +] + +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "caller-env" +version = "0.1.0" +dependencies = [ + "brotli", + "num_enum", + "rand", + "rand_pcg", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + [[package]] name = "cpufeatures" -version = "0.2.9" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" dependencies = [ "libc", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "crypto-common" version = "0.1.6" @@ -54,251 +261,1349 @@ dependencies = [ ] [[package]] -name = "digest" -version = "0.10.7" +name = "darling" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "block-buffer", - "crypto-common", + "darling_core 0.13.4", + "darling_macro 0.13.4", ] [[package]] -name = "equivalent" -version = "1.0.1" +name = "darling" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core 0.20.8", + "darling_macro 0.20.8", +] [[package]] -name = "fnv" -version = "1.0.7" +name = "darling_core" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] [[package]] -name = "generic-array" -version = "0.14.7" +name = "darling_core" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ - "typenum", - "version_check", + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn 2.0.52", ] [[package]] -name = "go-abi" -version = "0.1.0" - -[[package]] -name = "go-stub" -version = "0.1.0" +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "fnv", - "go-abi", - "rand", - "rand_pcg", + "darling_core 0.13.4", + "quote", + "syn 1.0.109", ] [[package]] -name = "hashbrown" -version = "0.14.0" +name = "darling_macro" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core 0.20.8", + "quote", + "syn 2.0.52", +] [[package]] -name = "host-io" -version = "0.1.0" +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ - "arbutil", - "go-abi", + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] -name = "indexmap" -version = "2.0.0" +name = "derive_more" +version = "0.99.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ - "equivalent", - "hashbrown", + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", ] [[package]] -name = "keccak" -version = "0.1.4" +name = "digest" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "cpufeatures", + "generic-array", ] [[package]] -name = "libc" -version = "0.2.151" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", +] [[package]] -name = "memchr" -version = "2.5.0" +name = "either" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" [[package]] -name = "num_enum" +name = "enum-iterator" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70bf6736f74634d299d00086f02986875b3c2d924781a6a2cb6c201e73da0ceb" +checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" dependencies = [ - "num_enum_derive", + "enum-iterator-derive", ] [[package]] -name = "num_enum_derive" +name = "enum-iterator-derive" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ea360eafe1022f7cc56cd7b869ed57330fb2453d0c7831d99b74c65d2f5597" +checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" dependencies = [ - "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] -name = "once_cell" -version = "1.18.0" +name = "enumset" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +dependencies = [ + "enumset_derive", +] [[package]] -name = "proc-macro-crate" -version = "1.3.1" +name = "enumset_derive" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" dependencies = [ - "once_cell", - "toml_edit", + "darling 0.20.8", + "proc-macro2", + "quote", + "syn 2.0.52", ] [[package]] -name = "proc-macro2" -version = "1.0.66" +name = "equivalent" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] -name = "quote" -version = "1.0.32" +name = "eyre" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ - "proc-macro2", + "indenter", + "once_cell", ] [[package]] -name = "rand" -version = "0.8.4" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "forward" +version = "0.1.0" dependencies = [ - "rand_core", + "eyre", + "structopt", ] [[package]] -name = "rand_core" -version = "0.6.3" +name = "funty" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] -name = "rand_pcg" -version = "0.3.1" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "rand_core", + "typenum", + "version_check", ] [[package]] -name = "sha2" -version = "0.10.7" +name = "getrandom" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ - "cfg-if", - "cpufeatures", - "digest", + "cfg-if 1.0.0", + "libc", + "wasi", ] [[package]] -name = "sha3" -version = "0.10.8" +name = "hashbrown" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "digest", - "keccak", + "ahash 0.7.8", ] [[package]] -name = "syn" -version = "2.0.32" +name = "hashbrown" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "ahash 0.8.11", + "allocator-api2", ] [[package]] -name = "toml_datetime" -version = "0.6.3" +name = "heck" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] [[package]] -name = "toml_edit" -version = "0.19.14" +name = "hermit-abi" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ - "indexmap", - "toml_datetime", - "winnow", + "libc", ] [[package]] -name = "typenum" -version = "1.16.0" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "unicode-ident" -version = "1.0.11" +name = "host-io" +version = "0.1.0" +dependencies = [ + "arbutil", + "caller-env", +] + +[[package]] +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "version_check" -version = "0.9.4" +name = "indenter" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] -name = "wasi-stub" -version = "0.1.0" +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] [[package]] -name = "winnow" -version = "0.5.12" +name = "indexmap" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83817bbecf72c73bad717ee86820ebf286203d2e04c3951f3cd538869c897364" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "lru" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +dependencies = [ + "hashbrown 0.14.3", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memory_units" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom-leb128" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a73b6c3a9ecfff12ad50dedba051ef838d2f478d938bb3e6b3842431028e62" +dependencies = [ + "arrayvec", + "nom", + "num-traits", +] + +[[package]] +name = "num" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "program-exec" +version = "0.1.0" + +[[package]] +name = "prover" +version = "0.1.0" +dependencies = [ + "arbutil", + "bincode", + "brotli", + "derivative", + "digest 0.9.0", + "eyre", + "fnv", + "hex", + "itertools", + "lazy_static", + "libc", + "lru", + "nom", + "nom-leb128", + "num", + "num-derive", + "num-traits", + "once_cell", + "parking_lot", + "rustc-demangle", + "serde", + "serde_json", + "serde_with", + "sha2 0.9.9", + "sha3 0.9.1", + "smallvec", + "static_assertions", + "structopt", + "wasmer-types", + "wasmparser", + "wat", +] + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_pcg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59cad018caf63deb318e5a4586d99a24424a364f40f1e5778c29aca23f4fc73e" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cba464629b3394fc4dbc6f940ff8f5b4ff5c7aef40f29166fd4ad12acbc99c0" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "indexmap 1.9.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7dddfff8de25e6f62b9d64e6e432bf1c6736c57d20323e15ee10435fbda7c65" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ruint2" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b066b8e4fcea7fae86b6932d2449670b6b5545b7e8407841b2d3a916ff2a9f86" +dependencies = [ + "derive_more", + "ruint2-macro", + "rustc_version", + "thiserror", +] + +[[package]] +name = "ruint2-macro" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89dc553bc0cf4512a8b96caa2e21ed5f6e4b66bf28a1bd08fd9eb07c0b39b28c" + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + +[[package]] +name = "serde" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.197" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "serde_json" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" +dependencies = [ + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" +dependencies = [ + "darling 0.13.4", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "keccak", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +dependencies = [ + "serde", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.2.5", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" + +[[package]] +name = "user-host" +version = "0.1.0" +dependencies = [ + "arbutil", + "caller-env", + "eyre", + "fnv", + "hex", + "prover", + "user-host-trait", + "wasmer-types", +] + +[[package]] +name = "user-host-trait" +version = "0.1.0" +dependencies = [ + "arbutil", + "caller-env", + "eyre", + "prover", + "ruint2", +] + +[[package]] +name = "user-test" +version = "0.1.0" +dependencies = [ + "arbutil", + "caller-env", + "eyre", + "fnv", + "hex", + "lazy_static", + "parking_lot", + "prover", + "user-host-trait", +] + +[[package]] +name = "uuid" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi-stub" +version = "0.1.0" +dependencies = [ + "caller-env", + "paste", + "wee_alloc", +] + +[[package]] +name = "wasm-encoder" +version = "0.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" +dependencies = [ + "leb128", +] + +[[package]] +name = "wasmer-types" +version = "4.2.8" +dependencies = [ + "bytecheck", + "enum-iterator", + "enumset", + "indexmap 1.9.3", + "more-asserts", + "rkyv", + "target-lexicon", + "thiserror", +] + +[[package]] +name = "wasmparser" +version = "0.121.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" +dependencies = [ + "bitflags 2.5.0", + "indexmap 2.2.5", + "semver", +] + +[[package]] +name = "wast" +version = "201.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef6e1ef34d7da3e2b374fd2b1a9c0227aff6cad596e1b24df9b58d0f6222faa" +dependencies = [ + "bumpalo", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder", +] + +[[package]] +name = "wat" +version = "1.201.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453d5b37a45b98dee4f4cb68015fc73634d7883bbef1c65e6e9c78d454cf3f32" +dependencies = [ + "wast", +] + +[[package]] +name = "wee_alloc" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "memory_units", + "winapi", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.52", +] diff --git a/arbitrator/wasm-libraries/Cargo.toml b/arbitrator/wasm-libraries/Cargo.toml index 3179163a6d..837df8f4da 100644 --- a/arbitrator/wasm-libraries/Cargo.toml +++ b/arbitrator/wasm-libraries/Cargo.toml @@ -1,8 +1,12 @@ [workspace] members = [ - "brotli", + "arbcompress", "wasi-stub", - "go-stub", - "go-abi", "host-io", + "user-host", + "user-host-trait", + "user-test", + "program-exec", + "forward", ] +resolver = "2" diff --git a/arbitrator/wasm-libraries/arbcompress/Cargo.toml b/arbitrator/wasm-libraries/arbcompress/Cargo.toml new file mode 100644 index 0000000000..ec4c32c1e9 --- /dev/null +++ b/arbitrator/wasm-libraries/arbcompress/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "arbcompress" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +crate-type = ["cdylib"] + +[dependencies] +brotli.path = "../../brotli" +caller-env = { path = "../../caller-env/", features = ["static_caller"] } +paste = "1.0.14" diff --git a/arbitrator/wasm-libraries/brotli/build.rs b/arbitrator/wasm-libraries/arbcompress/build.rs similarity index 65% rename from arbitrator/wasm-libraries/brotli/build.rs rename to arbitrator/wasm-libraries/arbcompress/build.rs index 9cf73a4ecc..1c42e27f53 100644 --- a/arbitrator/wasm-libraries/brotli/build.rs +++ b/arbitrator/wasm-libraries/arbcompress/build.rs @@ -1,6 +1,10 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + fn main() { // Tell Cargo that if the given file changes, to rerun this build script. println!("cargo:rustc-link-search=../../target/lib-wasm/"); + println!("cargo:rustc-link-search=../target/lib/"); println!("cargo:rustc-link-lib=static=brotlienc-static"); println!("cargo:rustc-link-lib=static=brotlidec-static"); println!("cargo:rustc-link-lib=static=brotlicommon-static"); diff --git a/arbitrator/wasm-libraries/arbcompress/src/lib.rs b/arbitrator/wasm-libraries/arbcompress/src/lib.rs new file mode 100644 index 0000000000..fe54e667d6 --- /dev/null +++ b/arbitrator/wasm-libraries/arbcompress/src/lib.rs @@ -0,0 +1,45 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +#![allow(clippy::missing_safety_doc)] // TODO: add safety docs + +use brotli::{BrotliStatus, Dictionary}; +use caller_env::{self, GuestPtr}; +use paste::paste; + +macro_rules! wrap { + ($(fn $func_name:ident ($($arg_name:ident : $arg_type:ty),* ) -> $return_type:ty);*) => { + paste! { + $( + #[no_mangle] + pub unsafe extern "C" fn []($($arg_name : $arg_type),*) -> $return_type { + caller_env::brotli::$func_name( + &mut caller_env::static_caller::STATIC_MEM, + &mut caller_env::static_caller::STATIC_ENV, + $($arg_name),* + ) + } + )* + } + }; +} + +wrap! { + fn brotli_compress( + in_buf_ptr: GuestPtr, + in_buf_len: u32, + out_buf_ptr: GuestPtr, + out_len_ptr: GuestPtr, + level: u32, + window_size: u32, + dictionary: Dictionary + ) -> BrotliStatus; + + fn brotli_decompress( + in_buf_ptr: GuestPtr, + in_buf_len: u32, + out_buf_ptr: GuestPtr, + out_len_ptr: GuestPtr, + dictionary: Dictionary + ) -> BrotliStatus +} diff --git a/arbitrator/wasm-libraries/brotli/src/lib.rs b/arbitrator/wasm-libraries/brotli/src/lib.rs deleted file mode 100644 index 7e95d90ca7..0000000000 --- a/arbitrator/wasm-libraries/brotli/src/lib.rs +++ /dev/null @@ -1,84 +0,0 @@ -use go_abi::*; - -extern "C" { - pub fn BrotliDecoderDecompress( - encoded_size: usize, - encoded_buffer: *const u8, - decoded_size: *mut usize, - decoded_buffer: *mut u8, - ) -> u32; - - pub fn BrotliEncoderCompress( - quality: u32, - lgwin: u32, - mode: u32, - input_size: usize, - input_buffer: *const u8, - encoded_size: *mut usize, - encoded_buffer: *mut u8, - ) -> u32; -} - -const BROTLI_MODE_GENERIC: u32 = 0; -const BROTLI_RES_SUCCESS: u32 = 1; - -#[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbcompress_brotliDecompress( - sp: GoStack, -) { - //(inBuf []byte, outBuf []byte) int - let in_buf_ptr = sp.read_u64(0); - let in_buf_len = sp.read_u64(1); - let out_buf_ptr = sp.read_u64(3); - let out_buf_len = sp.read_u64(4); - const OUTPUT_ARG: usize = 6; - - let in_slice = read_slice(in_buf_ptr, in_buf_len); - let mut output = vec![0u8; out_buf_len as usize]; - let mut output_len = out_buf_len as usize; - let res = BrotliDecoderDecompress( - in_buf_len as usize, - in_slice.as_ptr(), - &mut output_len, - output.as_mut_ptr(), - ); - if (res != BROTLI_RES_SUCCESS) || (output_len as u64 > out_buf_len) { - sp.write_u64(OUTPUT_ARG, u64::MAX); - return; - } - write_slice(&output[..output_len], out_buf_ptr); - sp.write_u64(OUTPUT_ARG, output_len as u64); - return; -} - -#[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_arbcompress_brotliCompress(sp: GoStack) { - //(inBuf []byte, outBuf []byte, level int, windowSize int) int - let in_buf_ptr = sp.read_u64(0); - let in_buf_len = sp.read_u64(1); - let out_buf_ptr = sp.read_u64(3); - let out_buf_len = sp.read_u64(4); - let level = sp.read_u64(6) as u32; - let windowsize = sp.read_u64(7) as u32; - const OUTPUT_ARG: usize = 8; - - let in_slice = read_slice(in_buf_ptr, in_buf_len); - let mut output = vec![0u8; out_buf_len as usize]; - let mut output_len = out_buf_len as usize; - let res = BrotliEncoderCompress( - level, - windowsize, - BROTLI_MODE_GENERIC, - in_buf_len as usize, - in_slice.as_ptr(), - &mut output_len, - output.as_mut_ptr(), - ); - if (res != BROTLI_RES_SUCCESS) || (output_len as u64 > out_buf_len) { - sp.write_u64(OUTPUT_ARG, u64::MAX); - return; - } - write_slice(&output[..output_len], out_buf_ptr); - sp.write_u64(OUTPUT_ARG, output_len as u64); - return; -} diff --git a/arbitrator/wasm-libraries/forward/.gitignore b/arbitrator/wasm-libraries/forward/.gitignore new file mode 100644 index 0000000000..40da2042b7 --- /dev/null +++ b/arbitrator/wasm-libraries/forward/.gitignore @@ -0,0 +1 @@ +**.wat diff --git a/arbitrator/wasm-libraries/forward/Cargo.toml b/arbitrator/wasm-libraries/forward/Cargo.toml new file mode 100644 index 0000000000..73ed9d8827 --- /dev/null +++ b/arbitrator/wasm-libraries/forward/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "forward" +version = "0.1.0" +edition = "2021" + +[dependencies] +eyre = "0.6.5" +structopt = "0.3.26" diff --git a/arbitrator/wasm-libraries/forward/src/main.rs b/arbitrator/wasm-libraries/forward/src/main.rs new file mode 100644 index 0000000000..05a949e8aa --- /dev/null +++ b/arbitrator/wasm-libraries/forward/src/main.rs @@ -0,0 +1,206 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use eyre::Result; +use std::{fs::File, io::Write, path::PathBuf}; +use structopt::StructOpt; + +/// order matters! +const HOSTIOS: [[&str; 3]; 42] = [ + ["read_args", "i32", ""], + ["write_result", "i32 i32", ""], + ["exit_early", "i32", ""], + ["storage_load_bytes32", "i32 i32", ""], + ["storage_cache_bytes32", "i32 i32", ""], + ["storage_flush_cache", "i32", ""], + ["transient_load_bytes32", "i32 i32", ""], + ["transient_store_bytes32", "i32 i32", ""], + ["call_contract", "i32 i32 i32 i32 i64 i32", "i32"], + ["delegate_call_contract", "i32 i32 i32 i64 i32", "i32"], + ["static_call_contract", "i32 i32 i32 i64 i32", "i32"], + ["create1", "i32 i32 i32 i32 i32", ""], + ["create2", "i32 i32 i32 i32 i32 i32", ""], + ["read_return_data", "i32 i32 i32", "i32"], + ["return_data_size", "", "i32"], + ["emit_log", "i32 i32 i32", ""], + ["account_balance", "i32 i32", ""], + ["account_code", "i32 i32 i32 i32", "i32"], + ["account_code_size", "i32", "i32"], + ["account_codehash", "i32 i32", ""], + ["evm_gas_left", "", "i64"], + ["evm_ink_left", "", "i64"], + ["block_basefee", "i32", ""], + ["chainid", "", "i64"], + ["block_coinbase", "i32", ""], + ["block_gas_limit", "", "i64"], + ["block_number", "", "i64"], + ["block_timestamp", "", "i64"], + ["contract_address", "i32", ""], + ["math_div", "i32 i32", ""], + ["math_mod", "i32 i32", ""], + ["math_pow", "i32 i32", ""], + ["math_add_mod", "i32 i32 i32", ""], + ["math_mul_mod", "i32 i32 i32", ""], + ["msg_reentrant", "", "i32"], + ["msg_sender", "i32", ""], + ["msg_value", "i32", ""], + ["native_keccak256", "i32 i32 i32", ""], + ["tx_gas_price", "i32", ""], + ["tx_ink_price", "", "i32"], + ["tx_origin", "i32", ""], + ["pay_for_memory_grow", "i32", ""], +]; + +#[derive(StructOpt)] +#[structopt(name = "arbitrator-prover")] +struct Opts { + #[structopt(long)] + path: PathBuf, + #[structopt(long)] + stub: bool, +} + +fn main() -> Result<()> { + let opts = Opts::from_args(); + let file = &mut File::options() + .create(true) + .write(true) + .truncate(true) + .open(opts.path)?; + + match opts.stub { + true => forward_stub(file), + false => forward(file), + } +} + +fn forward(file: &mut File) -> Result<()> { + macro_rules! wln { + ($($text:tt)*) => { + writeln!(file, $($text)*)?; + }; + } + let s = " "; + + wln!( + ";; Copyright 2022-2023, Offchain Labs, Inc.\n\ + ;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE\n\ + ;; This file is auto-generated.\n\ + \n\ + (module" + ); + + macro_rules! group { + ($list:expr, $kind:expr) => { + (!$list.is_empty()) + .then(|| format!(" ({} {})", $kind, $list)) + .unwrap_or_default() + }; + } + + wln!("{s};; symbols to re-export"); + for [name, ins, outs] in HOSTIOS { + let params = group!(ins, "param"); + let result = group!(outs, "result"); + wln!( + r#"{s}(import "user_host" "arbitrator_forward__{name}" (func ${name}{params}{result}))"# + ); + } + wln!(); + + wln!("{s};; reserved offsets for future user_host imports"); + for i in HOSTIOS.len()..512 { + wln!("{s}(func $reserved_{i} unreachable)"); + } + wln!(); + + wln!( + "{s};; allows user_host to request a trap\n\ + {s}(global $trap (mut i32) (i32.const 0))\n\ + {s}(func $check\n\ + {s}{s}global.get $trap ;; see if set\n\ + {s}{s}(global.set $trap (i32.const 0)) ;; reset the flag\n\ + {s}{s}(if (then (unreachable)))\n\ + {s})\n\ + {s}(func (export \"forward__set_trap\")\n\ + {s}{s}(global.set $trap (i32.const 1))\n\ + {s})\n" + ); + + wln!("{s};; user linkage"); + for [name, ins, outs] in HOSTIOS { + let params = group!(ins, "param"); + let result = group!(outs, "result"); + wln!("{s}(func (export \"vm_hooks__{name}\"){params}{result}"); + + let gets = (1 + ins.len()) / 4; + for i in 0..gets { + wln!("{s}{s}local.get {i}"); + } + + wln!( + "{s}{s}call ${name}\n\ + {s}{s}call $check\n\ + {s})" + ); + } + + wln!(")"); + Ok(()) +} + +fn forward_stub(file: &mut File) -> Result<()> { + macro_rules! wln { + ($($text:tt)*) => { + writeln!(file, $($text)*)?; + }; + } + let s = " "; + + wln!( + ";; Copyright 2022-2023, Offchain Labs, Inc.\n\ + ;; For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE\n\ + ;; This file is auto-generated.\n\ + \n\ + (module" + ); + + macro_rules! group { + ($list:expr, $kind:expr) => { + (!$list.is_empty()) + .then(|| format!(" ({} {})", $kind, $list)) + .unwrap_or_default() + }; + } + + wln!("{s};; stubs for the symbols we re-export"); + for [name, ins, outs] in HOSTIOS { + let params = group!(ins, "param"); + let result = group!(outs, "result"); + wln!("{s}(func ${name}{params}{result} unreachable)"); + } + wln!(); + + wln!("{s};; reserved offsets for future user_host imports"); + for i in HOSTIOS.len()..512 { + wln!("{s}(func $reserved_{i} unreachable)"); + } + wln!(); + + wln!( + "{s};; allows user_host to request a trap\n\ + {s}(global $trap (mut i32) (i32.const 0))\n\ + {s}(func $check unreachable)\n\ + {s}(func (export \"forward__set_trap\") unreachable)" + ); + + wln!("{s};; user linkage"); + for [name, ins, outs] in HOSTIOS { + let params = group!(ins, "param"); + let result = group!(outs, "result"); + wln!("{s}(func (export \"vm_hooks__{name}\"){params}{result} unreachable)"); + } + + wln!(")"); + Ok(()) +} diff --git a/arbitrator/wasm-libraries/go-abi/Cargo.toml b/arbitrator/wasm-libraries/go-abi/Cargo.toml deleted file mode 100644 index 36dc35c82b..0000000000 --- a/arbitrator/wasm-libraries/go-abi/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[package] -name = "go-abi" -version = "0.1.0" -edition = "2018" -publish = false - -[dependencies] diff --git a/arbitrator/wasm-libraries/go-abi/src/lib.rs b/arbitrator/wasm-libraries/go-abi/src/lib.rs deleted file mode 100644 index b6bcc45a61..0000000000 --- a/arbitrator/wasm-libraries/go-abi/src/lib.rs +++ /dev/null @@ -1,92 +0,0 @@ -use std::convert::TryFrom; - -extern "C" { - pub fn wavm_caller_load8(ptr: usize) -> u8; - pub fn wavm_caller_load32(ptr: usize) -> u32; - pub fn wavm_caller_store8(ptr: usize, val: u8); - pub fn wavm_caller_store32(ptr: usize, val: u32); - - pub fn wavm_guest_call__getsp() -> usize; - pub fn wavm_guest_call__resume(); -} - -pub unsafe fn wavm_caller_load64(ptr: usize) -> u64 { - let lower = wavm_caller_load32(ptr); - let upper = wavm_caller_load32(ptr + 4); - lower as u64 | ((upper as u64) << 32) -} - -pub unsafe fn wavm_caller_store64(ptr: usize, val: u64) { - wavm_caller_store32(ptr, val as u32); - wavm_caller_store32(ptr + 4, (val >> 32) as u32); -} - -#[derive(Clone, Copy)] -#[repr(transparent)] -pub struct GoStack(pub usize); - -impl GoStack { - fn offset(&self, arg: usize) -> usize { - self.0 + (arg + 1) * 8 - } - - pub unsafe fn read_u8(self, arg: usize) -> u8 { - wavm_caller_load8(self.offset(arg)) - } - - pub unsafe fn read_u32(self, arg: usize) -> u32 { - wavm_caller_load32(self.offset(arg)) - } - - pub unsafe fn read_u64(self, arg: usize) -> u64 { - wavm_caller_load64(self.offset(arg)) - } - - pub unsafe fn write_u8(self, arg: usize, x: u8) { - wavm_caller_store8(self.offset(arg), x); - } - - pub unsafe fn write_u32(self, arg: usize, x: u32) { - wavm_caller_store32(self.offset(arg), x); - } - - pub unsafe fn write_u64(self, arg: usize, x: u64) { - wavm_caller_store64(self.offset(arg), x); - } -} - -pub unsafe fn read_slice(ptr: u64, mut len: u64) -> Vec { - let mut data = Vec::with_capacity(len as usize); - if len == 0 { - return data; - } - let mut ptr = usize::try_from(ptr).expect("Go pointer didn't fit in usize"); - while len >= 4 { - data.extend(wavm_caller_load32(ptr).to_le_bytes()); - ptr += 4; - len -= 4; - } - for _ in 0..len { - data.push(wavm_caller_load8(ptr)); - ptr += 1; - } - data -} - -pub unsafe fn write_slice(mut src: &[u8], ptr: u64) { - if src.len() == 0 { - return; - } - let mut ptr = usize::try_from(ptr).expect("Go pointer didn't fit in usize"); - while src.len() >= 4 { - let mut arr = [0u8; 4]; - arr.copy_from_slice(&src[..4]); - wavm_caller_store32(ptr, u32::from_le_bytes(arr)); - ptr += 4; - src = &src[4..]; - } - for &byte in src { - wavm_caller_store8(ptr, byte); - ptr += 1; - } -} diff --git a/arbitrator/wasm-libraries/go-stub/Cargo.toml b/arbitrator/wasm-libraries/go-stub/Cargo.toml deleted file mode 100644 index 9398b2b44a..0000000000 --- a/arbitrator/wasm-libraries/go-stub/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "go-stub" -version = "0.1.0" -edition = "2018" -publish = false - -[lib] -crate-type = ["cdylib"] - -[dependencies] -fnv = "1.0.7" -rand = { version = "0.8.4", default-features = false } -rand_pcg = { version = "0.3.1", default-features = false } -go-abi = { path = "../go-abi" } - -[features] diff --git a/arbitrator/wasm-libraries/go-stub/src/lib.rs b/arbitrator/wasm-libraries/go-stub/src/lib.rs deleted file mode 100644 index 1a5d1963c7..0000000000 --- a/arbitrator/wasm-libraries/go-stub/src/lib.rs +++ /dev/null @@ -1,598 +0,0 @@ -mod value; - -use crate::value::*; -use fnv::FnvHashSet as HashSet; -use go_abi::*; -use rand::RngCore; -use rand_pcg::Pcg32; -use std::{collections::BinaryHeap, convert::TryFrom, io::Write}; - -fn interpret_value(repr: u64) -> InterpValue { - if repr == 0 { - return InterpValue::Undefined; - } - let float = f64::from_bits(repr); - if float.is_nan() && repr != f64::NAN.to_bits() { - let id = repr as u32; - if id == ZERO_ID { - return InterpValue::Number(0.); - } - return InterpValue::Ref(id); - } - InterpValue::Number(float) -} - -unsafe fn read_value_slice(mut ptr: u64, len: u64) -> Vec { - let mut values = Vec::new(); - for _ in 0..len { - let p = usize::try_from(ptr).expect("Go pointer didn't fit in usize"); - values.push(interpret_value(wavm_caller_load64(p))); - ptr += 8; - } - values -} - -#[no_mangle] -pub unsafe extern "C" fn go__debug(x: usize) { - println!("go debug: {}", x); -} - -#[no_mangle] -pub unsafe extern "C" fn go__runtime_resetMemoryDataView(_: GoStack) {} - -#[no_mangle] -pub unsafe extern "C" fn go__runtime_wasmExit(sp: GoStack) { - std::process::exit(sp.read_u32(0) as i32); -} - -#[no_mangle] -pub unsafe extern "C" fn go__runtime_wasmWrite(sp: GoStack) { - let fd = sp.read_u64(0); - let ptr = sp.read_u64(1); - let len = sp.read_u32(2); - let buf = read_slice(ptr, len.into()); - if fd == 2 { - let stderr = std::io::stderr(); - let mut stderr = stderr.lock(); - stderr.write_all(&buf).unwrap(); - } else { - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - stdout.write_all(&buf).unwrap(); - } -} - -// An increasing clock used when Go asks for time, measured in nanoseconds. -static mut TIME: u64 = 0; -// The amount of TIME advanced each check. Currently 10 milliseconds. -static mut TIME_INTERVAL: u64 = 10_000_000; - -#[no_mangle] -pub unsafe extern "C" fn go__runtime_nanotime1(sp: GoStack) { - TIME += TIME_INTERVAL; - sp.write_u64(0, TIME); -} - -#[no_mangle] -pub unsafe extern "C" fn go__runtime_walltime(sp: GoStack) { - TIME += TIME_INTERVAL; - sp.write_u64(0, TIME / 1_000_000_000); - sp.write_u32(1, (TIME % 1_000_000_000) as u32); -} - -#[no_mangle] -pub unsafe extern "C" fn go__runtime_walltime1(sp: GoStack) { - TIME += TIME_INTERVAL; - sp.write_u64(0, TIME / 1_000_000_000); - sp.write_u64(1, TIME % 1_000_000_000); -} - -static mut RNG: Option = None; - -unsafe fn get_rng<'a>() -> &'a mut Pcg32 { - RNG.get_or_insert_with(|| Pcg32::new(0xcafef00dd15ea5e5, 0xa02bdbf7bb3c0a7)) -} - -#[no_mangle] -pub unsafe extern "C" fn go__runtime_getRandomData(sp: GoStack) { - let rng = get_rng(); - let mut ptr = - usize::try_from(sp.read_u64(0)).expect("Go getRandomData pointer didn't fit in usize"); - let mut len = sp.read_u64(1); - while len >= 4 { - wavm_caller_store32(ptr, rng.next_u32()); - ptr += 4; - len -= 4; - } - if len > 0 { - let mut rem = rng.next_u32(); - for _ in 0..len { - wavm_caller_store8(ptr, rem as u8); - ptr += 1; - rem >>= 8; - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -struct TimeoutInfo { - time: u64, - id: u32, -} - -impl Ord for TimeoutInfo { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - other - .time - .cmp(&self.time) - .then_with(|| other.id.cmp(&self.id)) - } -} - -impl PartialOrd for TimeoutInfo { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(&other)) - } -} - -#[derive(Default, Debug)] -struct TimeoutState { - /// Contains tuples of (time, id) - times: BinaryHeap, - pending_ids: HashSet, - next_id: u32, -} - -static mut TIMEOUT_STATE: Option = None; - -#[no_mangle] -pub unsafe extern "C" fn go__runtime_scheduleTimeoutEvent(sp: GoStack) { - let mut time = sp.read_u64(0); - time = time.saturating_mul(1_000_000); // milliseconds to nanoseconds - time = time.saturating_add(TIME); // add the current time to the delay - - let state = TIMEOUT_STATE.get_or_insert_with(Default::default); - let id = state.next_id; - state.next_id += 1; - state.times.push(TimeoutInfo { time, id }); - state.pending_ids.insert(id); - - sp.write_u32(1, id); -} - -#[no_mangle] -pub unsafe extern "C" fn go__runtime_clearTimeoutEvent(sp: GoStack) { - let id = sp.read_u32(0); - - let state = TIMEOUT_STATE.get_or_insert_with(Default::default); - if !state.pending_ids.remove(&id) { - eprintln!("Go attempting to clear not pending timeout event {}", id); - } -} - -macro_rules! unimpl_js { - ($($f:ident),* $(,)?) => { - $( - #[no_mangle] - pub unsafe extern "C" fn $f(_: GoStack) { - unimplemented!("Go JS interface {} not supported", stringify!($f)); - } - )* - } -} - -unimpl_js!( - go__syscall_js_stringVal, - go__syscall_js_valueSetIndex, - go__syscall_js_valuePrepareString, - go__syscall_js_valueLoadString, - go__syscall_js_valueDelete, - go__syscall_js_valueInvoke, - go__syscall_js_valueInstanceOf, -); - -#[no_mangle] -pub unsafe extern "C" fn go__syscall_js_valueGet(sp: GoStack) { - let source = interpret_value(sp.read_u64(0)); - let field_ptr = sp.read_u64(1); - let field_len = sp.read_u64(2); - let field = read_slice(field_ptr, field_len); - let value = match source { - InterpValue::Ref(id) => get_field(id, &field), - val => { - eprintln!( - "Go attempting to read field {:?} . {}", - val, - String::from_utf8_lossy(&field), - ); - GoValue::Null - } - }; - sp.write_u64(3, value.encode()); -} - -#[no_mangle] -pub unsafe extern "C" fn go__syscall_js_valueNew(sp: GoStack) { - let class = sp.read_u32(0); - let args_ptr = sp.read_u64(1); - let args_len = sp.read_u64(2); - let args = read_value_slice(args_ptr, args_len); - if class == UINT8_ARRAY_ID { - if let Some(InterpValue::Number(size)) = args.first() { - let id = DynamicObjectPool::singleton() - .insert(DynamicObject::Uint8Array(vec![0; *size as usize])); - sp.write_u64(4, GoValue::Object(id).encode()); - sp.write_u8(5, 1); - return; - } else { - eprintln!( - "Go attempted to construct Uint8Array with bad args: {:?}", - args, - ); - } - } else if class == DATE_ID { - let id = DynamicObjectPool::singleton().insert(DynamicObject::Date); - sp.write_u64(4, GoValue::Object(id).encode()); - sp.write_u8(5, 1); - return; - } else { - eprintln!( - "Go attempting to construct unimplemented JS value {}", - class, - ); - } - sp.write_u64(4, GoValue::Null.encode()); - sp.write_u8(5, 0); -} - -#[no_mangle] -pub unsafe extern "C" fn go__syscall_js_copyBytesToJS(sp: GoStack) { - let dest_val = interpret_value(sp.read_u64(0)); - if let InterpValue::Ref(dest_id) = dest_val { - let src_ptr = sp.read_u64(1); - let src_len = sp.read_u64(2); - let dest = DynamicObjectPool::singleton().get_mut(dest_id); - if let Some(DynamicObject::Uint8Array(buf)) = dest { - if buf.len() as u64 != src_len { - eprintln!( - "Go copying bytes from Go source length {} to JS dest length {}", - src_len, - buf.len(), - ); - } - let len = std::cmp::min(src_len, buf.len() as u64) as usize; - // Slightly inefficient as this allocates a new temporary buffer - buf[..len].copy_from_slice(&read_slice(src_ptr, len as u64)); - sp.write_u64(4, GoValue::Number(len as f64).encode()); - sp.write_u8(5, 1); - return; - } else { - eprintln!( - "Go attempting to copy bytes into unsupported target {:?}", - dest, - ); - } - } else { - eprintln!("Go attempting to copy bytes into {:?}", dest_val); - } - sp.write_u64(4, GoValue::Null.encode()); - sp.write_u8(5, 0); -} - -#[no_mangle] -pub unsafe extern "C" fn go__syscall_js_copyBytesToGo(sp: GoStack) { - let dest_ptr = sp.read_u64(0); - let dest_len = sp.read_u64(1); - let src_val = interpret_value(sp.read_u64(3)); - if let InterpValue::Ref(src_id) = src_val { - let source = DynamicObjectPool::singleton().get_mut(src_id); - if let Some(DynamicObject::Uint8Array(buf)) = source { - if buf.len() as u64 != dest_len { - eprintln!( - "Go copying bytes from JS source length {} to Go dest length {}", - buf.len(), - dest_len, - ); - } - let len = std::cmp::min(buf.len() as u64, dest_len) as usize; - write_slice(&buf[..len], dest_ptr); - - sp.write_u64(4, GoValue::Number(len as f64).encode()); - sp.write_u8(5, 1); - return; - } else { - eprintln!( - "Go attempting to copy bytes from unsupported source {:?}", - source, - ); - } - } else { - eprintln!("Go attempting to copy bytes from {:?}", src_val); - } - sp.write_u8(5, 0); -} - -unsafe fn value_call_impl(sp: &mut GoStack) -> Result { - let object = interpret_value(sp.read_u64(0)); - let method_name_ptr = sp.read_u64(1); - let method_name_len = sp.read_u64(2); - let method_name = read_slice(method_name_ptr, method_name_len); - let args_ptr = sp.read_u64(3); - let args_len = sp.read_u64(4); - let args = read_value_slice(args_ptr, args_len); - if object == InterpValue::Ref(GO_ID) && &method_name == b"_makeFuncWrapper" { - let id = args.first().ok_or_else(|| { - format!( - "Go attempting to call Go._makeFuncWrapper with bad args {:?}", - args, - ) - })?; - let ref_id = - DynamicObjectPool::singleton().insert(DynamicObject::FunctionWrapper(*id, object)); - Ok(GoValue::Function(ref_id)) - } else if object == InterpValue::Ref(FS_ID) && &method_name == b"write" { - let args_len = std::cmp::min(6, args.len()); - if let &[InterpValue::Number(fd), InterpValue::Ref(buf_id), InterpValue::Number(offset), InterpValue::Number(length), InterpValue::Ref(NULL_ID), InterpValue::Ref(callback_id)] = - &args.as_slice()[..args_len] - { - let object_pool = DynamicObjectPool::singleton(); - let buf = match object_pool.get(buf_id) { - Some(DynamicObject::Uint8Array(x)) => x, - x => { - return Err(format!( - "Go attempting to call fs.write with bad buffer {:?}", - x, - )) - } - }; - let (func_id, this) = match object_pool.get(callback_id) { - Some(DynamicObject::FunctionWrapper(f, t)) => (f, t), - x => { - return Err(format!( - "Go attempting to call fs.write with bad buffer {:?}", - x, - )) - } - }; - let mut offset = offset as usize; - let mut length = length as usize; - if offset > buf.len() { - eprintln!( - "Go attempting to call fs.write with offset {} >= buf.len() {}", - offset, - buf.len(), - ); - offset = buf.len(); - } - if offset + length > buf.len() { - eprintln!( - "Go attempting to call fs.write with offset {} + length {} >= buf.len() {}", - offset, - length, - buf.len(), - ); - length = buf.len() - offset; - } - - if fd == 1. { - let stdout = std::io::stdout(); - let mut stdout = stdout.lock(); - stdout.write_all(&buf[offset..(offset + length)]).unwrap(); - } else if fd == 2. { - let stderr = std::io::stderr(); - let mut stderr = stderr.lock(); - stderr.write_all(&buf[offset..(offset + length)]).unwrap(); - } else { - eprintln!("Go attempting to write to unknown FD {}", fd); - } - - PENDING_EVENT = Some(PendingEvent { - id: *func_id, - this: *this, - args: vec![ - GoValue::Null, // no error - GoValue::Number(length as f64), // amount written - ], - }); - wavm_guest_call__resume(); - - *sp = GoStack(wavm_guest_call__getsp()); - Ok(GoValue::Null) - } else { - Err(format!( - "Go attempting to call fs.write with bad args {:?}", - args - )) - } - } else if object == InterpValue::Ref(CRYPTO_ID) && &method_name == b"getRandomValues" { - let id = match args.first() { - Some(InterpValue::Ref(x)) => *x, - _ => { - return Err(format!( - "Go attempting to call crypto.getRandomValues with bad args {:?}", - args, - )); - } - }; - match DynamicObjectPool::singleton().get_mut(id) { - Some(DynamicObject::Uint8Array(buf)) => { - get_rng().fill_bytes(buf.as_mut_slice()); - } - Some(x) => { - return Err(format!( - "Go attempting to call crypto.getRandomValues on bad object {:?}", - x, - )); - } - None => { - return Err(format!( - "Go attempting to call crypto.getRandomValues on unknown reference {}", - id, - )); - } - } - Ok(GoValue::Undefined) - } else if let InterpValue::Ref(obj_id) = object { - let val = DynamicObjectPool::singleton().get(obj_id); - if let Some(DynamicObject::Date) = val { - if &method_name == b"getTimezoneOffset" { - return Ok(GoValue::Number(0.0)); - } else { - return Err(format!( - "Go attempting to call unknown method {} for date object", - String::from_utf8_lossy(&method_name), - )); - } - } else { - return Err(format!( - "Go attempting to call method {} for unknown object - id {}", - String::from_utf8_lossy(&method_name), - obj_id, - )); - } - } else { - Err(format!( - "Go attempting to call unknown method {:?} . {}", - object, - String::from_utf8_lossy(&method_name), - )) - } -} - -#[no_mangle] -pub unsafe extern "C" fn go__syscall_js_valueCall(mut sp: GoStack) { - match value_call_impl(&mut sp) { - Ok(val) => { - sp.write_u64(6, val.encode()); - sp.write_u8(7, 1); - } - Err(err) => { - eprintln!("{}", err); - sp.write_u64(6, GoValue::Null.encode()); - sp.write_u8(7, 0); - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn go__syscall_js_valueSet(sp: GoStack) { - let source = interpret_value(sp.read_u64(0)); - let field_ptr = sp.read_u64(1); - let field_len = sp.read_u64(2); - let new_value = interpret_value(sp.read_u64(3)); - let field = read_slice(field_ptr, field_len); - if source == InterpValue::Ref(GO_ID) - && &field == b"_pendingEvent" - && new_value == InterpValue::Ref(NULL_ID) - { - PENDING_EVENT = None; - return; - } - let pool = DynamicObjectPool::singleton(); - if let InterpValue::Ref(id) = source { - let source = pool.get(id); - if let Some(DynamicObject::PendingEvent(_)) = source { - if field == b"result" { - return; - } - } - } - eprintln!( - "Go attempted to set unsupported value {:?} field {} to {:?}", - source, - String::from_utf8_lossy(&field), - new_value, - ); -} - -#[no_mangle] -pub unsafe extern "C" fn go__syscall_js_valueLength(sp: GoStack) { - let source = interpret_value(sp.read_u64(0)); - let pool = DynamicObjectPool::singleton(); - let source = match source { - InterpValue::Ref(x) => pool.get(x), - _ => None, - }; - let len = match source { - Some(DynamicObject::Uint8Array(x)) => Some(x.len()), - Some(DynamicObject::ValueArray(x)) => Some(x.len()), - _ => None, - }; - if let Some(len) = len { - sp.write_u64(1, len as u64); - } else { - eprintln!( - "Go attempted to get length of unsupported value {:?}", - source, - ); - sp.write_u64(1, 0); - } -} - -unsafe fn value_index_impl(sp: GoStack) -> Result { - let pool = DynamicObjectPool::singleton(); - let source = match interpret_value(sp.read_u64(0)) { - InterpValue::Ref(x) => pool.get(x), - val => return Err(format!("Go attempted to index into {:?}", val)), - }; - let index = usize::try_from(sp.read_u64(1)).map_err(|e| format!("{:?}", e))?; - let val = match source { - Some(DynamicObject::Uint8Array(x)) => { - Some(x.get(index).map(|x| GoValue::Number(*x as f64))) - } - Some(DynamicObject::ValueArray(x)) => Some(x.get(index).cloned()), - _ => None, - }; - match val { - Some(Some(val)) => Ok(val), - Some(None) => Err(format!( - "Go attempted to index out of bounds into value {:?} index {}", - source, index, - )), - None => Err(format!( - "Go attempted to index into unsupported value {:?}", - source - )), - } -} - -#[no_mangle] -pub unsafe extern "C" fn go__syscall_js_valueIndex(sp: GoStack) { - match value_index_impl(sp) { - Ok(v) => sp.write_u64(2, v.encode()), - Err(e) => { - eprintln!("{}", e); - sp.write_u64(2, GoValue::Null.encode()); - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn go__syscall_js_finalizeRef(sp: GoStack) { - let val = interpret_value(sp.read_u64(0)); - match val { - InterpValue::Ref(x) if x < DYNAMIC_OBJECT_ID_BASE => {} - InterpValue::Ref(x) => { - if DynamicObjectPool::singleton().remove(x).is_none() { - eprintln!("Go attempting to finalize unknown ref {}", x); - } - } - val => eprintln!("Go attempting to finalize {:?}", val), - } -} - -#[no_mangle] -pub unsafe extern "C" fn wavm__go_after_run() { - let mut state = TIMEOUT_STATE.get_or_insert_with(Default::default); - while let Some(info) = state.times.pop() { - while state.pending_ids.contains(&info.id) { - TIME = std::cmp::max(TIME, info.time); - // Important: the current reference to state shouldn't be used after this resume call, - // as it might during the resume call the reference might be invalidated. - // That's why immediately after this resume call, we replace the reference - // with a new reference to TIMEOUT_STATE. - wavm_guest_call__resume(); - state = TIMEOUT_STATE.get_or_insert_with(Default::default); - } - } -} diff --git a/arbitrator/wasm-libraries/go-stub/src/value.rs b/arbitrator/wasm-libraries/go-stub/src/value.rs deleted file mode 100644 index 3a015bbf70..0000000000 --- a/arbitrator/wasm-libraries/go-stub/src/value.rs +++ /dev/null @@ -1,204 +0,0 @@ -use fnv::FnvHashMap as HashMap; - -pub const ZERO_ID: u32 = 1; -pub const NULL_ID: u32 = 2; -pub const GLOBAL_ID: u32 = 5; -pub const GO_ID: u32 = 6; - -pub const OBJECT_ID: u32 = 100; -pub const ARRAY_ID: u32 = 101; -pub const PROCESS_ID: u32 = 102; -pub const FS_ID: u32 = 103; -pub const UINT8_ARRAY_ID: u32 = 104; -pub const CRYPTO_ID: u32 = 105; -pub const DATE_ID: u32 = 106; - -pub const FS_CONSTANTS_ID: u32 = 200; - -pub const DYNAMIC_OBJECT_ID_BASE: u32 = 10000; - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum InterpValue { - Undefined, - Number(f64), - Ref(u32), -} - -impl InterpValue { - pub fn assume_num_or_object(self) -> GoValue { - match self { - InterpValue::Undefined => GoValue::Undefined, - InterpValue::Number(x) => GoValue::Number(x), - InterpValue::Ref(x) => GoValue::Object(x), - } - } -} - -#[derive(Clone, Copy, Debug)] -#[allow(dead_code)] -pub enum GoValue { - Undefined, - Number(f64), - Null, - Object(u32), - String(u32), - Symbol(u32), - Function(u32), -} - -impl GoValue { - pub fn encode(self) -> u64 { - let (ty, id): (u32, u32) = match self { - GoValue::Undefined => return 0, - GoValue::Number(mut f) => { - // Canonicalize NaNs so they don't collide with other value types - if f.is_nan() { - f = f64::NAN; - } - if f == 0. { - // Zeroes are encoded differently for some reason - (0, ZERO_ID) - } else { - return f.to_bits(); - } - } - GoValue::Null => (0, NULL_ID), - GoValue::Object(x) => (1, x), - GoValue::String(x) => (2, x), - GoValue::Symbol(x) => (3, x), - GoValue::Function(x) => (4, x), - }; - // Must not be all zeroes, otherwise it'd collide with a real NaN - assert!(ty != 0 || id != 0, "GoValue must not be empty"); - f64::NAN.to_bits() | (u64::from(ty) << 32) | u64::from(id) - } -} - -#[derive(Clone, Debug)] -pub struct PendingEvent { - pub id: InterpValue, - pub this: InterpValue, - pub args: Vec, -} - -#[derive(Debug, Clone)] -pub enum DynamicObject { - Uint8Array(Vec), - FunctionWrapper(InterpValue, InterpValue), - PendingEvent(PendingEvent), - ValueArray(Vec), - Date, -} - -#[derive(Default, Debug)] -pub struct DynamicObjectPool { - objects: HashMap, - free_ids: Vec, -} - -static mut DYNAMIC_OBJECT_POOL: Option = None; - -impl DynamicObjectPool { - pub unsafe fn singleton<'a>() -> &'a mut Self { - DYNAMIC_OBJECT_POOL.get_or_insert_with(Default::default) - } - - pub fn insert(&mut self, object: DynamicObject) -> u32 { - let id = self - .free_ids - .pop() - .unwrap_or_else(|| DYNAMIC_OBJECT_ID_BASE + self.objects.len() as u32); - self.objects.insert(id, object); - id - } - - pub fn get(&self, id: u32) -> Option<&DynamicObject> { - self.objects.get(&id) - } - - pub fn get_mut(&mut self, id: u32) -> Option<&mut DynamicObject> { - self.objects.get_mut(&id) - } - - pub fn remove(&mut self, id: u32) -> Option { - let res = self.objects.remove(&id); - if res.is_some() { - self.free_ids.push(id); - } - res - } -} - -pub static mut PENDING_EVENT: Option = None; - -pub unsafe fn get_field(source: u32, field: &[u8]) -> GoValue { - if source == GLOBAL_ID { - if field == b"Object" { - return GoValue::Function(OBJECT_ID); - } else if field == b"Array" { - return GoValue::Function(ARRAY_ID); - } else if field == b"process" { - return GoValue::Object(PROCESS_ID); - } else if field == b"fs" { - return GoValue::Object(FS_ID); - } else if field == b"Uint8Array" { - return GoValue::Function(UINT8_ARRAY_ID); - } else if field == b"crypto" { - return GoValue::Object(CRYPTO_ID); - } else if field == b"Date" { - return GoValue::Object(DATE_ID); - } else if field == b"fetch" { - // Triggers a code path in Go for a fake network implementation - return GoValue::Undefined; - } - } else if source == FS_ID { - if field == b"constants" { - return GoValue::Object(FS_CONSTANTS_ID); - } - } else if source == FS_CONSTANTS_ID { - if matches!( - field, - b"O_WRONLY" | b"O_RDWR" | b"O_CREAT" | b"O_TRUNC" | b"O_APPEND" | b"O_EXCL" - ) { - return GoValue::Number(-1.); - } - } else if source == GO_ID { - if field == b"_pendingEvent" { - if let Some(event) = &PENDING_EVENT { - let id = DynamicObjectPool::singleton() - .insert(DynamicObject::PendingEvent(event.clone())); - return GoValue::Object(id); - } else { - return GoValue::Null; - } - } - } - - if let Some(source) = DynamicObjectPool::singleton().get(source).cloned() { - if let DynamicObject::PendingEvent(event) = &source { - if field == b"id" { - return event.id.assume_num_or_object(); - } else if field == b"this" { - return event.this.assume_num_or_object(); - } else if field == b"args" { - let id = DynamicObjectPool::singleton() - .insert(DynamicObject::ValueArray(event.args.clone())); - return GoValue::Object(id); - } - } - - eprintln!( - "Go attempting to access unimplemented unknown JS value {:?} field {}", - source, - String::from_utf8_lossy(field), - ); - GoValue::Undefined - } else { - eprintln!( - "Go attempting to access unimplemented unknown JS value {} field {}", - source, - String::from_utf8_lossy(field), - ); - GoValue::Undefined - } -} diff --git a/arbitrator/wasm-libraries/host-io/Cargo.toml b/arbitrator/wasm-libraries/host-io/Cargo.toml index 48f498f910..03803400c5 100644 --- a/arbitrator/wasm-libraries/host-io/Cargo.toml +++ b/arbitrator/wasm-libraries/host-io/Cargo.toml @@ -8,5 +8,5 @@ publish = false crate-type = ["cdylib"] [dependencies] -go-abi = { path = "../go-abi" } -arbutil = { path = "../../arbutil" } +arbutil = { path = "../../arbutil/" } +caller-env = { path = "../../caller-env/", default-features = false, features = ["static_caller"] } diff --git a/arbitrator/wasm-libraries/host-io/src/lib.rs b/arbitrator/wasm-libraries/host-io/src/lib.rs index 733d143354..d61cf1a977 100644 --- a/arbitrator/wasm-libraries/host-io/src/lib.rs +++ b/arbitrator/wasm-libraries/host-io/src/lib.rs @@ -1,6 +1,12 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +#![allow(clippy::missing_safety_doc)] // TODO: add safety docs + use arbutil::PreimageType; -use go_abi::*; -use std::convert::TryInto; +use caller_env::{static_caller::STATIC_MEM, GuestPtr, MemAccess}; +use core::convert::TryInto; +use core::ops::{Deref, DerefMut, Index, RangeTo}; extern "C" { pub fn wavm_get_globalstate_bytes32(idx: u32, ptr: *mut u8); @@ -17,144 +23,123 @@ extern "C" { #[repr(C, align(256))] struct MemoryLeaf([u8; 32]); -#[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_getGlobalStateBytes32( - sp: GoStack, -) { - let idx = sp.read_u64(0) as u32; - let out_ptr = sp.read_u64(1); - let mut out_len = sp.read_u64(2); - if out_len < 32 { - eprintln!( - "Go attempting to read block hash into {} bytes long buffer", - out_len, - ); - } else { - out_len = 32; +impl Deref for MemoryLeaf { + type Target = [u8; 32]; + + fn deref(&self) -> &[u8; 32] { + &self.0 + } +} + +impl DerefMut for MemoryLeaf { + fn deref_mut(&mut self) -> &mut [u8; 32] { + &mut self.0 } +} + +impl Index> for MemoryLeaf { + type Output = [u8]; + + fn index(&self, index: RangeTo) -> &[u8] { + &self.0[index] + } +} + +#[no_mangle] +pub unsafe extern "C" fn wavmio__getGlobalStateBytes32(idx: u32, out_ptr: GuestPtr) { let mut our_buf = MemoryLeaf([0u8; 32]); - let our_ptr = our_buf.0.as_mut_ptr(); + let our_ptr = our_buf.as_mut_ptr(); assert_eq!(our_ptr as usize % 32, 0); wavm_get_globalstate_bytes32(idx, our_ptr); - write_slice(&our_buf.0[..(out_len as usize)], out_ptr); + STATIC_MEM.write_slice(out_ptr, &our_buf[..32]); } +/// Writes 32-bytes of global state #[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_setGlobalStateBytes32( - sp: GoStack, -) { - let idx = sp.read_u64(0) as u32; - let src_ptr = sp.read_u64(1); - let src_len = sp.read_u64(2); - if src_len != 32 { - eprintln!( - "Go attempting to set block hash from {} bytes long buffer", - src_len, - ); - return; - } +pub unsafe extern "C" fn wavmio__setGlobalStateBytes32(idx: u32, src_ptr: GuestPtr) { let mut our_buf = MemoryLeaf([0u8; 32]); - our_buf.0.copy_from_slice(&read_slice(src_ptr, src_len)); - let our_ptr = our_buf.0.as_ptr(); + let value = STATIC_MEM.read_slice(src_ptr, 32); + our_buf.copy_from_slice(&value); + + let our_ptr = our_buf.as_ptr(); assert_eq!(our_ptr as usize % 32, 0); wavm_set_globalstate_bytes32(idx, our_ptr); } +/// Reads 8-bytes of global state #[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_getGlobalStateU64(sp: GoStack) { - let idx = sp.read_u64(0) as u32; - sp.write_u64(1, wavm_get_globalstate_u64(idx)); +pub unsafe extern "C" fn wavmio__getGlobalStateU64(idx: u32) -> u64 { + wavm_get_globalstate_u64(idx) } +/// Writes 8-bytes of global state #[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_setGlobalStateU64(sp: GoStack) { - let idx = sp.read_u64(0) as u32; - wavm_set_globalstate_u64(idx, sp.read_u64(1)); +pub unsafe extern "C" fn wavmio__setGlobalStateU64(idx: u32, val: u64) { + wavm_set_globalstate_u64(idx, val); } +/// Reads an inbox message #[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_readInboxMessage(sp: GoStack) { - let msg_num = sp.read_u64(0); - let offset = sp.read_u64(1); - let out_ptr = sp.read_u64(2); - let out_len = sp.read_u64(3); - if out_len != 32 { - eprintln!( - "Go attempting to read inbox message with out len {}", - out_len, - ); - sp.write_u64(5, 0); - return; - } +pub unsafe extern "C" fn wavmio__readInboxMessage( + msg_num: u64, + offset: usize, + out_ptr: GuestPtr, +) -> usize { let mut our_buf = MemoryLeaf([0u8; 32]); - let our_ptr = our_buf.0.as_mut_ptr(); + let our_ptr = our_buf.as_mut_ptr(); assert_eq!(our_ptr as usize % 32, 0); - let read = wavm_read_inbox_message(msg_num, our_ptr, offset as usize); + + let read = wavm_read_inbox_message(msg_num, our_ptr, offset); assert!(read <= 32); - write_slice(&our_buf.0[..read], out_ptr); - sp.write_u64(5, read as u64); + STATIC_MEM.write_slice(out_ptr, &our_buf[..read]); + read } +/// Reads a delayed inbox message #[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_readDelayedInboxMessage( - sp: GoStack, -) { - let seq_num = sp.read_u64(0); - let offset = sp.read_u64(1); - let out_ptr = sp.read_u64(2); - let out_len = sp.read_u64(3); - if out_len != 32 { - eprintln!( - "Go attempting to read inbox message with out len {}", - out_len, - ); - sp.write_u64(4, 0); - return; - } +pub unsafe extern "C" fn wavmio__readDelayedInboxMessage( + msg_num: u64, + offset: usize, + out_ptr: GuestPtr, +) -> usize { let mut our_buf = MemoryLeaf([0u8; 32]); - let our_ptr = our_buf.0.as_mut_ptr(); + let our_ptr = our_buf.as_mut_ptr(); assert_eq!(our_ptr as usize % 32, 0); - let read = wavm_read_delayed_inbox_message(seq_num, our_ptr, offset as usize); + + let read = wavm_read_delayed_inbox_message(msg_num, our_ptr, offset); assert!(read <= 32); - write_slice(&our_buf.0[..read], out_ptr); - sp.write_u64(5, read as u64); + STATIC_MEM.write_slice(out_ptr, &our_buf[..read]); + read } +/// Retrieves the preimage of the given hash. #[no_mangle] -pub unsafe extern "C" fn go__github_com_offchainlabs_nitro_wavmio_resolveTypedPreimage(sp: GoStack) { - let preimage_type = sp.read_u8(0); - let hash_ptr = sp.read_u64(1); - let hash_len = sp.read_u64(2); - let offset = sp.read_u64(4); - let out_ptr = sp.read_u64(5); - let out_len = sp.read_u64(6); - if hash_len != 32 || out_len != 32 { - eprintln!( - "Go attempting to resolve preimage with hash len {} and out len {}", - hash_len, out_len, - ); - sp.write_u64(8, 0); - return; - } - let Ok(preimage_type) = preimage_type.try_into() else { - eprintln!( - "Go trying to resolve preimage with unknown type {}", - preimage_type - ); - sp.write_u64(8, 0); - return; - }; +pub unsafe extern "C" fn wavmio__resolveTypedPreimage( + preimage_type: u8, + hash_ptr: GuestPtr, + offset: usize, + out_ptr: GuestPtr, +) -> usize { + let mut our_buf = MemoryLeaf([0u8; 32]); + let hash = STATIC_MEM.read_slice(hash_ptr, 32); + our_buf.copy_from_slice(&hash); + + let our_ptr = our_buf.as_mut_ptr(); + assert_eq!(our_ptr as usize % 32, 0); let mut our_buf = MemoryLeaf([0u8; 32]); - our_buf.0.copy_from_slice(&read_slice(hash_ptr, hash_len)); - let our_ptr = our_buf.0.as_mut_ptr(); + let hash = STATIC_MEM.read_slice(hash_ptr, 32); + our_buf.copy_from_slice(&hash); + + let our_ptr = our_buf.as_mut_ptr(); assert_eq!(our_ptr as usize % 32, 0); + let preimage_type: PreimageType = preimage_type.try_into().expect("unsupported preimage type"); let preimage_reader = match preimage_type { PreimageType::Keccak256 => wavm_read_keccak_256_preimage, PreimageType::Sha2_256 => wavm_read_sha2_256_preimage, PreimageType::EthVersionedHash => wavm_read_eth_versioned_hash_preimage, }; - let read = preimage_reader(our_ptr, offset as usize); + let read = preimage_reader(our_ptr, offset); assert!(read <= 32); - write_slice(&our_buf.0[..read], out_ptr); - sp.write_u64(8, read as u64); + STATIC_MEM.write_slice(out_ptr, &our_buf[..read]); + read } diff --git a/arbitrator/wasm-libraries/brotli/Cargo.toml b/arbitrator/wasm-libraries/program-exec/Cargo.toml similarity index 58% rename from arbitrator/wasm-libraries/brotli/Cargo.toml rename to arbitrator/wasm-libraries/program-exec/Cargo.toml index 304fc4c4e0..d45f5fe61b 100644 --- a/arbitrator/wasm-libraries/brotli/Cargo.toml +++ b/arbitrator/wasm-libraries/program-exec/Cargo.toml @@ -1,11 +1,9 @@ [package] -name = "brotli" +name = "program-exec" version = "0.1.0" edition = "2021" -publish = false [lib] crate-type = ["cdylib"] [dependencies] -go-abi = { path = "../go-abi" } diff --git a/arbitrator/wasm-libraries/program-exec/src/lib.rs b/arbitrator/wasm-libraries/program-exec/src/lib.rs new file mode 100644 index 0000000000..841da13494 --- /dev/null +++ b/arbitrator/wasm-libraries/program-exec/src/lib.rs @@ -0,0 +1,58 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#[link(wasm_import_module = "hostio")] +extern "C" { + fn program_continue(response: u32) -> u32; + fn program_call_main(module: u32, args_len: usize) -> u32; +} + +#[link(wasm_import_module = "program_internal")] +extern "C" { + fn set_done(status: u32) -> u32; + fn args_len(module: u32) -> usize; +} + +// This module works with user-host +// It has the calls from the main (go) module which transfer +// control to a cothread. +// +// In any time, user-host module's stack may have multiple +// co-threads waiting inside it, due to co-threads making +// to launch a new stylus program (=new cothread). This is +// o.k. because these thread calls are FIFO. +// the main go-module is not FIFO - i.e. we return to go +// while a cothread is waiting for a response - so +// all go-calls come here + +// request_ids start above 0x100 +// return status are 1 byte, so they don't mix +// if we got a return status - notify user-host +// user-host will generate an "execution done" request +fn check_program_done(mut req_id: u32) -> u32 { + if req_id < 0x100 { + unsafe { + req_id = set_done(req_id); + } + } + req_id +} + +/// starts the program (in jit waits for first request) +/// module MUST match last module number returned from new_program +/// returns request_id for the first request from the program +#[no_mangle] +pub unsafe extern "C" fn programs__start_program(module: u32) -> u32 { + // call the program + let args_len = args_len(module); + check_program_done(program_call_main(module, args_len)) +} + +// sends previous response and transfers control to program +// MUST be called right after set_response to the same id +// returns request_id for the next request +#[no_mangle] +pub unsafe extern "C" fn programs__send_response(req_id: u32) -> u32 { + // call the program + check_program_done(program_continue(req_id)) +} diff --git a/arbitrator/wasm-libraries/user-host-trait/Cargo.toml b/arbitrator/wasm-libraries/user-host-trait/Cargo.toml new file mode 100644 index 0000000000..95357f8494 --- /dev/null +++ b/arbitrator/wasm-libraries/user-host-trait/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "user-host-trait" +version = "0.1.0" +edition = "2021" + +[dependencies] +arbutil = { path = "../../arbutil/" } +caller-env = { path = "../../caller-env/" } +prover = { path = "../../prover/", default-features = false } +eyre = "0.6.5" +ruint2 = "1.9.0" diff --git a/arbitrator/wasm-libraries/user-host-trait/src/lib.rs b/arbitrator/wasm-libraries/user-host-trait/src/lib.rs new file mode 100644 index 0000000000..0191718dcc --- /dev/null +++ b/arbitrator/wasm-libraries/user-host-trait/src/lib.rs @@ -0,0 +1,952 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use arbutil::{ + crypto, + evm::{ + self, + api::{DataReader, EvmApi}, + storage::StorageCache, + user::UserOutcomeKind, + EvmData, + }, + pricing::{self, EVM_API_INK, HOSTIO_INK, PTR_INK}, + Bytes20, Bytes32, +}; +pub use caller_env::GuestPtr; +use eyre::{eyre, Result}; +use prover::{ + programs::{meter::OutOfInkError, prelude::*}, + value::Value, +}; +use ruint2::Uint; +use std::fmt::Display; + +macro_rules! be { + ($int:expr) => { + $int.to_be_bytes() + }; +} + +macro_rules! trace { + ($name:expr, $env:expr, [$($args:expr),+], [$($outs:expr),+], $ret:expr) => {{ + if $env.evm_data().tracing { + let end_ink = $env.ink_ready()?; + let mut args = vec![]; + $(args.extend($args);)* + let mut outs = vec![]; + $(outs.extend($outs);)* + $env.trace($name, &args, &outs, end_ink); + } + Ok($ret) + }}; + ($name:expr, $env:expr, [$($args:expr),+], $outs:expr) => {{ + trace!($name, $env, [$($args),+], $outs, ()) + }}; + ($name:expr, $env:expr, $args:expr, $outs:expr) => {{ + trace!($name, $env, $args, $outs, ()) + }}; + ($name:expr, $env:expr, [$($args:expr),+], $outs:expr, $ret:expr) => { + trace!($name, $env, [$($args),+], [$outs], $ret) + }; + ($name:expr, $env:expr, $args:expr, $outs:expr, $ret:expr) => { + trace!($name, $env, [$args], [$outs], $ret) + }; +} +type Address = Bytes20; +type Wei = Bytes32; +type U256 = Uint<256, 4>; + +#[allow(clippy::too_many_arguments)] +pub trait UserHost: GasMeteredMachine { + type Err: From + From + From; + type MemoryErr; + type A: EvmApi; + + fn args(&self) -> &[u8]; + fn outs(&mut self) -> &mut Vec; + + fn evm_api(&mut self) -> &mut Self::A; + fn evm_data(&self) -> &EvmData; + fn evm_return_data_len(&mut self) -> &mut u32; + + fn read_slice(&self, ptr: GuestPtr, len: u32) -> Result, Self::MemoryErr>; + fn read_fixed(&self, ptr: GuestPtr) -> Result<[u8; N], Self::MemoryErr>; + + fn write_u32(&mut self, ptr: GuestPtr, x: u32) -> Result<(), Self::MemoryErr>; + fn write_slice(&self, ptr: GuestPtr, src: &[u8]) -> Result<(), Self::MemoryErr>; + + fn read_bytes20(&self, ptr: GuestPtr) -> Result { + self.read_fixed(ptr).map(Into::into) + } + fn read_bytes32(&self, ptr: GuestPtr) -> Result { + self.read_fixed(ptr).map(Into::into) + } + fn read_u256(&self, ptr: GuestPtr) -> Result<(U256, Bytes32), Self::MemoryErr> { + let value = self.read_bytes32(ptr)?; + Ok((value.into(), value)) + } + + fn say(&self, text: D); + fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], end_ink: u64); + + fn write_bytes20(&self, ptr: GuestPtr, src: Bytes20) -> Result<(), Self::MemoryErr> { + self.write_slice(ptr, &src.0) + } + fn write_bytes32(&self, ptr: GuestPtr, src: Bytes32) -> Result<(), Self::MemoryErr> { + self.write_slice(ptr, &src.0) + } + + /// Reads the program calldata. The semantics are equivalent to that of the EVM's + /// [`CALLDATA_COPY`] opcode when requesting the entirety of the current call's calldata. + /// + /// [`CALLDATA_COPY`]: https://www.evm.codes/#37 + fn read_args(&mut self, ptr: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK)?; + self.pay_for_write(self.args().len() as u32)?; + self.write_slice(ptr, self.args())?; + trace!("read_args", self, &[], self.args()) + } + + /// Writes the final return data. If not called before the program exists, the return data will + /// be 0 bytes long. Note that this hostio does not cause the program to exit, which happens + /// naturally when `user_entrypoint` returns. + fn write_result(&mut self, ptr: GuestPtr, len: u32) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK)?; + self.pay_for_read(len)?; + self.pay_for_geth_bytes(len)?; // returned after call + *self.outs() = self.read_slice(ptr, len)?; + trace!("write_result", self, &*self.outs(), &[]) + } + + /// Exits program execution early with the given status code. + /// If `0`, the program returns successfully with any data supplied by `write_result`. + /// Otherwise, the program reverts and treats any `write_result` data as revert data. + /// + /// The semantics are equivalent to that of the EVM's [`Return`] and [`Revert`] opcodes. + /// Note: this function just traces, it's up to the caller to actually perform the exit. + /// + /// [`Return`]: https://www.evm.codes/#f3 + /// [`Revert`]: https://www.evm.codes/#fd + fn exit_early(&mut self, status: u32) -> Result<(), Self::Err> { + trace!("exit_early", self, be!(status), &[]) + } + + /// Reads a 32-byte value from permanent storage. Stylus's storage format is identical to + /// that of the EVM. This means that, under the hood, this hostio is accessing the 32-byte + /// value stored in the EVM state trie at offset `key`, which will be `0` when not previously + /// set. The semantics, then, are equivalent to that of the EVM's [`SLOAD`] opcode. + /// + /// Note: the Stylus VM implements storage caching. This means that repeated calls to the same key + /// will cost less than in the EVM. + /// + /// [`SLOAD`]: https://www.evm.codes/#54 + fn storage_load_bytes32(&mut self, key: GuestPtr, dest: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 2 * PTR_INK)?; + self.require_gas(evm::COLD_SLOAD_GAS + EVM_API_INK + StorageCache::REQUIRED_ACCESS_GAS)?; // cache-miss case + + let key = self.read_bytes32(key)?; + + let (value, gas_cost) = self.evm_api().get_bytes32(key); + self.buy_gas(gas_cost)?; + self.write_bytes32(dest, value)?; + trace!("storage_load_bytes32", self, key, value) + } + + /// Writes a 32-byte value to the permanent storage cache. Stylus's storage format is identical to that + /// of the EVM. This means that, under the hood, this hostio represents storing a 32-byte value into + /// the EVM state trie at offset `key`. Refunds are tabulated exactly as in the EVM. The semantics, then, + /// are equivalent to that of the EVM's [`SSTORE`] opcode. + /// + /// Note: because this value is cached, one must call `storage_flush_cache` to persist the value. + /// + /// Auditor's note: we require the [`SSTORE`] sentry per EVM rules. The `gas_cost` returned by the EVM API + /// may exceed this amount, but that's ok because the predominant cost is due to state bloat concerns. + /// + /// [`SSTORE`]: https://www.evm.codes/#55 + fn storage_cache_bytes32(&mut self, key: GuestPtr, value: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 2 * PTR_INK)?; + self.require_gas(evm::SSTORE_SENTRY_GAS + StorageCache::REQUIRED_ACCESS_GAS)?; // see operations_acl_arbitrum.go + + let key = self.read_bytes32(key)?; + let value = self.read_bytes32(value)?; + + let gas_cost = self.evm_api().cache_bytes32(key, value); + self.buy_gas(gas_cost)?; + trace!("storage_cache_bytes32", self, [key, value], &[]) + } + + /// Persists any dirty values in the storage cache to the EVM state trie, dropping the cache entirely if requested. + /// Analogous to repeated invocations of [`SSTORE`]. + /// + /// [`SSTORE`]: https://www.evm.codes/#55 + fn storage_flush_cache(&mut self, clear: bool) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + EVM_API_INK)?; + self.require_gas(evm::SSTORE_SENTRY_GAS)?; // see operations_acl_arbitrum.go + + let gas_left = self.gas_left()?; + self.evm_api().flush_storage_cache(clear, gas_left)?; + trace!("storage_flush_cache", self, [be!(clear as u8)], &[]) + } + + /// Reads a 32-byte value from transient storage. Stylus's storage format is identical to + /// that of the EVM. This means that, under the hood, this hostio is accessing the 32-byte + /// value stored in the EVM's transient state trie at offset `key`, which will be `0` when not previously + /// set. The semantics, then, are equivalent to that of the EVM's [`TLOAD`] opcode. + /// + /// [`TLOAD`]: https://www.evm.codes/#5c + fn transient_load_bytes32(&mut self, key: GuestPtr, dest: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 2 * PTR_INK + EVM_API_INK)?; + self.buy_gas(evm::TLOAD_GAS)?; + + let key = self.read_bytes32(key)?; + let value = self.evm_api().get_transient_bytes32(key); + self.write_bytes32(dest, value)?; + trace!("transient_load_bytes32", self, key, value) + } + + /// Writes a 32-byte value to transient storage. Stylus's storage format is identical to that + /// of the EVM. This means that, under the hood, this hostio represents storing a 32-byte value into + /// the EVM's transient state trie at offset `key`. The semantics, then, are equivalent to that of the + /// EVM's [`TSTORE`] opcode. + /// + /// [`TSTORE`]: https://www.evm.codes/#5d + fn transient_store_bytes32(&mut self, key: GuestPtr, value: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 2 * PTR_INK + EVM_API_INK)?; + self.buy_gas(evm::TSTORE_GAS)?; + + let key = self.read_bytes32(key)?; + let value = self.read_bytes32(value)?; + self.evm_api().set_transient_bytes32(key, value)?; + trace!("transient_store_bytes32", self, [key, value], &[]) + } + + /// Calls the contract at the given address with options for passing value and to limit the + /// amount of gas supplied. The return status indicates whether the call succeeded, and is + /// nonzero on failure. + /// + /// In both cases `return_data_len` will store the length of the result, the bytes of which can + /// be read via the `read_return_data` hostio. The bytes are not returned directly so that the + /// programmer can potentially save gas by choosing which subset of the return result they'd + /// like to copy. + /// + /// The semantics are equivalent to that of the EVM's [`CALL`] opcode, including callvalue + /// stipends and the 63/64 gas rule. This means that supplying the `u64::MAX` gas can be used + /// to send as much as possible. + /// + /// [`CALL`]: https://www.evm.codes/#f1 + fn call_contract( + &mut self, + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + value: GuestPtr, + gas: u64, + ret_len: GuestPtr, + ) -> Result { + let value = Some(value); + let call = |api: &mut Self::A, contract, data: &_, left, req, value: Option<_>| { + api.contract_call(contract, data, left, req, value.unwrap()) + }; + self.do_call(contract, data, data_len, value, gas, ret_len, call, "") + } + + /// Delegate calls the contract at the given address, with the option to limit the amount of + /// gas supplied. The return status indicates whether the call succeeded, and is nonzero on + /// failure. + /// + /// In both cases `return_data_len` will store the length of the result, the bytes of which + /// can be read via the `read_return_data` hostio. The bytes are not returned directly so that + /// the programmer can potentially save gas by choosing which subset of the return result + /// they'd like to copy. + /// + /// The semantics are equivalent to that of the EVM's [`DELEGATE_CALL`] opcode, including the + /// 63/64 gas rule. This means that supplying `u64::MAX` gas can be used to send as much as + /// possible. + /// + /// [`DELEGATE_CALL`]: https://www.evm.codes/#F4 + fn delegate_call_contract( + &mut self, + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + gas: u64, + ret_len: GuestPtr, + ) -> Result { + let call = |api: &mut Self::A, contract, data: &_, left, req, _| { + api.delegate_call(contract, data, left, req) + }; + self.do_call( + contract, data, data_len, None, gas, ret_len, call, "delegate", + ) + } + + /// Static calls the contract at the given address, with the option to limit the amount of gas + /// supplied. The return status indicates whether the call succeeded, and is nonzero on + /// failure. + /// + /// In both cases `return_data_len` will store the length of the result, the bytes of which can + /// be read via the `read_return_data` hostio. The bytes are not returned directly so that the + /// programmer can potentially save gas by choosing which subset of the return result they'd + /// like to copy. + /// + /// The semantics are equivalent to that of the EVM's [`STATIC_CALL`] opcode, including the + /// 63/64 gas rule. This means that supplying `u64::MAX` gas can be used to send as much as + /// possible. + /// + /// [`STATIC_CALL`]: https://www.evm.codes/#FA + fn static_call_contract( + &mut self, + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + gas: u64, + ret_len: GuestPtr, + ) -> Result { + let call = |api: &mut Self::A, contract, data: &_, left, req, _| { + api.static_call(contract, data, left, req) + }; + self.do_call(contract, data, data_len, None, gas, ret_len, call, "static") + } + + /// Performs one of the supported EVM calls. + /// Note that `value` must only be [`Some`] for normal calls. + fn do_call( + &mut self, + contract: GuestPtr, + calldata: GuestPtr, + calldata_len: u32, + value: Option, + gas: u64, + return_data_len: GuestPtr, + call: F, + name: &str, + ) -> Result + where + F: FnOnce( + &mut Self::A, + Address, + &[u8], + u64, + u64, + Option, + ) -> (u32, u64, UserOutcomeKind), + { + self.buy_ink(HOSTIO_INK + 3 * PTR_INK + EVM_API_INK)?; + self.pay_for_read(calldata_len)?; + self.pay_for_geth_bytes(calldata_len)?; + + let gas_left = self.gas_left()?; + let gas_req = gas.min(gas_left); + let contract = self.read_bytes20(contract)?; + let input = self.read_slice(calldata, calldata_len)?; + let value = value.map(|x| self.read_bytes32(x)).transpose()?; + let api = self.evm_api(); + + let (outs_len, gas_cost, status) = call(api, contract, &input, gas_left, gas_req, value); + self.buy_gas(gas_cost)?; + *self.evm_return_data_len() = outs_len; + self.write_u32(return_data_len, outs_len)?; + let status = status as u8; + + if self.evm_data().tracing { + let underscore = (!name.is_empty()).then_some("_").unwrap_or_default(); + let name = format!("{name}{underscore}call_contract"); + let value = value.into_iter().flatten(); + return trace!( + &name, + self, + [contract, be!(gas), value, &input], + [be!(outs_len), be!(status)], + status + ); + } + Ok(status) + } + + /// Deploys a new contract using the init code provided, which the EVM executes to construct + /// the code of the newly deployed contract. The init code must be written in EVM bytecode, but + /// the code it deploys can be that of a Stylus program. The code returned will be treated as + /// WASM if it begins with the EOF-inspired header `0xEFF000`. Otherwise the code will be + /// interpreted as that of a traditional EVM-style contract. See [`Deploying Stylus Programs`] + /// for more information on writing init code. + /// + /// On success, this hostio returns the address of the newly created account whose address is + /// a function of the sender and nonce. On failure the address will be `0`, `return_data_len` + /// will store the length of the revert data, the bytes of which can be read via the + /// `read_return_data` hostio. The semantics are equivalent to that of the EVM's [`CREATE`] + /// opcode, which notably includes the exact address returned. + /// + /// [`Deploying Stylus Programs`]: https://developer.arbitrum.io/TODO + /// [`CREATE`]: https://www.evm.codes/#f0 + fn create1( + &mut self, + code: GuestPtr, + code_len: u32, + endowment: GuestPtr, + contract: GuestPtr, + revert_data_len: GuestPtr, + ) -> Result<(), Self::Err> { + let call = |api: &mut Self::A, code, value, _, gas| api.create1(code, value, gas); + self.do_create( + code, + code_len, + endowment, + None, + contract, + revert_data_len, + 3 * PTR_INK + EVM_API_INK, + call, + "create1", + ) + } + + /// Deploys a new contract using the init code provided, which the EVM executes to construct + /// the code of the newly deployed contract. The init code must be written in EVM bytecode, but + /// the code it deploys can be that of a Stylus program. The code returned will be treated as + /// WASM if it begins with the EOF-inspired header `0xEFF000`. Otherwise the code will be + /// interpreted as that of a traditional EVM-style contract. See [`Deploying Stylus Programs`] + /// for more information on writing init code. + /// + /// On success, this hostio returns the address of the newly created account whose address is a + /// function of the sender, salt, and init code. On failure the address will be `0`, + /// `return_data_len` will store the length of the revert data, the bytes of which can be read + /// via the `read_return_data` hostio. The semantics are equivalent to that of the EVM's + /// `[CREATE2`] opcode, which notably includes the exact address returned. + /// + /// [`Deploying Stylus Programs`]: https://developer.arbitrum.io/TODO + /// [`CREATE2`]: https://www.evm.codes/#f5 + fn create2( + &mut self, + code: GuestPtr, + code_len: u32, + endowment: GuestPtr, + salt: GuestPtr, + contract: GuestPtr, + revert_data_len: GuestPtr, + ) -> Result<(), Self::Err> { + let call = |api: &mut Self::A, code, value, salt: Option<_>, gas| { + api.create2(code, value, salt.unwrap(), gas) + }; + self.do_create( + code, + code_len, + endowment, + Some(salt), + contract, + revert_data_len, + 4 * PTR_INK + EVM_API_INK, + call, + "create2", + ) + } + + /// Deploys a contract via [`CREATE`] or [`CREATE2`]. + /// + /// [`CREATE`]: https://www.evm.codes/#f0 + /// [`CREATE2`]: https://www.evm.codes/#f5 + fn do_create( + &mut self, + code: GuestPtr, + code_len: u32, + endowment: GuestPtr, + salt: Option, + contract: GuestPtr, + revert_data_len: GuestPtr, + cost: u64, + call: F, + name: &str, + ) -> Result<(), Self::Err> + where + F: FnOnce(&mut Self::A, Vec, Bytes32, Option, u64) -> (Result
, u32, u64), + { + self.buy_ink(HOSTIO_INK + cost)?; + self.pay_for_read(code_len)?; + self.pay_for_geth_bytes(code_len)?; + + let code = self.read_slice(code, code_len)?; + let code_copy = self.evm_data().tracing.then(|| code.clone()); + + let endowment = self.read_bytes32(endowment)?; + let salt = salt.map(|x| self.read_bytes32(x)).transpose()?; + let gas = self.gas_left()?; + let api = self.evm_api(); + + let (result, ret_len, gas_cost) = call(api, code, endowment, salt, gas); + let result = result?; + + self.buy_gas(gas_cost)?; + *self.evm_return_data_len() = ret_len; + self.write_u32(revert_data_len, ret_len)?; + self.write_bytes20(contract, result)?; + + let salt = salt.into_iter().flatten(); + trace!( + name, + self, + [endowment, salt, code_copy.unwrap()], + [result, be!(ret_len)], + () + ) + } + + /// Copies the bytes of the last EVM call or deployment return result. Does not revert if out of + /// bounds, but rather copies the overlapping portion. The semantics are otherwise equivalent + /// to that of the EVM's [`RETURN_DATA_COPY`] opcode. + /// + /// Returns the number of bytes written. + /// + /// [`RETURN_DATA_COPY`]: https://www.evm.codes/#3e + fn read_return_data( + &mut self, + dest: GuestPtr, + offset: u32, + size: u32, + ) -> Result { + self.buy_ink(HOSTIO_INK + EVM_API_INK)?; + + // pay for only as many bytes as could possibly be written + let max = self.evm_return_data_len().saturating_sub(offset); + self.pay_for_write(size.min(max))?; + + let ret_data = self.evm_api().get_return_data(); + let ret_data = ret_data.slice(); + let out_slice = arbutil::slice_with_runoff(&ret_data, offset, offset.saturating_add(size)); + + let out_len = out_slice.len() as u32; + if out_len > 0 { + self.write_slice(dest, out_slice)?; + } + trace!( + "read_return_data", + self, + [be!(offset), be!(size)], + out_slice.to_vec(), + out_len + ) + } + + /// Returns the length of the last EVM call or deployment return result, or `0` if neither have + /// happened during the program's execution. The semantics are equivalent to that of the EVM's + /// [`RETURN_DATA_SIZE`] opcode. + /// + /// [`RETURN_DATA_SIZE`]: https://www.evm.codes/#3d + fn return_data_size(&mut self) -> Result { + self.buy_ink(HOSTIO_INK)?; + let len = *self.evm_return_data_len(); + trace!("return_data_size", self, be!(len), &[], len) + } + + /// Emits an EVM log with the given number of topics and data, the first bytes of which should + /// be the 32-byte-aligned topic data. The semantics are equivalent to that of the EVM's + /// [`LOG0`], [`LOG1`], [`LOG2`], [`LOG3`], and [`LOG4`] opcodes based on the number of topics + /// specified. Requesting more than `4` topics will induce a revert. + /// + /// [`LOG0`]: https://www.evm.codes/#a0 + /// [`LOG1`]: https://www.evm.codes/#a1 + /// [`LOG2`]: https://www.evm.codes/#a2 + /// [`LOG3`]: https://www.evm.codes/#a3 + /// [`LOG4`]: https://www.evm.codes/#a4 + fn emit_log(&mut self, data: GuestPtr, len: u32, topics: u32) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + EVM_API_INK)?; + if topics > 4 || len < topics * 32 { + Err(eyre!("bad topic data"))?; + } + self.pay_for_read(len)?; + self.pay_for_evm_log(topics, len - topics * 32)?; + + let data = self.read_slice(data, len)?; + self.evm_api().emit_log(data.clone(), topics)?; + trace!("emit_log", self, [be!(topics), data], &[]) + } + + /// Gets the ETH balance in wei of the account at the given address. + /// The semantics are equivalent to that of the EVM's [`BALANCE`] opcode. + /// + /// [`BALANCE`]: https://www.evm.codes/#31 + fn account_balance(&mut self, address: GuestPtr, ptr: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 2 * PTR_INK + EVM_API_INK)?; + self.require_gas(evm::COLD_ACCOUNT_GAS)?; + let address = self.read_bytes20(address)?; + + let (balance, gas_cost) = self.evm_api().account_balance(address); + self.buy_gas(gas_cost)?; + self.write_bytes32(ptr, balance)?; + trace!("account_balance", self, address, balance) + } + + /// Gets a subset of the code from the account at the given address. The semantics are identical to that + /// of the EVM's [`EXT_CODE_COPY`] opcode, aside from one small detail: the write to the buffer `dest` will + /// stop after the last byte is written. This is unlike the EVM, which right pads with zeros in this scenario. + /// The return value is the number of bytes written, which allows the caller to detect if this has occured. + /// + /// [`EXT_CODE_COPY`]: https://www.evm.codes/#3C + fn account_code( + &mut self, + address: GuestPtr, + offset: u32, + size: u32, + dest: GuestPtr, + ) -> Result { + self.buy_ink(HOSTIO_INK + EVM_API_INK)?; + self.require_gas(evm::COLD_ACCOUNT_GAS)?; // not necessary since we also check in Go + + let address = self.read_bytes20(address)?; + let gas = self.gas_left()?; + + // we pass `gas` to check if there's enough before loading from the db + let (code, gas_cost) = self.evm_api().account_code(address, gas); + self.buy_gas(gas_cost)?; + + let code = code.slice(); + self.pay_for_write(code.len() as u32)?; + + let out_slice = arbutil::slice_with_runoff(&code, offset, offset.saturating_add(size)); + let out_len = out_slice.len() as u32; + self.write_slice(dest, out_slice)?; + + trace!( + "account_code", + self, + [address, be!(offset), be!(size)], + out_slice.to_vec(), + out_len + ) + } + + /// Gets the size of the code in bytes at the given address. The semantics are equivalent + /// to that of the EVM's [`EXT_CODESIZE`]. + /// + /// [`EXT_CODESIZE`]: https://www.evm.codes/#3B + fn account_code_size(&mut self, address: GuestPtr) -> Result { + self.buy_ink(HOSTIO_INK + EVM_API_INK)?; + self.require_gas(evm::COLD_ACCOUNT_GAS)?; // not necessary since we also check in Go + let address = self.read_bytes20(address)?; + let gas = self.gas_left()?; + + // we pass `gas` to check if there's enough before loading from the db + let (code, gas_cost) = self.evm_api().account_code(address, gas); + self.buy_gas(gas_cost)?; + + let code = code.slice(); + trace!("account_code_size", self, address, &[], code.len() as u32) + } + + /// Gets the code hash of the account at the given address. The semantics are equivalent + /// to that of the EVM's [`EXT_CODEHASH`] opcode. Note that the code hash of an account without + /// code will be the empty hash + /// `keccak("") = c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470`. + /// + /// [`EXT_CODEHASH`]: https://www.evm.codes/#3F + fn account_codehash(&mut self, address: GuestPtr, ptr: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 2 * PTR_INK + EVM_API_INK)?; + self.require_gas(evm::COLD_ACCOUNT_GAS)?; + let address = self.read_bytes20(address)?; + + let (hash, gas_cost) = self.evm_api().account_codehash(address); + self.buy_gas(gas_cost)?; + self.write_bytes32(ptr, hash)?; + trace!("account_codehash", self, address, hash) + } + + /// Gets the basefee of the current block. The semantics are equivalent to that of the EVM's + /// [`BASEFEE`] opcode. + /// + /// [`BASEFEE`]: https://www.evm.codes/#48 + fn block_basefee(&mut self, ptr: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + PTR_INK)?; + self.write_bytes32(ptr, self.evm_data().block_basefee)?; + trace!("block_basefee", self, &[], self.evm_data().block_basefee) + } + + /// Gets the coinbase of the current block, which on Arbitrum chains is the L1 batch poster's + /// address. This differs from Ethereum where the validator including the transaction + /// determines the coinbase. + fn block_coinbase(&mut self, ptr: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + PTR_INK)?; + self.write_bytes20(ptr, self.evm_data().block_coinbase)?; + trace!("block_coinbase", self, &[], self.evm_data().block_coinbase) + } + + /// Gets the gas limit of the current block. The semantics are equivalent to that of the EVM's + /// [`GAS_LIMIT`] opcode. Note that as of the time of this writing, `evm.codes` incorrectly + /// implies that the opcode returns the gas limit of the current transaction. When in doubt, + /// consult [`The Ethereum Yellow Paper`]. + /// + /// [`GAS_LIMIT`]: https://www.evm.codes/#45 + /// [`The Ethereum Yellow Paper`]: https://ethereum.github.io/yellowpaper/paper.pdf + fn block_gas_limit(&mut self) -> Result { + self.buy_ink(HOSTIO_INK)?; + let limit = self.evm_data().block_gas_limit; + trace!("block_gas_limit", self, &[], be!(limit), limit) + } + + /// Gets a bounded estimate of the L1 block number at which the Sequencer sequenced the + /// transaction. See [`Block Numbers and Time`] for more information on how this value is + /// determined. + /// + /// [`Block Numbers and Time`]: https://developer.arbitrum.io/time + fn block_number(&mut self) -> Result { + self.buy_ink(HOSTIO_INK)?; + let number = self.evm_data().block_number; + trace!("block_number", self, &[], be!(number), number) + } + + /// Gets a bounded estimate of the Unix timestamp at which the Sequencer sequenced the + /// transaction. See [`Block Numbers and Time`] for more information on how this value is + /// determined. + /// + /// [`Block Numbers and Time`]: https://developer.arbitrum.io/time + fn block_timestamp(&mut self) -> Result { + self.buy_ink(HOSTIO_INK)?; + let timestamp = self.evm_data().block_timestamp; + trace!("block_timestamp", self, &[], be!(timestamp), timestamp) + } + + /// Gets the unique chain identifier of the Arbitrum chain. The semantics are equivalent to + /// that of the EVM's [`CHAIN_ID`] opcode. + /// + /// [`CHAIN_ID`]: https://www.evm.codes/#46 + fn chainid(&mut self) -> Result { + self.buy_ink(HOSTIO_INK)?; + let chainid = self.evm_data().chainid; + trace!("chainid", self, &[], be!(chainid), chainid) + } + + /// Gets the address of the current program. The semantics are equivalent to that of the EVM's + /// [`ADDRESS`] opcode. + /// + /// [`ADDRESS`]: https://www.evm.codes/#30 + fn contract_address(&mut self, ptr: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + PTR_INK)?; + self.write_bytes20(ptr, self.evm_data().contract_address)?; + trace!( + "contract_address", + self, + &[], + self.evm_data().contract_address + ) + } + + /// Gets the amount of gas left after paying for the cost of this hostio. The semantics are + /// equivalent to that of the EVM's [`GAS`] opcode. + /// + /// [`GAS`]: https://www.evm.codes/#5a + fn evm_gas_left(&mut self) -> Result { + self.buy_ink(HOSTIO_INK)?; + let gas = self.gas_left()?; + trace!("evm_gas_left", self, be!(gas), &[], gas) + } + + /// Gets the amount of ink remaining after paying for the cost of this hostio. The semantics + /// are equivalent to that of the EVM's [`GAS`] opcode, except the units are in ink. See + /// [`Ink and Gas`] for more information on Stylus's compute pricing. + /// + /// [`GAS`]: https://www.evm.codes/#5a + /// [`Ink and Gas`]: https://developer.arbitrum.io/TODO + fn evm_ink_left(&mut self) -> Result { + self.buy_ink(HOSTIO_INK)?; + let ink = self.ink_ready()?; + trace!("evm_ink_left", self, be!(ink), &[], ink) + } + + /// Computes `value ÷ exponent` using 256-bit math, writing the result to the first. + /// The semantics are equivalent to that of the EVM's [`DIV`] opcode, which means that a `divisor` of `0` + /// writes `0` to `value`. + /// + /// [`DIV`]: https://www.evm.codes/#04 + fn math_div(&mut self, value: GuestPtr, divisor: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 3 * PTR_INK + pricing::DIV_INK)?; + let (a, a32) = self.read_u256(value)?; + let (b, b32) = self.read_u256(divisor)?; + + let result = a.checked_div(b).unwrap_or_default().into(); + self.write_bytes32(value, result)?; + trace!("math_div", self, [a32, b32], result) + } + + /// Computes `value % exponent` using 256-bit math, writing the result to the first. + /// The semantics are equivalent to that of the EVM's [`MOD`] opcode, which means that a `modulus` of `0` + /// writes `0` to `value`. + /// + /// [`MOD`]: https://www.evm.codes/#06 + fn math_mod(&mut self, value: GuestPtr, modulus: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 3 * PTR_INK + pricing::DIV_INK)?; + let (a, a32) = self.read_u256(value)?; + let (b, b32) = self.read_u256(modulus)?; + + let result = a.checked_rem(b).unwrap_or_default().into(); + self.write_bytes32(value, result)?; + trace!("math_mod", self, [a32, b32], result) + } + + /// Computes `value ^ exponent` using 256-bit math, writing the result to the first. + /// The semantics are equivalent to that of the EVM's [`EXP`] opcode. + /// + /// [`EXP`]: https://www.evm.codes/#0A + fn math_pow(&mut self, value: GuestPtr, exponent: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 3 * PTR_INK)?; + let (a, a32) = self.read_u256(value)?; + let (b, b32) = self.read_u256(exponent)?; + + self.pay_for_pow(&b32)?; + let result = a.wrapping_pow(b).into(); + self.write_bytes32(value, result)?; + trace!("math_pow", self, [a32, b32], result) + } + + /// Computes `(value + addend) % modulus` using 256-bit math, writing the result to the first. + /// The semantics are equivalent to that of the EVM's [`ADDMOD`] opcode, which means that a `modulus` of `0` + /// writes `0` to `value`. + /// + /// [`ADDMOD`]: https://www.evm.codes/#08 + fn math_add_mod( + &mut self, + value: GuestPtr, + addend: GuestPtr, + modulus: GuestPtr, + ) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 4 * PTR_INK + pricing::ADD_MOD_INK)?; + let (a, a32) = self.read_u256(value)?; + let (b, b32) = self.read_u256(addend)?; + let (c, c32) = self.read_u256(modulus)?; + + let result = a.add_mod(b, c).into(); + self.write_bytes32(value, result)?; + trace!("math_add_mod", self, [a32, b32, c32], result) + } + + /// Computes `(value * multiplier) % modulus` using 256-bit math, writing the result to the first. + /// The semantics are equivalent to that of the EVM's [`MULMOD`] opcode, which means that a `modulus` of `0` + /// writes `0` to `value`. + /// + /// [`MULMOD`]: https://www.evm.codes/#09 + fn math_mul_mod( + &mut self, + value: GuestPtr, + multiplier: GuestPtr, + modulus: GuestPtr, + ) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + 4 * PTR_INK + pricing::MUL_MOD_INK)?; + let (a, a32) = self.read_u256(value)?; + let (b, b32) = self.read_u256(multiplier)?; + let (c, c32) = self.read_u256(modulus)?; + + let result = a.mul_mod(b, c).into(); + self.write_bytes32(value, result)?; + trace!("math_mul_mod", self, [a32, b32, c32], result) + } + + /// Whether the current call is reentrant. + fn msg_reentrant(&mut self) -> Result { + self.buy_ink(HOSTIO_INK)?; + let reentrant = self.evm_data().reentrant; + trace!("msg_reentrant", self, &[], be!(reentrant), reentrant) + } + + /// Gets the address of the account that called the program. For normal L2-to-L2 transactions + /// the semantics are equivalent to that of the EVM's [`CALLER`] opcode, including in cases + /// arising from [`DELEGATE_CALL`]. + /// + /// For L1-to-L2 retryable ticket transactions, the top-level sender's address will be aliased. + /// See [`Retryable Ticket Address Aliasing`][aliasing] for more information on how this works. + /// + /// [`CALLER`]: https://www.evm.codes/#33 + /// [`DELEGATE_CALL`]: https://www.evm.codes/#f4 + /// [aliasing]: https://developer.arbitrum.io/arbos/l1-to-l2-messaging#address-aliasing + fn msg_sender(&mut self, ptr: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + PTR_INK)?; + self.write_bytes20(ptr, self.evm_data().msg_sender)?; + trace!("msg_sender", self, &[], self.evm_data().msg_sender) + } + + /// Get the ETH value in wei sent to the program. The semantics are equivalent to that of the + /// EVM's [`CALLVALUE`] opcode. + /// + /// [`CALLVALUE`]: https://www.evm.codes/#34 + fn msg_value(&mut self, ptr: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + PTR_INK)?; + self.write_bytes32(ptr, self.evm_data().msg_value)?; + trace!("msg_value", self, &[], self.evm_data().msg_value) + } + + /// Efficiently computes the [`keccak256`] hash of the given preimage. + /// The semantics are equivalent to that of the EVM's [`SHA3`] opcode. + /// + /// [`keccak256`]: https://en.wikipedia.org/wiki/SHA-3 + /// [`SHA3`]: https://www.evm.codes/#20 + fn native_keccak256( + &mut self, + input: GuestPtr, + len: u32, + output: GuestPtr, + ) -> Result<(), Self::Err> { + self.pay_for_keccak(len)?; + + let preimage = self.read_slice(input, len)?; + let digest = crypto::keccak(&preimage); + self.write_bytes32(output, digest.into())?; + trace!("native_keccak256", self, preimage, digest) + } + + /// Gets the gas price in wei per gas, which on Arbitrum chains equals the basefee. The + /// semantics are equivalent to that of the EVM's [`GAS_PRICE`] opcode. + /// + /// [`GAS_PRICE`]: https://www.evm.codes/#3A + fn tx_gas_price(&mut self, ptr: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + PTR_INK)?; + self.write_bytes32(ptr, self.evm_data().tx_gas_price)?; + trace!("tx_gas_price", self, &[], self.evm_data().tx_gas_price) + } + + /// Gets the price of ink in evm gas basis points. See [`Ink and Gas`] for more information on + /// Stylus's compute-pricing model. + /// + /// [`Ink and Gas`]: https://developer.arbitrum.io/TODO + fn tx_ink_price(&mut self) -> Result { + self.buy_ink(HOSTIO_INK)?; + let ink_price = self.pricing().ink_price; + trace!("tx_ink_price", self, &[], be!(ink_price), ink_price) + } + + /// Gets the top-level sender of the transaction. The semantics are equivalent to that of the + /// EVM's [`ORIGIN`] opcode. + /// + /// [`ORIGIN`]: https://www.evm.codes/#32 + fn tx_origin(&mut self, ptr: GuestPtr) -> Result<(), Self::Err> { + self.buy_ink(HOSTIO_INK + PTR_INK)?; + self.write_bytes20(ptr, self.evm_data().tx_origin)?; + trace!("tx_origin", self, &[], self.evm_data().tx_origin) + } + + /// Pays for new pages as needed before the memory.grow opcode is invoked. + fn pay_for_memory_grow(&mut self, pages: u16) -> Result<(), Self::Err> { + if pages == 0 { + self.buy_ink(HOSTIO_INK)?; + return Ok(()); + } + let gas_cost = self.evm_api().add_pages(pages); // no sentry needed since the work happens after the hostio + self.buy_gas(gas_cost)?; + trace!("pay_for_memory_grow", self, be!(pages), &[]) + } + + /// Prints a UTF-8 encoded string to the console. Only available in debug mode. + fn console_log_text(&mut self, ptr: GuestPtr, len: u32) -> Result<(), Self::Err> { + let text = self.read_slice(ptr, len)?; + self.say(String::from_utf8_lossy(&text)); + trace!("console_log_text", self, text, &[]) + } + + /// Prints a value to the console. Only available in debug mode. + fn console_log>(&mut self, value: T) -> Result<(), Self::Err> { + let value = value.into(); + self.say(value); + trace!("console_log", self, [format!("{value}").as_bytes()], &[]) + } + + /// Prints and returns a value to the console. Only available in debug mode. + fn console_tee + Copy>(&mut self, value: T) -> Result { + self.say(value.into()); + Ok(value) + } +} diff --git a/arbitrator/wasm-libraries/user-host/Cargo.toml b/arbitrator/wasm-libraries/user-host/Cargo.toml new file mode 100644 index 0000000000..15174397eb --- /dev/null +++ b/arbitrator/wasm-libraries/user-host/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "user-host" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +arbutil = { path = "../../arbutil/" } +caller-env = { path = "../../caller-env/", features = ["static_caller"] } +prover = { path = "../../prover/", default-features = false } +user-host-trait = { path = "../user-host-trait" } +wasmer-types = { path = "../../tools/wasmer/lib/types" } +eyre = "0.6.5" +fnv = "1.0.7" +hex = "0.4.3" diff --git a/arbitrator/wasm-libraries/user-host/src/host.rs b/arbitrator/wasm-libraries/user-host/src/host.rs new file mode 100644 index 0000000000..abe55b8c12 --- /dev/null +++ b/arbitrator/wasm-libraries/user-host/src/host.rs @@ -0,0 +1,289 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use crate::program::Program; +use arbutil::evm::user::UserOutcomeKind; +use caller_env::GuestPtr; +use user_host_trait::UserHost; + +#[link(wasm_import_module = "forward")] +extern "C" { + fn set_trap(); +} + +macro_rules! hostio { + ($($func:tt)*) => { + match Program::current().$($func)* { + Ok(value) => value, + Err(_) => { + set_trap(); + Default::default() + } + } + }; +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__read_args(ptr: GuestPtr) { + hostio!(read_args(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__exit_early(status: u32) { + hostio!(exit_early(status)); + Program::current().early_exit = Some(match status { + 0 => UserOutcomeKind::Success, + _ => UserOutcomeKind::Revert, + }); + set_trap(); +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__write_result(ptr: GuestPtr, len: u32) { + hostio!(write_result(ptr, len)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__storage_load_bytes32(key: GuestPtr, dest: GuestPtr) { + hostio!(storage_load_bytes32(key, dest)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__storage_cache_bytes32(key: GuestPtr, value: GuestPtr) { + hostio!(storage_cache_bytes32(key, value)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__storage_flush_cache(clear: u32) { + hostio!(storage_flush_cache(clear != 0)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__transient_load_bytes32(key: GuestPtr, dest: GuestPtr) { + hostio!(transient_load_bytes32(key, dest)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__transient_store_bytes32(key: GuestPtr, value: GuestPtr) { + hostio!(transient_store_bytes32(key, value)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__call_contract( + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + value: GuestPtr, + gas: u64, + ret_len: GuestPtr, +) -> u8 { + hostio!(call_contract(contract, data, data_len, value, gas, ret_len)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__delegate_call_contract( + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + gas: u64, + ret_len: GuestPtr, +) -> u8 { + hostio!(delegate_call_contract( + contract, data, data_len, gas, ret_len + )) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__static_call_contract( + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + gas: u64, + ret_len: GuestPtr, +) -> u8 { + hostio!(static_call_contract(contract, data, data_len, gas, ret_len)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__create1( + code: GuestPtr, + code_len: u32, + value: GuestPtr, + contract: GuestPtr, + revert_len: GuestPtr, +) { + hostio!(create1(code, code_len, value, contract, revert_len)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__create2( + code: GuestPtr, + code_len: u32, + value: GuestPtr, + salt: GuestPtr, + contract: GuestPtr, + revert_len: GuestPtr, +) { + hostio!(create2(code, code_len, value, salt, contract, revert_len)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__read_return_data( + dest: GuestPtr, + offset: u32, + size: u32, +) -> u32 { + hostio!(read_return_data(dest, offset, size)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__return_data_size() -> u32 { + hostio!(return_data_size()) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__emit_log(data: GuestPtr, len: u32, topics: u32) { + hostio!(emit_log(data, len, topics)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__account_balance(address: GuestPtr, ptr: GuestPtr) { + hostio!(account_balance(address, ptr)) +} +#[no_mangle] +pub unsafe extern "C" fn user_host__account_code( + address: GuestPtr, + offset: u32, + size: u32, + dest: GuestPtr, +) -> u32 { + hostio!(account_code(address, offset, size, dest)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__account_code_size(address: GuestPtr) -> u32 { + hostio!(account_code_size(address)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__account_codehash(address: GuestPtr, ptr: GuestPtr) { + hostio!(account_codehash(address, ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__block_basefee(ptr: GuestPtr) { + hostio!(block_basefee(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__block_coinbase(ptr: GuestPtr) { + hostio!(block_coinbase(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__block_gas_limit() -> u64 { + hostio!(block_gas_limit()) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__block_number() -> u64 { + hostio!(block_number()) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__block_timestamp() -> u64 { + hostio!(block_timestamp()) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__chainid() -> u64 { + hostio!(chainid()) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__contract_address(ptr: GuestPtr) { + hostio!(contract_address(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__evm_gas_left() -> u64 { + hostio!(evm_gas_left()) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__evm_ink_left() -> u64 { + hostio!(evm_ink_left()) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__math_div(value: GuestPtr, divisor: GuestPtr) { + hostio!(math_div(value, divisor)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__math_mod(value: GuestPtr, modulus: GuestPtr) { + hostio!(math_mod(value, modulus)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__math_pow(value: GuestPtr, exponent: GuestPtr) { + hostio!(math_pow(value, exponent)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__math_add_mod( + value: GuestPtr, + addend: GuestPtr, + modulus: GuestPtr, +) { + hostio!(math_add_mod(value, addend, modulus)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__math_mul_mod( + value: GuestPtr, + multiplier: GuestPtr, + modulus: GuestPtr, +) { + hostio!(math_mul_mod(value, multiplier, modulus)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__msg_reentrant() -> u32 { + hostio!(msg_reentrant()) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__msg_sender(ptr: GuestPtr) { + hostio!(msg_sender(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__msg_value(ptr: GuestPtr) { + hostio!(msg_value(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__native_keccak256(input: GuestPtr, len: u32, output: GuestPtr) { + hostio!(native_keccak256(input, len, output)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__tx_gas_price(ptr: GuestPtr) { + hostio!(tx_gas_price(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__tx_ink_price() -> u32 { + hostio!(tx_ink_price()) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__tx_origin(ptr: GuestPtr) { + hostio!(tx_origin(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn user_host__pay_for_memory_grow(pages: u16) { + hostio!(pay_for_memory_grow(pages)) +} diff --git a/arbitrator/wasm-libraries/user-host/src/ink.rs b/arbitrator/wasm-libraries/user-host/src/ink.rs new file mode 100644 index 0000000000..e01e616e07 --- /dev/null +++ b/arbitrator/wasm-libraries/user-host/src/ink.rs @@ -0,0 +1,38 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::program::Program; +use prover::programs::{ + config::PricingParams, + prelude::{GasMeteredMachine, MachineMeter, MeteredMachine}, +}; + +#[link(wasm_import_module = "hostio")] +extern "C" { + fn user_ink_left() -> u64; + fn user_ink_status() -> u32; + fn user_set_ink(ink: u64, status: u32); +} + +impl MeteredMachine for Program { + fn ink_left(&self) -> MachineMeter { + unsafe { + match user_ink_status() { + 0 => MachineMeter::Ready(user_ink_left()), + _ => MachineMeter::Exhausted, + } + } + } + + fn set_meter(&mut self, meter: MachineMeter) { + unsafe { + user_set_ink(meter.ink(), meter.status()); + } + } +} + +impl GasMeteredMachine for Program { + fn pricing(&self) -> PricingParams { + self.config.pricing + } +} diff --git a/arbitrator/wasm-libraries/user-host/src/lib.rs b/arbitrator/wasm-libraries/user-host/src/lib.rs new file mode 100644 index 0000000000..cd2d142850 --- /dev/null +++ b/arbitrator/wasm-libraries/user-host/src/lib.rs @@ -0,0 +1,7 @@ +// Copyright 2022-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +mod host; +mod ink; +mod link; +mod program; diff --git a/arbitrator/wasm-libraries/user-host/src/link.rs b/arbitrator/wasm-libraries/user-host/src/link.rs new file mode 100644 index 0000000000..428611167d --- /dev/null +++ b/arbitrator/wasm-libraries/user-host/src/link.rs @@ -0,0 +1,280 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::program::Program; +use arbutil::{ + evm::{user::UserOutcomeKind, EvmData}, + format::DebugBytes, + heapify, Bytes20, Bytes32, +}; +use caller_env::{static_caller::STATIC_MEM, GuestPtr, MemAccess}; +use prover::{machine::Module, programs::config::StylusConfig}; + +// these hostio methods allow the replay machine to modify itself +#[link(wasm_import_module = "hostio")] +extern "C" { + fn wavm_link_module(hash: *const MemoryLeaf) -> u32; + fn wavm_unlink_module(); +} + +// these dynamic hostio methods allow introspection into user modules +#[link(wasm_import_module = "hostio")] +extern "C" { + fn program_set_ink(module: u32, ink: u64); + fn program_set_stack(module: u32, stack: u32); + fn program_ink_left(module: u32) -> u64; + fn program_ink_status(module: u32) -> u32; + fn program_stack_left(module: u32) -> u32; +} + +#[repr(C, align(256))] +struct MemoryLeaf([u8; 32]); + +/// Instruments and "activates" a user wasm, producing a unique module hash. +/// +/// Note that this operation costs gas and is limited by the amount supplied via the `gas` pointer. +/// The amount left is written back at the end of the call. +/// +/// pages_ptr: starts pointing to max allowed pages, returns number of pages used +#[no_mangle] +pub unsafe extern "C" fn programs__activate( + wasm_ptr: GuestPtr, + wasm_size: usize, + pages_ptr: GuestPtr, + asm_estimate_ptr: GuestPtr, + init_cost_ptr: GuestPtr, + cached_init_cost_ptr: GuestPtr, + version: u16, + debug: u32, + codehash: GuestPtr, + module_hash_ptr: GuestPtr, + gas_ptr: GuestPtr, + err_buf: GuestPtr, + err_buf_len: usize, +) -> usize { + let wasm = STATIC_MEM.read_slice(wasm_ptr, wasm_size); + let codehash = &read_bytes32(codehash); + let debug = debug != 0; + + let page_limit = STATIC_MEM.read_u16(pages_ptr); + let gas_left = &mut STATIC_MEM.read_u64(gas_ptr); + match Module::activate(&wasm, codehash, version, page_limit, debug, gas_left) { + Ok((module, data)) => { + STATIC_MEM.write_u64(gas_ptr, *gas_left); + STATIC_MEM.write_u16(pages_ptr, data.footprint); + STATIC_MEM.write_u32(asm_estimate_ptr, data.asm_estimate); + STATIC_MEM.write_u16(init_cost_ptr, data.init_cost); + STATIC_MEM.write_u16(cached_init_cost_ptr, data.cached_init_cost); + STATIC_MEM.write_slice(module_hash_ptr, module.hash().as_slice()); + 0 + } + Err(error) => { + let mut err_bytes = error.wrap_err("failed to activate").debug_bytes(); + err_bytes.truncate(err_buf_len); + STATIC_MEM.write_slice(err_buf, &err_bytes); + STATIC_MEM.write_u64(gas_ptr, 0); + STATIC_MEM.write_u16(pages_ptr, 0); + STATIC_MEM.write_u32(asm_estimate_ptr, 0); + STATIC_MEM.write_u16(init_cost_ptr, 0); + STATIC_MEM.write_u16(cached_init_cost_ptr, 0); + STATIC_MEM.write_slice(module_hash_ptr, Bytes32::default().as_slice()); + err_bytes.len() + } + } +} + +unsafe fn read_bytes32(ptr: GuestPtr) -> Bytes32 { + STATIC_MEM.read_fixed(ptr).into() +} + +unsafe fn read_bytes20(ptr: GuestPtr) -> Bytes20 { + STATIC_MEM.read_fixed(ptr).into() +} + +/// Links and creates user program +/// consumes both evm_data_handler and config_handler +/// returns module number +/// see program-exec for starting the user program +#[no_mangle] +pub unsafe extern "C" fn programs__new_program( + module_hash_ptr: GuestPtr, + calldata_ptr: GuestPtr, + calldata_size: usize, + config_box: u64, + evm_data_box: u64, + gas: u64, +) -> u32 { + let module_hash = read_bytes32(module_hash_ptr); + let calldata = STATIC_MEM.read_slice(calldata_ptr, calldata_size); + let config: StylusConfig = *Box::from_raw(config_box as _); + let evm_data: EvmData = *Box::from_raw(evm_data_box as _); + + // buy ink + let pricing = config.pricing; + let ink = pricing.gas_to_ink(gas); + + // link the program and ready its instrumentation + let module = wavm_link_module(&MemoryLeaf(*module_hash)); + program_set_ink(module, ink); + program_set_stack(module, config.max_depth); + + // provide arguments + Program::push_new(calldata, evm_data, module, config); + module +} + +/// Gets information about request according to id. +/// +/// # Safety +/// +/// `request_id` MUST be last request id returned from start_program or send_response. +#[no_mangle] +pub unsafe extern "C" fn programs__get_request(id: u32, len_ptr: GuestPtr) -> u32 { + let (req_type, len) = Program::current().request_handler().get_request_meta(id); + if len_ptr != GuestPtr(0) { + STATIC_MEM.write_u32(len_ptr, len as u32); + } + req_type +} + +/// Gets data associated with last request. +/// +/// # Safety +/// +/// `request_id` MUST be last request receieved +/// `data_ptr` MUST point to a buffer of at least the length returned by `get_request` +#[no_mangle] +pub unsafe extern "C" fn programs__get_request_data(id: u32, data_ptr: GuestPtr) { + let (_, data) = Program::current().request_handler().take_request(id); + STATIC_MEM.write_slice(data_ptr, &data); +} + +/// sets response for the next request made +/// id MUST be the id of last request made +/// see `program-exec::send_response` for sending this response to the program +#[no_mangle] +pub unsafe extern "C" fn programs__set_response( + id: u32, + gas: u64, + result_ptr: GuestPtr, + result_len: usize, + raw_data_ptr: GuestPtr, + raw_data_len: usize, +) { + let program = Program::current(); + program.request_handler().set_response( + id, + STATIC_MEM.read_slice(result_ptr, result_len), + STATIC_MEM.read_slice(raw_data_ptr, raw_data_len), + gas, + ); +} + +// removes the last created program +#[no_mangle] +pub unsafe extern "C" fn programs__pop() { + Program::pop(); + wavm_unlink_module(); +} + +// used by program-exec +// returns arguments_len +// module MUST be the last one returned from new_program +#[no_mangle] +pub unsafe extern "C" fn program_internal__args_len(module: u32) -> usize { + let program = Program::current(); + if program.module != module { + panic!("args_len requested for wrong module"); + } + program.args_len() +} + +/// used by program-exec +/// sets status of the last program and sends a program_done request +#[no_mangle] +pub unsafe extern "C" fn program_internal__set_done(mut status: UserOutcomeKind) -> u32 { + use UserOutcomeKind::*; + + let program = Program::current(); + let module = program.module; + let mut outs = program.outs.as_slice(); + let mut ink_left = program_ink_left(module); + + // apply any early exit codes + if let Some(early) = program.early_exit { + status = early; + } + + // check if instrumentation stopped the program + if program_ink_status(module) != 0 { + status = OutOfInk; + outs = &[]; + ink_left = 0; + } + if program_stack_left(module) == 0 { + status = OutOfStack; + outs = &[]; + ink_left = 0; + } + + let gas_left = program.config.pricing.ink_to_gas(ink_left); + + let mut output = Vec::with_capacity(8 + outs.len()); + output.extend(gas_left.to_be_bytes()); + output.extend(outs); + program + .request_handler() + .set_request(status as u32, &output) +} + +/// Creates a `StylusConfig` from its component parts. +#[no_mangle] +pub unsafe extern "C" fn programs__create_stylus_config( + version: u16, + max_depth: u32, + ink_price: u32, + _debug: u32, +) -> u64 { + let config = StylusConfig::new(version, max_depth, ink_price); + heapify(config) as u64 +} + +/// Creates an `EvmData` handler from its component parts. +/// +#[no_mangle] +pub unsafe extern "C" fn programs__create_evm_data( + block_basefee_ptr: GuestPtr, + chainid: u64, + block_coinbase_ptr: GuestPtr, + block_gas_limit: u64, + block_number: u64, + block_timestamp: u64, + contract_address_ptr: GuestPtr, + module_hash_ptr: GuestPtr, + msg_sender_ptr: GuestPtr, + msg_value_ptr: GuestPtr, + tx_gas_price_ptr: GuestPtr, + tx_origin_ptr: GuestPtr, + cached: u32, + reentrant: u32, +) -> u64 { + let evm_data = EvmData { + block_basefee: read_bytes32(block_basefee_ptr), + cached: cached != 0, + chainid, + block_coinbase: read_bytes20(block_coinbase_ptr), + block_gas_limit, + block_number, + block_timestamp, + contract_address: read_bytes20(contract_address_ptr), + module_hash: read_bytes32(module_hash_ptr), + msg_sender: read_bytes20(msg_sender_ptr), + msg_value: read_bytes32(msg_value_ptr), + tx_gas_price: read_bytes32(tx_gas_price_ptr), + tx_origin: read_bytes20(tx_origin_ptr), + reentrant, + return_data_len: 0, + tracing: false, + }; + heapify(evm_data) as u64 +} diff --git a/arbitrator/wasm-libraries/user-host/src/program.rs b/arbitrator/wasm-libraries/user-host/src/program.rs new file mode 100644 index 0000000000..4199a691f7 --- /dev/null +++ b/arbitrator/wasm-libraries/user-host/src/program.rs @@ -0,0 +1,273 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use arbutil::{ + evm::{ + api::{EvmApiMethod, VecReader, EVM_API_METHOD_REQ_OFFSET}, + req::{EvmApiRequestor, RequestHandler}, + user::UserOutcomeKind, + EvmData, + }, + Color, +}; +use caller_env::{static_caller::STATIC_MEM, GuestPtr, MemAccess}; +use core::sync::atomic::{compiler_fence, Ordering}; +use eyre::{eyre, Result}; +use prover::programs::prelude::*; +use std::fmt::Display; +use user_host_trait::UserHost; +use wasmer_types::{Pages, WASM_PAGE_SIZE}; + +// allows introspection into user modules +#[link(wasm_import_module = "hostio")] +extern "C" { + fn program_memory_size(module: u32) -> u32; +} + +/// Signifies an out-of-bounds memory access was requested. +pub(crate) struct MemoryBoundsError; + +impl From for eyre::ErrReport { + fn from(_: MemoryBoundsError) -> Self { + eyre!("memory access out of bounds") + } +} + +/// The list of active programs. The current program is always the last. +/// +/// Note that this data-structure may re-alloc while references to [`Program`] are held. +/// This is sound due to [`Box`] providing a level of indirection. +/// +/// Normal Rust rules would suggest using a [`Vec`] of cells would be better. The issue is that, +/// should an error guard recover, this WASM will reset to an earlier state but with the current +/// memory. This means that stack unwinding won't happen, rendering these primitives unhelpful. +#[allow(clippy::vec_box)] +static mut PROGRAMS: Vec> = vec![]; + +static mut LAST_REQUEST_ID: u32 = 0x10000; + +#[derive(Clone)] +pub(crate) struct UserHostRequester { + data: Option>, + answer: Option<(Vec, VecReader, u64)>, + req_type: u32, + id: u32, +} + +impl UserHostRequester { + pub fn default() -> Self { + Self { + req_type: 0, + data: None, + answer: None, + id: 0, + } + } +} + +/// An active user program. +pub(crate) struct Program { + /// Arguments passed via the VM. + pub args: Vec, + /// Output generated by the program. + pub outs: Vec, + /// Mechanism for calling back into Geth. + pub evm_api: EvmApiRequestor, + /// EVM Context info. + pub evm_data: EvmData, + /// WAVM module index. + pub module: u32, + /// Call configuration. + pub config: StylusConfig, + /// Whether the program exited early. + pub early_exit: Option, +} + +#[link(wasm_import_module = "hostio")] +extern "C" { + fn program_request(status: u32) -> u32; +} + +impl UserHostRequester { + #[no_mangle] + pub unsafe fn set_response( + &mut self, + req_id: u32, + result: Vec, + raw_data: Vec, + gas: u64, + ) { + self.answer = Some((result, VecReader::new(raw_data), gas)); + if req_id != self.id { + panic!("bad req id returning from send_request") + } + compiler_fence(Ordering::SeqCst); + } + + pub unsafe fn set_request(&mut self, req_type: u32, data: &[u8]) -> u32 { + LAST_REQUEST_ID += 1; + self.id = LAST_REQUEST_ID; + self.req_type = req_type; + self.data = Some(data.to_vec()); + self.answer = None; + self.id + } + + pub unsafe fn get_request_meta(&self, id: u32) -> (u32, usize) { + if self.id != id { + panic!("get_request got wrong id"); + } + let size = self.data.as_ref().expect("no data get_request_meta").len(); + (self.req_type, size) + } + + pub unsafe fn take_request(&mut self, id: u32) -> (u32, Vec) { + if self.id != id { + panic!("get_request got wrong id"); + } + let data = self.data.take().expect("no request on take_request"); + (self.req_type, data) + } + + #[no_mangle] + unsafe fn send_request(&mut self, req_type: u32, data: Vec) -> (Vec, VecReader, u64) { + let req_id = self.set_request(req_type, &data); + compiler_fence(Ordering::SeqCst); + + let got_id = program_request(req_id); + compiler_fence(Ordering::SeqCst); + + if got_id != req_id { + panic!("bad req id returning from send_request") + } + self.answer.take().unwrap() + } +} + +impl RequestHandler for UserHostRequester { + fn request( + &mut self, + req_type: EvmApiMethod, + req_data: impl AsRef<[u8]>, + ) -> (Vec, VecReader, u64) { + unsafe { + self.send_request( + req_type as u32 + EVM_API_METHOD_REQ_OFFSET, + req_data.as_ref().to_vec(), + ) + } + } +} + +impl Program { + /// Adds a new program, making it current. + pub fn push_new(args: Vec, evm_data: EvmData, module: u32, config: StylusConfig) { + let program = Self { + args, + outs: vec![], + evm_api: EvmApiRequestor::new(UserHostRequester::default()), + evm_data, + module, + config, + early_exit: None, + }; + unsafe { PROGRAMS.push(Box::new(program)) } + } + + /// Removes the current program + pub fn pop() { + unsafe { + PROGRAMS.pop().expect("no program"); + } + } + + /// Provides a reference to the current program. + pub fn current() -> &'static mut Self { + unsafe { PROGRAMS.last_mut().expect("no program") } + } + + /// Reads the program's memory size in pages. + fn memory_size(&self) -> Pages { + unsafe { Pages(program_memory_size(self.module)) } + } + + /// Reads the program's memory size in bytes. + fn memory_size_bytes(&self) -> u64 { + self.memory_size().0 as u64 * WASM_PAGE_SIZE as u64 + } + + /// Provides the length of the program's calldata in bytes. + pub fn args_len(&self) -> usize { + self.args.len() + } + + /// Ensures an access is within bounds + fn check_memory_access(&self, ptr: GuestPtr, bytes: u32) -> Result<(), MemoryBoundsError> { + let end = ptr.to_u64() + bytes as u64; + if end > self.memory_size_bytes() { + return Err(MemoryBoundsError); + } + Ok(()) + } + + pub fn request_handler(&mut self) -> &mut UserHostRequester { + self.evm_api.request_handler() + } +} + +#[allow(clippy::unit_arg)] +impl UserHost for Program { + type Err = eyre::ErrReport; + type MemoryErr = MemoryBoundsError; + type A = EvmApiRequestor; + + fn args(&self) -> &[u8] { + &self.args + } + + fn outs(&mut self) -> &mut Vec { + &mut self.outs + } + + fn evm_api(&mut self) -> &mut Self::A { + &mut self.evm_api + } + + fn evm_data(&self) -> &EvmData { + &self.evm_data + } + + fn evm_return_data_len(&mut self) -> &mut u32 { + &mut self.evm_data.return_data_len + } + + fn read_slice(&self, ptr: GuestPtr, len: u32) -> Result, MemoryBoundsError> { + self.check_memory_access(ptr, len)?; + unsafe { Ok(STATIC_MEM.read_slice(ptr, len as usize)) } + } + + fn read_fixed(&self, ptr: GuestPtr) -> Result<[u8; N], MemoryBoundsError> { + self.read_slice(ptr, N as u32) + .map(|x| x.try_into().unwrap()) + } + + fn write_u32(&mut self, ptr: GuestPtr, x: u32) -> Result<(), MemoryBoundsError> { + self.check_memory_access(ptr, 4)?; + unsafe { Ok(STATIC_MEM.write_u32(ptr, x)) } + } + + fn write_slice(&self, ptr: GuestPtr, src: &[u8]) -> Result<(), MemoryBoundsError> { + self.check_memory_access(ptr, src.len() as u32)?; + unsafe { Ok(STATIC_MEM.write_slice(ptr, src)) } + } + + fn say(&self, text: D) { + println!("{} {text}", "Stylus says:".yellow()); + } + + fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], _end_ink: u64) { + let args = hex::encode(args); + let outs = hex::encode(outs); + println!("Error: unexpected hostio tracing info for {name} while proving: {args}, {outs}"); + } +} diff --git a/arbitrator/wasm-libraries/user-test/Cargo.toml b/arbitrator/wasm-libraries/user-test/Cargo.toml new file mode 100644 index 0000000000..aad9d8ec2e --- /dev/null +++ b/arbitrator/wasm-libraries/user-test/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "user-test" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +arbutil = { path = "../../arbutil/" } +caller-env = { path = "../../caller-env/", features = ["static_caller"] } +prover = { path = "../../prover/", default-features = false } +user-host-trait = { path = "../user-host-trait" } +eyre = "0.6.5" +fnv = "1.0.7" +hex = "0.4.3" +lazy_static = "1.4.0" +parking_lot = "0.12.1" diff --git a/arbitrator/wasm-libraries/user-test/src/host.rs b/arbitrator/wasm-libraries/user-test/src/host.rs new file mode 100644 index 0000000000..f2912eaae3 --- /dev/null +++ b/arbitrator/wasm-libraries/user-test/src/host.rs @@ -0,0 +1,238 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +use crate::program::Program; +use caller_env::GuestPtr; +use user_host_trait::UserHost; + +macro_rules! hostio { + ($($func:tt)*) => { + match Program::current().$($func)* { + Ok(value) => value, + Err(error) => panic!("{error}"), + } + }; +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__read_args(ptr: GuestPtr) { + hostio!(read_args(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__exit_early(status: u32) { + hostio!(exit_early(status)); +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__write_result(ptr: GuestPtr, len: u32) { + hostio!(write_result(ptr, len)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__storage_load_bytes32(key: GuestPtr, dest: GuestPtr) { + hostio!(storage_load_bytes32(key, dest)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__storage_cache_bytes32(key: GuestPtr, value: GuestPtr) { + hostio!(storage_cache_bytes32(key, value)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__storage_flush_cache(clear: u32) { + hostio!(storage_flush_cache(clear != 0)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__transient_load_bytes32(key: GuestPtr, dest: GuestPtr) { + hostio!(transient_load_bytes32(key, dest)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__transient_store_bytes32(key: GuestPtr, value: GuestPtr) { + hostio!(transient_store_bytes32(key, value)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__call_contract( + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + value: GuestPtr, + gas: u64, + ret_len: GuestPtr, +) -> u8 { + hostio!(call_contract(contract, data, data_len, value, gas, ret_len)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__delegate_call_contract( + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + gas: u64, + ret_len: GuestPtr, +) -> u8 { + hostio!(delegate_call_contract( + contract, data, data_len, gas, ret_len + )) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__static_call_contract( + contract: GuestPtr, + data: GuestPtr, + data_len: u32, + gas: u64, + ret_len: GuestPtr, +) -> u8 { + hostio!(static_call_contract(contract, data, data_len, gas, ret_len)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__create1( + code: GuestPtr, + code_len: u32, + value: GuestPtr, + contract: GuestPtr, + revert_len: GuestPtr, +) { + hostio!(create1(code, code_len, value, contract, revert_len)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__create2( + code: GuestPtr, + code_len: u32, + value: GuestPtr, + salt: GuestPtr, + contract: GuestPtr, + revert_len: GuestPtr, +) { + hostio!(create2(code, code_len, value, salt, contract, revert_len)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__read_return_data(dest: GuestPtr, offset: u32, size: u32) -> u32 { + hostio!(read_return_data(dest, offset, size)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__return_data_size() -> u32 { + hostio!(return_data_size()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__emit_log(data: GuestPtr, len: u32, topics: u32) { + hostio!(emit_log(data, len, topics)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__account_balance(address: GuestPtr, ptr: GuestPtr) { + hostio!(account_balance(address, ptr)) +} +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__account_code( + address: GuestPtr, + offset: u32, + size: u32, + dest: GuestPtr, +) -> u32 { + hostio!(account_code(address, offset, size, dest)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__account_code_size(address: GuestPtr) -> u32 { + hostio!(account_code_size(address)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__account_codehash(address: GuestPtr, ptr: GuestPtr) { + hostio!(account_codehash(address, ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__block_basefee(ptr: GuestPtr) { + hostio!(block_basefee(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__block_coinbase(ptr: GuestPtr) { + hostio!(block_coinbase(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__block_gas_limit() -> u64 { + hostio!(block_gas_limit()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__block_number() -> u64 { + hostio!(block_number()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__block_timestamp() -> u64 { + hostio!(block_timestamp()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__chainid() -> u64 { + hostio!(chainid()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__contract_address(ptr: GuestPtr) { + hostio!(contract_address(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__evm_gas_left() -> u64 { + hostio!(evm_gas_left()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__evm_ink_left() -> u64 { + hostio!(evm_ink_left()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__msg_reentrant() -> u32 { + hostio!(msg_reentrant()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__msg_sender(ptr: GuestPtr) { + hostio!(msg_sender(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__msg_value(ptr: GuestPtr) { + hostio!(msg_value(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__native_keccak256(input: GuestPtr, len: u32, output: GuestPtr) { + hostio!(native_keccak256(input, len, output)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__tx_gas_price(ptr: GuestPtr) { + hostio!(tx_gas_price(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__tx_ink_price() -> u32 { + hostio!(tx_ink_price()) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__tx_origin(ptr: GuestPtr) { + hostio!(tx_origin(ptr)) +} + +#[no_mangle] +pub unsafe extern "C" fn vm_hooks__pay_for_memory_grow(pages: u16) { + hostio!(pay_for_memory_grow(pages)) +} diff --git a/arbitrator/wasm-libraries/user-test/src/ink.rs b/arbitrator/wasm-libraries/user-test/src/ink.rs new file mode 100644 index 0000000000..fca658e59b --- /dev/null +++ b/arbitrator/wasm-libraries/user-test/src/ink.rs @@ -0,0 +1,38 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{program::Program, CONFIG}; +use prover::programs::{ + config::PricingParams, + prelude::{GasMeteredMachine, MachineMeter, MeteredMachine}, +}; + +#[link(wasm_import_module = "hostio")] +extern "C" { + fn user_ink_left() -> u64; + fn user_ink_status() -> u32; + fn user_set_ink(ink: u64, status: u32); +} + +impl MeteredMachine for Program { + fn ink_left(&self) -> MachineMeter { + unsafe { + match user_ink_status() { + 0 => MachineMeter::Ready(user_ink_left()), + _ => MachineMeter::Exhausted, + } + } + } + + fn set_meter(&mut self, meter: MachineMeter) { + unsafe { + user_set_ink(meter.ink(), meter.status()); + } + } +} + +impl GasMeteredMachine for Program { + fn pricing(&self) -> PricingParams { + unsafe { CONFIG.unwrap().pricing } + } +} diff --git a/arbitrator/wasm-libraries/user-test/src/lib.rs b/arbitrator/wasm-libraries/user-test/src/lib.rs new file mode 100644 index 0000000000..ffb8d4a287 --- /dev/null +++ b/arbitrator/wasm-libraries/user-test/src/lib.rs @@ -0,0 +1,55 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +#![allow(clippy::missing_safety_doc)] + +use arbutil::{evm::EvmData, Bytes32}; +use fnv::FnvHashMap as HashMap; +use lazy_static::lazy_static; +use parking_lot::Mutex; +use prover::programs::prelude::StylusConfig; + +pub mod host; +mod ink; +mod program; + +pub(crate) static mut ARGS: Vec = vec![]; +pub(crate) static mut OUTS: Vec = vec![]; +pub(crate) static mut LOGS: Vec> = vec![]; +pub(crate) static mut CONFIG: Option = None; +pub(crate) static mut OPEN_PAGES: u16 = 0; +pub(crate) static mut EVER_PAGES: u16 = 0; + +lazy_static! { + static ref KEYS: Mutex> = Mutex::new(HashMap::default()); + static ref EVM_DATA: EvmData = EvmData::default(); +} + +#[no_mangle] +pub unsafe extern "C" fn user_test__prepare( + len: usize, + version: u16, + max_depth: u32, + ink_price: u32, +) -> *const u8 { + let config = StylusConfig::new(version, max_depth, ink_price); + CONFIG = Some(config); + ARGS = vec![0; len]; + ARGS.as_ptr() +} + +#[no_mangle] +pub unsafe extern "C" fn user_test__set_pages(pages: u16) { + OPEN_PAGES = OPEN_PAGES.saturating_add(pages); + EVER_PAGES = EVER_PAGES.max(OPEN_PAGES); +} + +#[no_mangle] +pub unsafe extern "C" fn user_test__get_outs_ptr() -> *const u8 { + OUTS.as_ptr() +} + +#[no_mangle] +pub unsafe extern "C" fn user_test__get_outs_len() -> usize { + OUTS.len() +} diff --git a/arbitrator/wasm-libraries/user-test/src/program.rs b/arbitrator/wasm-libraries/user-test/src/program.rs new file mode 100644 index 0000000000..c56ea52ad0 --- /dev/null +++ b/arbitrator/wasm-libraries/user-test/src/program.rs @@ -0,0 +1,220 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use crate::{ARGS, EVER_PAGES, EVM_DATA, KEYS, LOGS, OPEN_PAGES, OUTS}; +use arbutil::{ + evm::{ + api::{EvmApi, VecReader}, + user::UserOutcomeKind, + EvmData, + }, + Bytes20, Bytes32, Color, +}; +use caller_env::{static_caller::STATIC_MEM, GuestPtr, MemAccess}; +use eyre::{eyre, Result}; +use prover::programs::memory::MemoryModel; +use std::fmt::Display; +use user_host_trait::UserHost; + +/// Signifies an out-of-bounds memory access was requested. +pub struct MemoryBoundsError; + +impl From for eyre::ErrReport { + fn from(_: MemoryBoundsError) -> Self { + eyre!("memory access out of bounds") + } +} + +/// Mock type representing a `user_host::Program` +pub struct Program { + evm_api: MockEvmApi, +} + +#[allow(clippy::unit_arg)] +impl UserHost for Program { + type Err = eyre::ErrReport; + type MemoryErr = MemoryBoundsError; + type A = MockEvmApi; + + fn args(&self) -> &[u8] { + unsafe { &ARGS } + } + + fn outs(&mut self) -> &mut Vec { + unsafe { &mut OUTS } + } + + fn evm_api(&mut self) -> &mut Self::A { + &mut self.evm_api + } + + fn evm_data(&self) -> &EvmData { + &EVM_DATA + } + + fn evm_return_data_len(&mut self) -> &mut u32 { + unimplemented!() + } + + fn read_slice(&self, ptr: GuestPtr, len: u32) -> Result, MemoryBoundsError> { + self.check_memory_access(ptr, len)?; + unsafe { Ok(STATIC_MEM.read_slice(ptr, len as usize)) } + } + + fn read_fixed(&self, ptr: GuestPtr) -> Result<[u8; N], MemoryBoundsError> { + self.read_slice(ptr, N as u32) + .map(|x| x.try_into().unwrap()) + } + + fn write_u32(&mut self, ptr: GuestPtr, x: u32) -> Result<(), MemoryBoundsError> { + self.check_memory_access(ptr, 4)?; + unsafe { Ok(STATIC_MEM.write_u32(ptr, x)) } + } + + fn write_slice(&self, ptr: GuestPtr, src: &[u8]) -> Result<(), MemoryBoundsError> { + self.check_memory_access(ptr, src.len() as u32)?; + unsafe { Ok(STATIC_MEM.write_slice(ptr, src)) } + } + + fn say(&self, text: D) { + println!("{} {text}", "Stylus says:".yellow()); + } + + fn trace(&mut self, name: &str, args: &[u8], outs: &[u8], _end_ink: u64) { + let args = hex::encode(args); + let outs = hex::encode(outs); + println!("Error: unexpected hostio tracing info for {name} while proving: {args}, {outs}"); + } +} + +impl Program { + pub fn current() -> Self { + Self { + evm_api: MockEvmApi, + } + } + + fn check_memory_access(&self, _ptr: GuestPtr, _bytes: u32) -> Result<(), MemoryBoundsError> { + Ok(()) // pretend we did a check + } +} + +pub struct MockEvmApi; + +impl EvmApi for MockEvmApi { + fn get_bytes32(&mut self, key: Bytes32) -> (Bytes32, u64) { + let value = KEYS.lock().get(&key).cloned().unwrap_or_default(); + (value, 2100) // pretend worst case + } + + fn cache_bytes32(&mut self, key: Bytes32, value: Bytes32) -> u64 { + KEYS.lock().insert(key, value); + 0 + } + + fn flush_storage_cache(&mut self, _clear: bool, _gas_left: u64) -> Result { + Ok(22100 * KEYS.lock().len() as u64) // pretend worst case + } + + fn get_transient_bytes32(&mut self, _key: Bytes32) -> Bytes32 { + unimplemented!() + } + + fn set_transient_bytes32(&mut self, _key: Bytes32, _value: Bytes32) -> Result<()> { + unimplemented!() + } + + /// Simulates a contract call. + /// Note: this call function is for testing purposes only and deviates from onchain behavior. + fn contract_call( + &mut self, + _contract: Bytes20, + _calldata: &[u8], + _gas_left: u64, + _gas_req: u64, + _value: Bytes32, + ) -> (u32, u64, UserOutcomeKind) { + unimplemented!() + } + + fn delegate_call( + &mut self, + _contract: Bytes20, + _calldata: &[u8], + _gas_left: u64, + _gas_req: u64, + ) -> (u32, u64, UserOutcomeKind) { + unimplemented!() + } + + fn static_call( + &mut self, + _contract: Bytes20, + _calldata: &[u8], + _gas_left: u64, + _gas_req: u64, + ) -> (u32, u64, UserOutcomeKind) { + unimplemented!() + } + + fn create1( + &mut self, + _code: Vec, + _endowment: Bytes32, + _gas: u64, + ) -> (Result, u32, u64) { + unimplemented!() + } + + fn create2( + &mut self, + _code: Vec, + _endowment: Bytes32, + _salt: Bytes32, + _gas: u64, + ) -> (Result, u32, u64) { + unimplemented!() + } + + fn get_return_data(&self) -> VecReader { + unimplemented!() + } + + fn emit_log(&mut self, data: Vec, _topics: u32) -> Result<()> { + unsafe { LOGS.push(data) }; + Ok(()) + } + + fn account_balance(&mut self, _address: Bytes20) -> (Bytes32, u64) { + unimplemented!() + } + + fn account_code(&mut self, _address: Bytes20, _gas_left: u64) -> (VecReader, u64) { + unimplemented!() + } + + fn account_codehash(&mut self, _address: Bytes20) -> (Bytes32, u64) { + unimplemented!() + } + + fn add_pages(&mut self, pages: u16) -> u64 { + let model = MemoryModel::new(2, 1000); + unsafe { + let (open, ever) = (OPEN_PAGES, EVER_PAGES); + OPEN_PAGES = OPEN_PAGES.saturating_add(pages); + EVER_PAGES = EVER_PAGES.max(OPEN_PAGES); + model.gas_cost(pages, open, ever) + } + } + + fn capture_hostio( + &mut self, + _name: &str, + _args: &[u8], + _outs: &[u8], + _start_ink: u64, + _end_ink: u64, + ) { + unimplemented!() + } +} diff --git a/arbitrator/wasm-libraries/wasi-stub/Cargo.toml b/arbitrator/wasm-libraries/wasi-stub/Cargo.toml index ebba324fe5..698c1e0f21 100644 --- a/arbitrator/wasm-libraries/wasi-stub/Cargo.toml +++ b/arbitrator/wasm-libraries/wasi-stub/Cargo.toml @@ -8,3 +8,6 @@ publish = false crate-type = ["cdylib"] [dependencies] +paste = { version = "1.0.14" } +caller-env = { path = "../../caller-env/", default-features = false, features = ["static_caller"] } +wee_alloc = "0.4.2" diff --git a/arbitrator/wasm-libraries/wasi-stub/src/lib.rs b/arbitrator/wasm-libraries/wasi-stub/src/lib.rs index d10b708071..2f237dcb4f 100644 --- a/arbitrator/wasm-libraries/wasi-stub/src/lib.rs +++ b/arbitrator/wasm-libraries/wasi-stub/src/lib.rs @@ -1,17 +1,20 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +#![allow(clippy::missing_safety_doc)] // TODO: require safety docs #![no_std] -const ERRNO_BADF: u16 = 8; -const ERRNO_INTVAL: u16 = 28; +use caller_env::{self, wasip1_stub::Errno, GuestPtr}; +use paste::paste; +use wee_alloc::WeeAlloc; -#[allow(dead_code)] extern "C" { - fn wavm_caller_load8(ptr: usize) -> u8; - fn wavm_caller_load32(ptr: usize) -> u32; - fn wavm_caller_store8(ptr: usize, val: u8); - fn wavm_caller_store32(ptr: usize, val: u32); fn wavm_halt_and_set_finished() -> !; } +#[global_allocator] +static ALLOC: WeeAlloc = WeeAlloc::INIT; + #[panic_handler] unsafe fn panic(_: &core::panic::PanicInfo) -> ! { core::arch::wasm32::unreachable() @@ -26,89 +29,157 @@ pub unsafe extern "C" fn wasi_snapshot_preview1__proc_exit(code: u32) -> ! { } } -#[no_mangle] -pub unsafe extern "C" fn env__exit(code: u32) { - if code == 0 { - wavm_halt_and_set_finished() - } else { - core::arch::wasm32::unreachable() - } +macro_rules! wrap { + ($(fn $func_name:ident ($($arg_name:ident : $arg_type:ty),* ) -> $return_type:ty);*) => { + paste! { + $( + #[no_mangle] + pub unsafe extern "C" fn []($($arg_name : $arg_type),*) -> $return_type { + caller_env::wasip1_stub::$func_name( + &mut caller_env::static_caller::STATIC_MEM, + &mut caller_env::static_caller::STATIC_ENV, + $($arg_name),* + ) + } + )* + } + }; } -#[no_mangle] -pub unsafe extern "C" fn wasi_snapshot_preview1__environ_sizes_get( - length_ptr: usize, - data_size_ptr: usize, -) -> u16 { - wavm_caller_store32(length_ptr, 0); - wavm_caller_store32(data_size_ptr, 0); - 0 -} +wrap! { + fn clock_time_get( + clock_id: u32, + precision: u64, + time_ptr: GuestPtr + ) -> Errno; -#[no_mangle] -pub unsafe extern "C" fn wasi_snapshot_preview1__fd_write( - fd: usize, - iovecs_ptr: usize, - iovecs_len: usize, - ret_ptr: usize, -) -> u16 { - if fd != 1 && fd != 2 { - return ERRNO_BADF; - } - let mut size = 0; - for i in 0..iovecs_len { - let ptr = iovecs_ptr + i * 8; - size += wavm_caller_load32(ptr + 4); - } - wavm_caller_store32(ret_ptr, size); - 0 -} + fn random_get(buf: GuestPtr, len: u32) -> Errno; -#[no_mangle] -pub unsafe extern "C" fn wasi_snapshot_preview1__environ_get(_: usize, _: usize) -> u16 { - ERRNO_INTVAL -} + fn environ_sizes_get(length_ptr: GuestPtr, data_size_ptr: GuestPtr) -> Errno; -#[no_mangle] -pub unsafe extern "C" fn wasi_snapshot_preview1__fd_close(_: usize) -> u16 { - ERRNO_BADF -} + fn fd_write( + fd: u32, + iovecs_ptr: GuestPtr, + iovecs_len: u32, + ret_ptr: GuestPtr + ) -> Errno; -#[no_mangle] -pub unsafe extern "C" fn wasi_snapshot_preview1__fd_read( - _: usize, - _: usize, - _: usize, - _: usize, -) -> u16 { - ERRNO_BADF -} + fn environ_get(a: GuestPtr, b: GuestPtr) -> Errno; -#[no_mangle] -pub unsafe extern "C" fn wasi_snapshot_preview1__path_open( - _: usize, - _: usize, - _: usize, - _: usize, - _: usize, - _: u64, - _: u64, - _: usize, - _: usize, -) -> u16 { - ERRNO_BADF -} + fn fd_close(fd: u32) -> Errno; + fn fd_read(a: u32, b: u32, c: u32, d: u32) -> Errno; + fn fd_readdir( + fd: u32, + a: u32, + b: u32, + c: u64, + d: u32 + ) -> Errno; -#[no_mangle] -pub unsafe extern "C" fn wasi_snapshot_preview1__fd_prestat_get(_: usize, _: usize) -> u16 { - ERRNO_BADF -} + fn fd_sync(a: u32) -> Errno; -#[no_mangle] -pub unsafe extern "C" fn wasi_snapshot_preview1__fd_prestat_dir_name( - _: usize, - _: usize, - _: usize, -) -> u16 { - ERRNO_BADF + fn fd_seek( + fd: u32, + offset: u64, + whence: u8, + filesize: u32 + ) -> Errno; + + fn fd_datasync(fd: u32) -> Errno; + + fn path_open( + a: u32, + b: u32, + c: u32, + d: u32, + e: u32, + f: u64, + g: u64, + h: u32, + i: u32 + ) -> Errno; + + fn path_create_directory( + a: u32, + b: u32, + c: u32 + ) -> Errno; + + fn path_remove_directory( + a: u32, + b: u32, + c: u32 + ) -> Errno; + + fn path_readlink( + a: u32, + b: u32, + c: u32, + d: u32, + e: u32, + f: u32 + ) -> Errno; + + fn path_rename( + a: u32, + b: u32, + c: u32, + d: u32, + e: u32, + f: u32 + ) -> Errno; + + fn path_filestat_get( + a: u32, + b: u32, + c: u32, + d: u32, + e: u32 + ) -> Errno; + + fn path_unlink_file(a: u32, b: u32, c: u32) -> Errno; + + fn fd_prestat_get(a: u32, b: u32) -> Errno; + fn fd_prestat_dir_name(a: u32, b: u32, c: u32) -> Errno; + + fn fd_filestat_get(fd: u32, filestat: u32) -> Errno; + fn fd_filestat_set_size(fd: u32, size: u64) -> Errno; + + fn fd_pread( + fd: u32, + a: u32, + b: u32, + c: u64, + d: u32 + ) -> Errno; + + fn fd_pwrite( + fd: u32, + a: u32, + b: u32, + c: u64, + d: u32 + ) -> Errno; + + fn sock_accept(fd: u32, a: u32, b: u32) -> Errno; + fn sock_shutdown(a: u32, b: u32) -> Errno; + + fn sched_yield() -> Errno; + + fn args_sizes_get( + length_ptr: GuestPtr, + data_size_ptr: GuestPtr + ) -> Errno; + + fn args_get(argv_buf: GuestPtr, data_buf: GuestPtr) -> Errno; + + fn fd_fdstat_get(a: u32, b: u32) -> Errno; + fn fd_fdstat_set_flags(a: u32, b: u32) -> Errno; + + fn poll_oneoff( + in_subs: GuestPtr, + out_evt: GuestPtr, + nsubscriptions: u32, + nevents_ptr: GuestPtr + ) -> Errno } diff --git a/arbitrator/wasm-testsuite/Cargo.lock b/arbitrator/wasm-testsuite/Cargo.lock index 60e48adf1f..c6f946b8ea 100644 --- a/arbitrator/wasm-testsuite/Cargo.lock +++ b/arbitrator/wasm-testsuite/Cargo.lock @@ -2,6 +2,32 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.0", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "ansi_term" version = "0.12.1" @@ -11,6 +37,14 @@ dependencies = [ "winapi", ] +[[package]] +name = "arbutil" +version = "0.1.0" +dependencies = [ + "sha3 0.10.6", + "siphasher", +] + [[package]] name = "arrayvec" version = "0.7.2" @@ -34,6 +68,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "backtrace" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + [[package]] name = "bincode" version = "1.3.3" @@ -59,6 +108,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + [[package]] name = "block-padding" version = "0.2.1" @@ -85,6 +143,45 @@ dependencies = [ "libc", ] +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "bytecheck" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d11cac2c12b5adc6570dad2ee1b87eff4955dac476fe12d81e5fdd352e52406f" +dependencies = [ + "bytecheck_derive", + "ptr_meta", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13e576ebe98e605500b3c8041bb888e966653577172df6dd97398714eb30b9bf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + [[package]] name = "cc" version = "1.0.73" @@ -112,6 +209,85 @@ dependencies = [ "vec_map", ] +[[package]] +name = "corosensei" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9847f90f32a50b0dcbd68bc23ff242798b13080b97b0569f6ed96a45ce4cf2cd" +dependencies = [ + "autocfg", + "cfg-if", + "libc", + "scopeguard", + "windows-sys 0.33.0", +] + +[[package]] +name = "cranelift-bforest" +version = "0.86.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "529ffacce2249ac60edba2941672dfedf3d96558b415d0d8083cd007456e0f55" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-codegen" +version = "0.86.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427d105f617efc8cb55f8d036a7fded2e227892d8780b4985e5551f8d27c4a92" +dependencies = [ + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", + "cranelift-isle", + "gimli 0.26.2", + "log", + "regalloc2", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.86.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "551674bed85b838d45358e3eab4f0ffaa6790c70dc08184204b9a54b41cdb7d1" +dependencies = [ + "cranelift-codegen-shared", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.86.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b3a63ae57498c3eb495360944a33571754241e15e47e3bcae6082f40fec5866" + +[[package]] +name = "cranelift-entity" +version = "0.86.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11aa8aa624c72cc1c94ea3d0739fa61248260b5b14d3646f51593a88d67f3e6e" + +[[package]] +name = "cranelift-frontend" +version = "0.86.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "544ee8f4d1c9559c9aa6d46e7aaeac4a13856d620561094f35527356c7d21bd0" +dependencies = [ + "cranelift-codegen", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.86.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed16b14363d929b8c37e3c557d0a7396791b383ecc302141643c054343170aad" + [[package]] name = "crossbeam-channel" version = "0.5.4" @@ -157,14 +333,34 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "darling" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +dependencies = [ + "darling_core 0.14.2", + "darling_macro 0.14.2", ] [[package]] @@ -181,13 +377,37 @@ dependencies = [ "syn", ] +[[package]] +name = "darling_core" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core", + "darling_core 0.13.4", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +dependencies = [ + "darling_core 0.14.2", "quote", "syn", ] @@ -201,12 +421,89 @@ dependencies = [ "generic-array", ] +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.3", + "crypto-common", +] + +[[package]] +name = "dynasm" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" +dependencies = [ + "bitflags", + "byteorder", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dynasmrt" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" +dependencies = [ + "byteorder", + "dynasm", + "memmap2", +] + [[package]] name = "either" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "enum-iterator" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enumset" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" +dependencies = [ + "darling 0.14.2", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "eyre" version = "0.6.8" @@ -217,12 +514,27 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.5" @@ -233,12 +545,49 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +dependencies = [ + "fallible-iterator", + "indexmap", + "stable_deref_trait", +] + +[[package]] +name = "gimli" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" + [[package]] name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + [[package]] name = "heck" version = "0.3.3" @@ -282,7 +631,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.11.2", ] [[package]] @@ -291,6 +640,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "keccak" version = "0.1.0" @@ -303,11 +661,45 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "leb128" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" + [[package]] name = "libc" -version = "0.2.125" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "mach" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] [[package]] name = "memchr" @@ -315,6 +707,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memmap2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +dependencies = [ + "libc", +] + [[package]] name = "memoffset" version = "0.6.5" @@ -330,6 +731,21 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "more-asserts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" + [[package]] name = "nom" version = "7.1.1" @@ -438,17 +854,55 @@ dependencies = [ ] [[package]] -name = "once_cell" -version = "1.10.0" +name = "object" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "239da7f290cfa979f43f85a8efeee9a8a76d0827c356d37f9d3d7254d6b537fb" +dependencies = [ + "memchr", +] [[package]] -name = "opaque-debug" +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.42.0", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -486,27 +940,54 @@ dependencies = [ name = "prover" version = "0.1.0" dependencies = [ + "arbutil", "bincode", "brotli2", - "digest", + "digest 0.9.0", "eyre", "fnv", "hex", + "lazy_static", "libc", "nom", "nom-leb128", "num", + "parking_lot", "rayon", "rustc-demangle", "serde", "serde_json", "serde_with", - "sha3", + "sha3 0.9.1", + "smallvec", "static_assertions", "structopt", + "wasmer", + "wasmer-compiler-singlepass", + "wasmer-types", "wasmparser", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "quote" version = "1.0.18" @@ -540,6 +1021,74 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regalloc2" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d43a209257d978ef079f3d446331d0f1794f5e0fc19b306a199983857833a779" +dependencies = [ + "fxhash", + "log", + "slice-group-by", + "smallvec", +] + +[[package]] +name = "region" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" +dependencies = [ + "bitflags", + "libc", + "mach", + "winapi", +] + +[[package]] +name = "rend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cec2b3485b07d96ddfd3134767b8a447b45ea4eb91448d0a35180ec0ffd5ed15" +dependencies = [ + "bytecheck", + "hashbrown 0.12.3", + "indexmap", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eaedadc88b53e36dd32d940ed21ae4d850d5916f2581526921f553a72ac34c4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "rustc-demangle" version = "0.1.21" @@ -564,6 +1113,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + [[package]] name = "serde" version = "1.0.137" @@ -573,6 +1128,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-wasm-bindgen" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b4c031cd0d9014307d82b8abf653c0290fbdaeb4c02d00c63cf52f728628bf" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "serde_derive" version = "1.0.137" @@ -612,7 +1178,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ - "darling", + "darling 0.13.4", "proc-macro2", "quote", "syn", @@ -624,12 +1190,49 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.9.0", + "digest 0.9.0", "keccak", "opaque-debug", ] +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + +[[package]] +name = "siphasher" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" + +[[package]] +name = "slice-group-by" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b634d87b960ab1a38c4fe143b508576f075e7c978bfad18217645ebfdfa2ec" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +dependencies = [ + "serde", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -683,6 +1286,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "target-lexicon" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" + [[package]] name = "textwrap" version = "0.11.0" @@ -692,6 +1301,58 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +dependencies = [ + "cfg-if", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + [[package]] name = "typenum" version = "1.15.0" @@ -728,10 +1389,103 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-downcast" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dac026d43bcca6e7ce1c0956ba68f59edf6403e8e930a5d891be72c31a44340" +dependencies = [ + "js-sys", + "once_cell", + "wasm-bindgen", + "wasm-bindgen-downcast-macros", +] + +[[package]] +name = "wasm-bindgen-downcast-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5020cfa87c7cecefef118055d44e3c1fc122c7ec25701d528ee458a0b45f38f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "wasm-encoder" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05632e0a66a6ed8cca593c24223aabd6262f256c3693ad9822c315285f010614" +dependencies = [ + "leb128", +] + [[package]] name = "wasm-testsuite" version = "0.1.0" dependencies = [ + "arbutil", "eyre", "hex", "prover", @@ -740,13 +1494,156 @@ dependencies = [ "structopt", ] +[[package]] +name = "wasmer" +version = "3.1.0" +dependencies = [ + "bytes", + "cfg-if", + "indexmap", + "js-sys", + "more-asserts", + "serde", + "serde-wasm-bindgen", + "target-lexicon", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-downcast", + "wasmer-compiler", + "wasmer-compiler-cranelift", + "wasmer-derive", + "wasmer-types", + "wasmer-vm", + "wat", + "winapi", +] + +[[package]] +name = "wasmer-compiler" +version = "3.1.0" +dependencies = [ + "backtrace", + "cfg-if", + "enum-iterator", + "enumset", + "lazy_static", + "leb128", + "memmap2", + "more-asserts", + "region", + "rustc-demangle", + "smallvec", + "thiserror", + "wasmer-types", + "wasmer-vm", + "wasmparser", + "winapi", +] + +[[package]] +name = "wasmer-compiler-cranelift" +version = "3.1.0" +dependencies = [ + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "gimli 0.26.2", + "more-asserts", + "rayon", + "smallvec", + "target-lexicon", + "tracing", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-compiler-singlepass" +version = "3.1.0" +dependencies = [ + "byteorder", + "dynasm", + "dynasmrt", + "enumset", + "gimli 0.26.2", + "lazy_static", + "more-asserts", + "rayon", + "smallvec", + "wasmer-compiler", + "wasmer-types", +] + +[[package]] +name = "wasmer-derive" +version = "3.1.0" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "wasmer-types" +version = "3.1.0" +dependencies = [ + "enum-iterator", + "enumset", + "indexmap", + "more-asserts", + "rkyv", + "target-lexicon", + "thiserror", +] + +[[package]] +name = "wasmer-vm" +version = "3.1.0" +dependencies = [ + "backtrace", + "cc", + "cfg-if", + "corosensei", + "enum-iterator", + "indexmap", + "lazy_static", + "libc", + "mach", + "memoffset", + "more-asserts", + "region", + "scopeguard", + "thiserror", + "wasmer-types", + "winapi", +] + [[package]] name = "wasmparser" -version = "0.84.0" +version = "0.83.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77dc97c22bb5ce49a47b745bed8812d30206eff5ef3af31424f2c1820c0974b2" +checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" + +[[package]] +name = "wast" +version = "50.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2cbb59d4ac799842791fe7e806fa5dbbf6b5554d538e51cc8e176db6ff0ae34" dependencies = [ - "indexmap", + "leb128", + "memchr", + "unicode-width", + "wasm-encoder", +] + +[[package]] +name = "wat" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "584aaf7a1ecf4d383bbe1a25eeab0cbb8ff96acc6796707ff65cde48f4632f15" +dependencies = [ + "wast", ] [[package]] @@ -770,3 +1667,103 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" +dependencies = [ + "windows_aarch64_msvc 0.33.0", + "windows_i686_gnu 0.33.0", + "windows_i686_msvc 0.33.0", + "windows_x86_64_gnu 0.33.0", + "windows_x86_64_msvc 0.33.0", +] + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" diff --git a/arbitrator/wasm-testsuite/Cargo.toml b/arbitrator/wasm-testsuite/Cargo.toml index 5ace2ca584..b24570ab55 100644 --- a/arbitrator/wasm-testsuite/Cargo.toml +++ b/arbitrator/wasm-testsuite/Cargo.toml @@ -6,6 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +arbutil = { path = "../arbutil" } prover = { path = "../prover" } structopt = "0.3.23" serde = { version = "1.0.130", features = ["derive", "rc"] } diff --git a/arbitrator/wasm-testsuite/check.sh b/arbitrator/wasm-testsuite/check.sh index 9c67557dc8..a32e084655 100755 --- a/arbitrator/wasm-testsuite/check.sh +++ b/arbitrator/wasm-testsuite/check.sh @@ -18,7 +18,7 @@ cargo build --release for file in tests/*.json; do base="${file#tests/}" name="${base%.wasm}" - target/release/wasm-testsuite $name & + nice target/release/wasm-testsuite $name & done wait diff --git a/arbitrator/wasm-testsuite/src/main.rs b/arbitrator/wasm-testsuite/src/main.rs index 4ff511d9de..2144dcf992 100644 --- a/arbitrator/wasm-testsuite/src/main.rs +++ b/arbitrator/wasm-testsuite/src/main.rs @@ -1,9 +1,9 @@ // Copyright 2022, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -use eyre::bail; +use arbutil::Color; +use eyre::{bail, ErrReport}; use prover::{ - console::Color, machine, machine::{GlobalState, Machine, MachineStatus, ProofInfo}, value::Value, @@ -11,6 +11,7 @@ use prover::{ use serde::{Deserialize, Serialize}; use std::{ collections::{HashMap, HashSet}, + convert::TryInto, fs::File, io::BufReader, path::PathBuf, @@ -54,6 +55,7 @@ enum Command { }, AssertInvalid {}, AssertUninstantiable {}, + Register {}, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -67,7 +69,7 @@ enum Action { struct TextValue { #[serde(rename = "type")] ty: TextValueType, - value: String, + value: TextValueData, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -77,52 +79,74 @@ enum TextValueType { I64, F32, F64, + V128, + Funcref, + Externref, } -impl Into for TextValue { - fn into(self) -> Value { - match self.ty { - TextValueType::I32 => { - let value = self.value.parse().expect("not an i32"); +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +enum TextValueData { + String(String), + Array(Vec), +} + +impl TryInto for TextValue { + type Error = ErrReport; + + fn try_into(self) -> eyre::Result { + let TextValueData::String(value) = self.value else { + bail!("array-expressed values not supported"); + }; + + use TextValueType::*; + Ok(match self.ty { + I32 => { + let value = value.parse().expect("not an i32"); Value::I32(value) } - TextValueType::I64 => { - let value = self.value.parse().expect("not an i64"); + I64 => { + let value = value.parse().expect("not an i64"); Value::I64(value) } - TextValueType::F32 => { - if self.value.contains("nan") { - return Value::F32(f32::NAN); + F32 => { + if value.contains("nan") { + return Ok(Value::F32(f32::NAN)); } - let message = format!("{} not the bit representation of an f32", self.value); - let bits: u32 = self.value.parse().expect(&message); + let message = format!("{} not the bit representation of an f32", value); + let bits: u32 = value.parse().expect(&message); Value::F32(f32::from_bits(bits)) } - TextValueType::F64 => { - if self.value.contains("nan") { - return Value::F64(f64::NAN); + F64 => { + if value.contains("nan") { + return Ok(Value::F64(f64::NAN)); } - let message = format!("{} not the bit representation of an f64", self.value); - let bits: u64 = self.value.parse().expect(&message); + let message = format!("{} not the bit representation of an f64", value); + let bits: u64 = value.parse().expect(&message); Value::F64(f64::from_bits(bits)) } - } + x @ (V128 | Funcref | Externref) => bail!("not supported {:?}", x), + }) } } impl PartialEq for TextValue { fn eq(&self, other: &Value) -> bool { - if &Into::::into(self.clone()) == other { + if &TryInto::::try_into(self.clone()).unwrap() == other { return true; } + let TextValueData::String(text_value) = &self.value else { + panic!("array-expressed values not supported"); + }; + match self.ty { TextValueType::F32 => match other { - Value::F32(value) => value.is_nan() && self.value.contains("nan"), + Value::F32(value) => value.is_nan() && text_value.contains("nan"), _ => false, }, TextValueType::F64 => match other { - Value::F64(value) => value.is_nan() && self.value.contains("nan"), + Value::F64(value) => value.is_nan() && text_value.contains("nan"), _ => false, }, _ => false, @@ -166,13 +190,33 @@ fn main() -> eyre::Result<()> { do_not_prove.insert(PathBuf::from("float_exprs.json")); let export_proofs = !do_not_prove.contains(&opts.json); if !export_proofs { - println!("{}", Color::grey("skipping OSP proof generation")); + println!("{}", "skipping OSP proof generation".grey()); + } + + fn setup<'a>( + machine: &'a mut Option, + func: &str, + args: Vec, + file: &str, + ) -> &'a mut Machine { + let Some(machine) = machine.as_mut() else { + panic!("no machine {} {}", file.red(), func.red()) + }; + let main = machine.main_module_name(); + let (module, func) = machine.find_module_func(&main, func).unwrap(); + machine.jump_into_func(module, func, args); + machine + } + + fn to_values(text: Vec) -> eyre::Result> { + text.into_iter().map(TryInto::try_into).collect() } let mut wasmfile = String::new(); let mut machine = None; let mut subtest = 0; let mut skip = false; + let mut has_skipped = false; macro_rules! run { ($machine:expr, $bound:expr, $path:expr, $prove:expr) => {{ @@ -199,7 +243,7 @@ fn main() -> eyre::Result<()> { leap *= leap + 1; if leap > 6 { let message = format!("backing off {} {} {}", leap, count, $bound); - println!("{}", Color::grey(message)); + println!("{}", message.grey()); $machine.stop_merkle_caching(); } } @@ -219,7 +263,7 @@ fn main() -> eyre::Result<()> { Action::Invoke { field, args } => (field, args), Action::Get { .. } => { // get() is only used in the export test, which we don't support - println!("skipping unsupported action {}", Color::red("get")); + println!("skipping unsupported action {}", "get".red()); continue; } } @@ -234,30 +278,33 @@ fn main() -> eyre::Result<()> { }; } - for (index, command) in case.commands.into_iter().enumerate() { + 'next: for (index, command) in case.commands.into_iter().enumerate() { + // each iteration represets a test case + macro_rules! test_success { ($func:expr, $args:expr, $expected:expr) => { - let args: Vec<_> = $args.into_iter().map(Into::into).collect(); + let args = match to_values($args) { + Ok(args) => args, + Err(_) => continue, // TODO: can't use let-else due to rust fmt bug + }; if skip { - println!("skipping {}", Color::red($func)); + if !has_skipped { + println!("skipping {}", $func.red()); + } subtest += 1; + has_skipped = true; continue; } - let machine = machine.as_mut().expect("no machine"); - machine.jump_into_function(&$func, args.clone()); + let machine = setup(&mut machine, &$func, args.clone(), &wasmfile); machine.start_merkle_caching(); run!(machine, 10_000_000, outname!(), true); let output = match machine.get_final_result() { Ok(output) => output, Err(error) => { - let expected: Vec = $expected.into_iter().map(Into::into).collect(); - println!( - "Divergence in func {} of test {}", - Color::red($func), - Color::red(index), - ); + let expected = to_values($expected)?; + println!("Divergence in func {} of test {}", $func.red(), index.red()); pretty_print_values("Args ", args); pretty_print_values("Expected", expected); println!(); @@ -266,19 +313,15 @@ fn main() -> eyre::Result<()> { }; if $expected != output { - let expected: Vec = $expected.into_iter().map(Into::into).collect(); - println!( - "Divergence in func {} of test {}", - Color::red($func), - Color::red(index), - ); + let expected = to_values($expected)?; + println!("Divergence in func {} of test {}", $func.red(), index.red()); pretty_print_values("Args ", args); pretty_print_values("Expected", expected); pretty_print_values("Observed", output); println!(); bail!( "Failure in test {}", - Color::red(format!("{} #{}", wasmfile, subtest)) + format!("{} #{}", wasmfile, subtest).red() ) } subtest += 1; @@ -306,21 +349,20 @@ fn main() -> eyre::Result<()> { let error = error.root_cause().to_string(); skip = true; - if error.contains("Module has no code") { - // We don't support metadata-only modules that have no code - continue; - } - if error.contains("Unsupported import") { - // We don't support the import test's functions - continue; - } - if error.contains("multiple tables") { - // We don't support the reference-type extension - continue; - } - if error.contains("bulk memory") { - // We don't support the bulk-memory extension - continue; + let skippables = vec![ + "module has no code", // we don't support metadata-only modules that have no code + "no such import", // we don't support imports + "unsupported import", // we don't support imports + "reference types", // we don't support the reference-type extension + "multiple tables", // we don't support the reference-type extension + "bulk memory", // we don't support the bulk-memory extension + "simd support", // we don't support the SIMD extension + ]; + + for skippable in skippables { + if error.to_lowercase().contains(skippable) { + continue 'next; + } } bail!("Unexpected error parsing module {}: {}", wasmfile, error) } @@ -344,22 +386,17 @@ fn main() -> eyre::Result<()> { } Command::AssertTrap { action } => { let (func, args) = action!(action); - let args: Vec<_> = args.into_iter().map(Into::into).collect(); - let test = Color::red(format!("{} #{}", wasmfile, subtest)); + let args = to_values(args)?; + let test = format!("{} #{}", wasmfile, subtest).red(); - let machine = machine.as_mut().unwrap(); - machine.jump_into_function(&func, args.clone()); + let machine = setup(&mut machine, &func, args.clone(), &wasmfile); run!(machine, 1000, outname!(), true); if machine.get_status() == MachineStatus::Running { bail!("machine failed to trap in test {}", test) } if let Ok(output) = machine.get_final_result() { - println!( - "Divergence in func {} of test {}", - Color::red(func), - Color::red(index), - ); + println!("Divergence in func {} of test {}", func.red(), index.red()); pretty_print_values("Args ", args); pretty_print_values("Output", output); println!(); @@ -369,11 +406,10 @@ fn main() -> eyre::Result<()> { } Command::AssertExhaustion { action } => { let (func, args) = action!(action); - let args: Vec<_> = args.into_iter().map(Into::into).collect(); - let test = Color::red(format!("{} #{}", wasmfile, subtest)); + let args = to_values(args)?; + let test = format!("{} #{}", wasmfile, subtest).red(); - let machine = machine.as_mut().unwrap(); - machine.jump_into_function(&func, args.clone()); + let machine = setup(&mut machine, &func, args.clone(), &wasmfile); run!(machine, 100_000, outname!(), false); // this is proportional to the amount of RAM if machine.get_status() != MachineStatus::Running { @@ -402,8 +438,8 @@ fn main() -> eyre::Result<()> { println!( "{} {}", - Color::grey("done in"), - Color::pink(format!("{}ms", start_time.elapsed().as_millis())) + "done in".grey(), + format!("{}ms", start_time.elapsed().as_millis()).pink() ); Ok(()) } diff --git a/arbnode/api.go b/arbnode/api.go index 51437864d1..228ad51cf8 100644 --- a/arbnode/api.go +++ b/arbnode/api.go @@ -2,7 +2,6 @@ package arbnode import ( "context" - "errors" "fmt" "time" @@ -40,11 +39,11 @@ func (a *BlockValidatorDebugAPI) ValidateMessageNumber( if moduleRootOptional != nil { moduleRoot = *moduleRootOptional } else { - moduleRoots := a.val.GetModuleRootsToValidate() - if len(moduleRoots) == 0 { - return result, errors.New("no current WasmModuleRoot configured, must provide parameter") + var err error + moduleRoot, err = a.val.GetLatestWasmModuleRoot(ctx) + if err != nil { + return result, fmt.Errorf("no latest WasmModuleRoot configured, must provide parameter: %w", err) } - moduleRoot = moduleRoots[0] } start_time := time.Now() valid, gs, err := a.val.ValidateResult(ctx, arbutil.MessageIndex(msgNum), full, moduleRoot) diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index e9cfe1dd3a..058db160c8 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -38,10 +38,10 @@ import ( "github.com/offchainlabs/nitro/arbnode/redislock" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/cmd/genericconf" - "github.com/offchainlabs/nitro/das" "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/util" @@ -53,8 +53,18 @@ import ( ) var ( - batchPosterWalletBalance = metrics.NewRegisteredGaugeFloat64("arb/batchposter/wallet/balanceether", nil) - batchPosterGasRefunderBalance = metrics.NewRegisteredGaugeFloat64("arb/batchposter/gasrefunder/balanceether", nil) + batchPosterWalletBalance = metrics.NewRegisteredGaugeFloat64("arb/batchposter/wallet/eth", nil) + batchPosterGasRefunderBalance = metrics.NewRegisteredGaugeFloat64("arb/batchposter/gasrefunder/eth", nil) + baseFeeGauge = metrics.NewRegisteredGauge("arb/batchposter/basefee", nil) + blobFeeGauge = metrics.NewRegisteredGauge("arb/batchposter/blobfee", nil) + l1GasPriceGauge = metrics.NewRegisteredGauge("arb/batchposter/l1gasprice", nil) + l1GasPriceEstimateGauge = metrics.NewRegisteredGauge("arb/batchposter/l1gasprice/estimate", nil) + latestBatchSurplusGauge = metrics.NewRegisteredGauge("arb/batchposter/latestbatchsurplus", nil) + blockGasUsedGauge = metrics.NewRegisteredGauge("arb/batchposter/blockgas/used", nil) + blockGasLimitGauge = metrics.NewRegisteredGauge("arb/batchposter/blockgas/limit", nil) + blobGasUsedGauge = metrics.NewRegisteredGauge("arb/batchposter/blobgas/used", nil) + blobGasLimitGauge = metrics.NewRegisteredGauge("arb/batchposter/blobgas/limit", nil) + suggestedTipCapGauge = metrics.NewRegisteredGauge("arb/batchposter/suggestedtipcap", nil) usableBytesInBlob = big.NewInt(int64(len(kzg4844.Blob{}) * 31 / 32)) blobTxBlobGasPerBlob = big.NewInt(params.BlobTxBlobGasPerBlob) @@ -88,7 +98,7 @@ type BatchPoster struct { bridgeAddr common.Address gasRefunderAddr common.Address building *buildingBatch - daWriter das.DataAvailabilityServiceWriter + dapWriter daprovider.Writer dataPoster *dataposter.DataPoster redisLock *redislock.Simple messagesPerBatch *arbmath.MovingAverage[uint64] @@ -119,7 +129,7 @@ const ( type BatchPosterConfig struct { Enable bool `koanf:"enable"` - DisableDasFallbackStoreDataOnChain bool `koanf:"disable-das-fallback-store-data-on-chain" reload:"hot"` + DisableDapFallbackStoreDataOnChain bool `koanf:"disable-dap-fallback-store-data-on-chain" reload:"hot"` // Max batch size. MaxSize int `koanf:"max-size" reload:"hot"` // Maximum 4844 blob enabled batch size. @@ -179,7 +189,7 @@ type BatchPosterConfigFetcher func() *BatchPosterConfig func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".enable", DefaultBatchPosterConfig.Enable, "enable posting batches to l1") - f.Bool(prefix+".disable-das-fallback-store-data-on-chain", DefaultBatchPosterConfig.DisableDasFallbackStoreDataOnChain, "If unable to batch to DAS, disable fallback storing data on chain") + f.Bool(prefix+".disable-dap-fallback-store-data-on-chain", DefaultBatchPosterConfig.DisableDapFallbackStoreDataOnChain, "If unable to batch to DA provider, disable fallback storing data on chain") f.Int(prefix+".max-size", DefaultBatchPosterConfig.MaxSize, "maximum batch size") f.Int(prefix+".max-4844-batch-size", DefaultBatchPosterConfig.Max4844BatchSize, "maximum 4844 blob enabled batch size") f.Duration(prefix+".max-delay", DefaultBatchPosterConfig.MaxDelay, "maximum batch posting delay") @@ -204,11 +214,11 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { var DefaultBatchPosterConfig = BatchPosterConfig{ Enable: false, - DisableDasFallbackStoreDataOnChain: false, + DisableDapFallbackStoreDataOnChain: false, // This default is overridden for L3 chains in applyChainParameters in cmd/nitro/nitro.go MaxSize: 100000, - // TODO: is 1000 bytes an appropriate margin for error vs blob space efficiency? - Max4844BatchSize: blobs.BlobEncodableData*(params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob) - 1000, + // Try to fill 3 blobs per batch + Max4844BatchSize: blobs.BlobEncodableData*(params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob)/2 - 2000, PollInterval: time.Second * 10, ErrorDelay: time.Second * 10, MaxDelay: time.Hour, @@ -268,7 +278,7 @@ type BatchPosterOpts struct { Config BatchPosterConfigFetcher DeployInfo *chaininfo.RollupAddresses TransactOpts *bind.TransactOpts - DAWriter das.DataAvailabilityServiceWriter + DAPWriter daprovider.Writer ParentChainID *big.Int } @@ -314,7 +324,7 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e seqInboxAddr: opts.DeployInfo.SequencerInbox, gasRefunderAddr: opts.Config().gasRefunder, bridgeAddr: opts.DeployInfo.Bridge, - daWriter: opts.DAWriter, + dapWriter: opts.DAPWriter, redisLock: redisLock, } b.messagesPerBatch, err = arbmath.NewMovingAverage[uint64](20) @@ -481,7 +491,7 @@ func (b *BatchPoster) checkReverts(ctx context.Context, to int64) (bool, error) return false, fmt.Errorf("getting a receipt for transaction: %v, %w", tx.Hash, err) } if r.Status == types.ReceiptStatusFailed { - shouldHalt := !b.config().DataPoster.UseNoOpStorage + shouldHalt := !b.dataPoster.UsingNoOpStorage() logLevel := log.Warn if shouldHalt { logLevel = log.Error @@ -510,6 +520,49 @@ func (b *BatchPoster) checkReverts(ctx context.Context, to int64) (bool, error) return false, nil } +func (b *BatchPoster) pollForL1PriceData(ctx context.Context) { + headerCh, unsubscribe := b.l1Reader.Subscribe(false) + defer unsubscribe() + + blobGasLimitGauge.Update(params.MaxBlobGasPerBlock) + for { + select { + case h, ok := <-headerCh: + if !ok { + log.Info("L1 headers channel checking for l1 price data has been closed") + return + } + baseFeeGauge.Update(h.BaseFee.Int64()) + l1GasPrice := h.BaseFee.Uint64() + if h.BlobGasUsed != nil { + if h.ExcessBlobGas != nil { + blobFeePerByte := eip4844.CalcBlobFee(eip4844.CalcExcessBlobGas(*h.ExcessBlobGas, *h.BlobGasUsed)) + blobFeePerByte.Mul(blobFeePerByte, blobTxBlobGasPerBlob) + blobFeePerByte.Div(blobFeePerByte, usableBytesInBlob) + blobFeeGauge.Update(blobFeePerByte.Int64()) + if l1GasPrice > blobFeePerByte.Uint64()/16 { + l1GasPrice = blobFeePerByte.Uint64() / 16 + } + } + blobGasUsedGauge.Update(int64(*h.BlobGasUsed)) + } + blockGasUsedGauge.Update(int64(h.GasUsed)) + blockGasLimitGauge.Update(int64(h.GasLimit)) + suggestedTipCap, err := b.l1Reader.Client().SuggestGasTipCap(ctx) + if err != nil { + log.Warn("unable to fetch suggestedTipCap from l1 client to update arb/batchposter/suggestedtipcap metric", "err", err) + } else { + suggestedTipCapGauge.Update(suggestedTipCap.Int64()) + } + l1GasPriceEstimate := b.streamer.CurrentEstimateOfL1GasPrice() + l1GasPriceGauge.Update(int64(l1GasPrice)) + l1GasPriceEstimateGauge.Update(int64(l1GasPriceEstimate)) + case <-ctx.Done(): + return + } + } +} + // pollForReverts runs a gouroutine that listens to l1 block headers, checks // if any transaction made by batch poster was reverted. func (b *BatchPoster) pollForReverts(ctx context.Context) { @@ -831,7 +884,7 @@ func (s *batchSegments) CloseAndGetBytes() ([]byte, error) { } compressedBytes := s.compressedBuffer.Bytes() fullMsg := make([]byte, 1, len(compressedBytes)+1) - fullMsg[0] = arbstate.BrotliMessageHeaderByte + fullMsg[0] = daprovider.BrotliMessageHeaderByte fullMsg = append(fullMsg, compressedBytes...) return fullMsg, nil } @@ -1011,7 +1064,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) } var use4844 bool config := b.config() - if config.Post4844Blobs && b.daWriter == nil && latestHeader.ExcessBlobGas != nil && latestHeader.BlobGasUsed != nil { + if config.Post4844Blobs && b.dapWriter == nil && latestHeader.ExcessBlobGas != nil && latestHeader.BlobGasUsed != nil { arbOSVersion, err := b.arbOSVersionGetter.ArbOSVersionForMessageNumber(arbutil.MessageIndex(arbmath.SaturatingUSub(uint64(batchPosition.MessageCount), 1))) if err != nil { return false, err @@ -1067,7 +1120,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) } config := b.config() - forcePostBatch := time.Since(firstMsgTime) >= config.MaxDelay + forcePostBatch := config.MaxDelay <= 0 || time.Since(firstMsgTime) >= config.MaxDelay var l1BoundMaxBlockNumber uint64 = math.MaxUint64 var l1BoundMaxTimestamp uint64 = math.MaxUint64 @@ -1194,7 +1247,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) return false, nil } - if b.daWriter != nil { + if b.dapWriter != nil { if !b.redisLock.AttemptLock(ctx) { return false, errAttemptLockFailed } @@ -1206,17 +1259,9 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) if nonce != gotNonce || !bytes.Equal(batchPositionBytes, gotMeta) { return false, fmt.Errorf("%w: nonce changed from %d to %d while creating batch", storage.ErrStorageRace, nonce, gotNonce) } - - cert, err := b.daWriter.Store(ctx, sequencerMsg, uint64(time.Now().Add(config.DASRetentionPeriod).Unix()), []byte{}) // b.daWriter will append signature if enabled - if errors.Is(err, das.BatchToDasFailed) { - if config.DisableDasFallbackStoreDataOnChain { - return false, errors.New("unable to batch to DAS and fallback storing data on chain is disabled") - } - log.Warn("Falling back to storing data on chain", "err", err) - } else if err != nil { + sequencerMsg, err = b.dapWriter.Store(ctx, sequencerMsg, uint64(time.Now().Add(config.DASRetentionPeriod).Unix()), []byte{}, config.DisableDapFallbackStoreDataOnChain) + if err != nil { return false, err - } else { - sequencerMsg = das.Serialize(cert) } } @@ -1225,7 +1270,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) return false, err } if len(kzgBlobs)*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock { - return false, fmt.Errorf("produced %v blobs for batch but a block can only hold %v", len(kzgBlobs), params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob) + return false, fmt.Errorf("produced %v blobs for batch but a block can only hold %v (compressed batch was %v bytes long)", len(kzgBlobs), params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob, len(sequencerMsg)) } accessList := b.accessList(int(batchPosition.NextSeqNum), int(b.building.segments.delayedMsg)) // On restart, we may be trying to estimate gas for a batch whose successor has @@ -1272,6 +1317,14 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) "numBlobs", len(kzgBlobs), ) + surplus := arbmath.SaturatingMul( + arbmath.SaturatingSub( + l1GasPriceGauge.Snapshot().Value(), + l1GasPriceEstimateGauge.Snapshot().Value()), + int64(len(sequencerMsg)*16), + ) + latestBatchSurplusGauge.Update(surplus) + recentlyHitL1Bounds := time.Since(b.lastHitL1Bounds) < config.PollInterval*3 postedMessages := b.building.msgCount - batchPosition.MessageCount b.messagesPerBatch.Update(uint64(postedMessages)) @@ -1341,6 +1394,7 @@ func (b *BatchPoster) Start(ctxIn context.Context) { b.redisLock.Start(ctxIn) b.StopWaiter.Start(ctxIn, b) b.LaunchThread(b.pollForReverts) + b.LaunchThread(b.pollForL1PriceData) commonEphemeralErrorHandler := util.NewEphemeralErrorHandler(time.Minute, "", 0) exceedMaxMempoolSizeEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, dataposter.ErrExceedsMaxMempoolSize.Error(), time.Minute) storageRaceEphemeralErrorHandler := util.NewEphemeralErrorHandler(5*time.Minute, storage.ErrStorageRace.Error(), time.Minute) diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index 416ebf7253..5aaef959d8 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -217,6 +217,10 @@ func NewDataPoster(ctx context.Context, opts *DataPosterOpts) (*DataPoster, erro func rpcClient(ctx context.Context, opts *ExternalSignerCfg) (*rpc.Client, error) { tlsCfg := &tls.Config{ MinVersion: tls.VersionTLS12, + // Dataposter verifies that signed transaction was signed by the account + // that it expects to be signed with. So signer is already authenticated + // on application level and does not need to rely on TLS for authentication. + InsecureSkipVerify: opts.InsecureSkipVerify, // #nosec G402 } if opts.ClientCert != "" && opts.ClientPrivateKey != "" { @@ -304,6 +308,10 @@ func (p *DataPoster) MaxMempoolTransactions() uint64 { return arbmath.MinInt(config.MaxMempoolTransactions, config.MaxMempoolWeight) } +func (p *DataPoster) UsingNoOpStorage() bool { + return p.usingNoOpStorage +} + var ErrExceedsMaxMempoolSize = errors.New("posting this transaction will exceed max mempool size") // Does basic check whether posting transaction with specified nonce would @@ -588,7 +596,7 @@ func (p *DataPoster) feeAndTipCaps(ctx context.Context, nonce uint64, gasLimit u targetBlobCost := arbmath.BigMulByUint(newBlobFeeCap, blobGasUsed) targetNonBlobCost := arbmath.BigSub(targetMaxCost, targetBlobCost) newBaseFeeCap := arbmath.BigDivByUint(targetNonBlobCost, gasLimit) - if lastTx != nil && numBlobs > 0 && arbmath.BigDivToBips(newBaseFeeCap, lastTx.GasFeeCap()) < minRbfIncrease { + if lastTx != nil && numBlobs > 0 && lastTx.GasFeeCap().Sign() > 0 && arbmath.BigDivToBips(newBaseFeeCap, lastTx.GasFeeCap()) < minRbfIncrease { // Increase the non-blob fee cap to the minimum rbf increase newBaseFeeCap = arbmath.BigMulByBips(lastTx.GasFeeCap(), minRbfIncrease) newNonBlobCost := arbmath.BigMulByUint(newBaseFeeCap, gasLimit) @@ -661,6 +669,14 @@ func (p *DataPoster) feeAndTipCaps(ctx context.Context, nonce uint64, gasLimit u return lastTx.GasFeeCap(), lastTx.GasTipCap(), lastTx.BlobGasFeeCap(), nil } + // Ensure we bid at least 1 wei to prevent division by zero + if newBaseFeeCap.Sign() == 0 { + newBaseFeeCap = big.NewInt(1) + } + if newBlobFeeCap.Sign() == 0 { + newBlobFeeCap = big.NewInt(1) + } + return newBaseFeeCap, newTipCap, newBlobFeeCap, nil } @@ -828,6 +844,41 @@ func (p *DataPoster) sendTx(ctx context.Context, prevTx *storage.QueuedTransacti if err := p.saveTx(ctx, prevTx, newTx); err != nil { return err } + + // The following check is to avoid sending transactions of a different type (eg DynamicFeeTxType vs BlobTxType) + // to the previous tx if the previous tx is not yet included in a reorg resistant block, in order to avoid issues + // where eventual consistency of parent chain mempools causes a tx with higher nonce blocking a tx of a + // different type with a lower nonce. + // If we decide not to send this tx yet, just leave it queued and with Sent set to false. + // The resending/repricing loop in DataPoster.Start will keep trying. + previouslySent := newTx.Sent || (prevTx != nil && prevTx.Sent) // if we've previously sent this nonce + if !previouslySent && newTx.FullTx.Nonce() > 0 { + precedingTx, err := p.queue.Get(ctx, arbmath.SaturatingUSub(newTx.FullTx.Nonce(), 1)) + if err != nil { + return fmt.Errorf("couldn't get preceding tx in DataPoster to check if should send tx with nonce %d: %w", newTx.FullTx.Nonce(), err) + } + if precedingTx != nil { // precedingTx == nil -> the actual preceding tx was already confirmed + var latestBlockNumber, prevBlockNumber, reorgResistantTxCount uint64 + if precedingTx.FullTx.Type() != newTx.FullTx.Type() || !precedingTx.Sent { + latestBlockNumber, err = p.client.BlockNumber(ctx) + if err != nil { + return fmt.Errorf("couldn't get block number in DataPoster to check if should send tx with nonce %d: %w", newTx.FullTx.Nonce(), err) + } + prevBlockNumber = arbmath.SaturatingUSub(latestBlockNumber, 1) + reorgResistantTxCount, err = p.client.NonceAt(ctx, p.Sender(), new(big.Int).SetUint64(prevBlockNumber)) + if err != nil { + return fmt.Errorf("couldn't determine reorg resistant nonce in DataPoster to check if should send tx with nonce %d: %w", newTx.FullTx.Nonce(), err) + } + + if newTx.FullTx.Nonce() > reorgResistantTxCount { + log.Info("DataPoster is avoiding creating a mempool nonce gap (the tx remains queued and will be retried)", "nonce", newTx.FullTx.Nonce(), "prevType", precedingTx.FullTx.Type(), "type", newTx.FullTx.Type(), "prevSent", precedingTx.Sent, "latestBlockNumber", latestBlockNumber, "prevBlockNumber", prevBlockNumber, "reorgResistantTxCount", reorgResistantTxCount) + return nil + } + } + log.Debug("DataPoster will send previously unsent batch tx", "nonce", newTx.FullTx.Nonce(), "prevType", precedingTx.FullTx.Type(), "type", newTx.FullTx.Type(), "prevSent", precedingTx.Sent, "latestBlockNumber", latestBlockNumber, "prevBlockNumber", prevBlockNumber, "reorgResistantTxCount", reorgResistantTxCount) + } + } + if err := p.client.SendTransaction(ctx, newTx.FullTx); err != nil { if !rpcclient.IsAlreadyKnownError(err) && !strings.Contains(err.Error(), "nonce too low") { log.Warn("DataPoster failed to send transaction", "err", err, "nonce", newTx.FullTx.Nonce(), "feeCap", newTx.FullTx.GasFeeCap(), "tipCap", newTx.FullTx.GasTipCap(), "blobFeeCap", newTx.FullTx.BlobGasFeeCap(), "gas", newTx.FullTx.Gas()) @@ -895,8 +946,8 @@ func (p *DataPoster) replaceTx(ctx context.Context, prevTx *storage.QueuedTransa } newTx := *prevTx - if arbmath.BigDivToBips(newFeeCap, prevTx.FullTx.GasFeeCap()) < minRbfIncrease || - (prevTx.FullTx.BlobGasFeeCap() != nil && arbmath.BigDivToBips(newBlobFeeCap, prevTx.FullTx.BlobGasFeeCap()) < minRbfIncrease) { + if (prevTx.FullTx.GasFeeCap().Sign() > 0 && arbmath.BigDivToBips(newFeeCap, prevTx.FullTx.GasFeeCap()) < minRbfIncrease) || + (prevTx.FullTx.BlobGasFeeCap() != nil && prevTx.FullTx.BlobGasFeeCap().Sign() > 0 && arbmath.BigDivToBips(newBlobFeeCap, prevTx.FullTx.BlobGasFeeCap()) < minRbfIncrease) { log.Debug( "no need to replace by fee transaction", "nonce", prevTx.FullTx.Nonce(), @@ -1068,19 +1119,21 @@ func (p *DataPoster) Start(ctxIn context.Context) { latestNonce = latestQueued.FullTx.Nonce() } for _, tx := range queueContents { - replacing := false + previouslyUnsent := !tx.Sent + sendAttempted := false if now.After(tx.NextReplacement) { - replacing = true nonceBacklog := arbmath.SaturatingUSub(latestNonce, tx.FullTx.Nonce()) weightBacklog := arbmath.SaturatingUSub(latestCumulativeWeight, tx.CumulativeWeight()) err := p.replaceTx(ctx, tx, arbmath.MaxInt(nonceBacklog, weightBacklog)) + sendAttempted = true p.maybeLogError(err, tx, "failed to replace-by-fee transaction") } if nextCheck.After(tx.NextReplacement) { nextCheck = tx.NextReplacement } - if !replacing && !tx.Sent { + if !sendAttempted && previouslyUnsent { err := p.sendTx(ctx, tx, tx) + sendAttempted = true p.maybeLogError(err, tx, "failed to re-send transaction") if err != nil { nextSend := time.Now().Add(time.Minute) @@ -1089,6 +1142,12 @@ func (p *DataPoster) Start(ctxIn context.Context) { } } } + if previouslyUnsent && sendAttempted { + // Don't try to send more than 1 unsent transaction, to play nicely with parent chain mempools. + // Transactions will be unsent if there was some error when originally sending them, + // or if transaction type changes and the prior tx is not yet reorg resistant. + break + } } wait := time.Until(nextCheck) if wait < minWait { @@ -1168,6 +1227,8 @@ type ExternalSignerCfg struct { // (Optional) Client certificate key for mtls. // This is required when client-cert is set. ClientPrivateKey string `koanf:"client-private-key"` + // TLS config option, when enabled skips certificate verification of external signer. + InsecureSkipVerify bool `koanf:"insecure-skip-verify"` } type DangerousConfig struct { @@ -1221,6 +1282,7 @@ func addExternalSignerOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".root-ca", DefaultDataPosterConfig.ExternalSigner.RootCA, "external signer root CA") f.String(prefix+".client-cert", DefaultDataPosterConfig.ExternalSigner.ClientCert, "rpc client cert") f.String(prefix+".client-private-key", DefaultDataPosterConfig.ExternalSigner.ClientPrivateKey, "rpc client private key") + f.Bool(prefix+".insecure-skip-verify", DefaultDataPosterConfig.ExternalSigner.InsecureSkipVerify, "skip TLS certificate verification") } var DefaultDataPosterConfig = DataPosterConfig{ @@ -1242,7 +1304,7 @@ var DefaultDataPosterConfig = DataPosterConfig{ UseNoOpStorage: false, LegacyStorageEncoding: false, Dangerous: DangerousConfig{ClearDBStorage: false}, - ExternalSigner: ExternalSignerCfg{Method: "eth_signTransaction"}, + ExternalSigner: ExternalSignerCfg{Method: "eth_signTransaction", InsecureSkipVerify: false}, MaxFeeCapFormula: "((BacklogOfBatches * UrgencyGWei) ** 2) + ((ElapsedTime/ElapsedTimeBase) ** 2) * ElapsedTimeImportance + TargetPriceGWei", ElapsedTimeBase: 10 * time.Minute, ElapsedTimeImportance: 10, @@ -1275,7 +1337,7 @@ var TestDataPosterConfig = DataPosterConfig{ UseDBStorage: false, UseNoOpStorage: false, LegacyStorageEncoding: false, - ExternalSigner: ExternalSignerCfg{Method: "eth_signTransaction"}, + ExternalSigner: ExternalSignerCfg{Method: "eth_signTransaction", InsecureSkipVerify: true}, MaxFeeCapFormula: "((BacklogOfBatches * UrgencyGWei) ** 2) + ((ElapsedTime/ElapsedTimeBase) ** 2) * ElapsedTimeImportance + TargetPriceGWei", ElapsedTimeBase: 10 * time.Minute, ElapsedTimeImportance: 10, diff --git a/arbnode/dataposter/storage_test.go b/arbnode/dataposter/storage_test.go index f98c120f38..e2aa321e0d 100644 --- a/arbnode/dataposter/storage_test.go +++ b/arbnode/dataposter/storage_test.go @@ -19,6 +19,7 @@ import ( "github.com/offchainlabs/nitro/arbnode/dataposter/redis" "github.com/offchainlabs/nitro/arbnode/dataposter/slice" "github.com/offchainlabs/nitro/arbnode/dataposter/storage" + "github.com/offchainlabs/nitro/cmd/conf" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/redisutil" "github.com/offchainlabs/nitro/util/signature" @@ -44,7 +45,7 @@ func newLevelDBStorage(t *testing.T, encF storage.EncoderDecoderF) *dbstorage.St func newPebbleDBStorage(t *testing.T, encF storage.EncoderDecoderF) *dbstorage.Storage { t.Helper() - db, err := rawdb.NewPebbleDBDatabase(path.Join(t.TempDir(), "pebble.db"), 0, 0, "default", false, true) + db, err := rawdb.NewPebbleDBDatabase(path.Join(t.TempDir(), "pebble.db"), 0, 0, "default", false, true, conf.PersistentConfigDefault.Pebble.ExtraOptions("pebble")) if err != nil { t.Fatalf("NewPebbleDBDatabase() unexpected error: %v", err) } diff --git a/arbnode/delayed_seq_reorg_test.go b/arbnode/delayed_seq_reorg_test.go index beb2656e2b..699eb3e8f6 100644 --- a/arbnode/delayed_seq_reorg_test.go +++ b/arbnode/delayed_seq_reorg_test.go @@ -19,7 +19,7 @@ func TestSequencerReorgFromDelayed(t *testing.T) { defer cancel() exec, streamer, db, _ := NewTransactionStreamerForTest(t, common.Address{}) - tracker, err := NewInboxTracker(db, streamer, nil, nil) + tracker, err := NewInboxTracker(db, streamer, nil, DefaultSnapSyncConfig) Require(t, err) err = streamer.Start(ctx) diff --git a/arbnode/inbox_reader.go b/arbnode/inbox_reader.go index 72881b52fd..3ba9aa78f3 100644 --- a/arbnode/inbox_reader.go +++ b/arbnode/inbox_reader.go @@ -10,7 +10,6 @@ import ( "math" "math/big" "strings" - "sync" "sync/atomic" "time" @@ -99,10 +98,6 @@ type InboxReader struct { // Atomic lastSeenBatchCount uint64 - - // Behind the mutex - lastReadMutex sync.RWMutex - lastReadBlock uint64 lastReadBatchCount uint64 } @@ -144,6 +139,9 @@ func (r *InboxReader) Start(ctxIn context.Context) error { return err } if batchCount > 0 { + if r.tracker.snapSyncConfig.Enabled { + break + } // Validate the init message matches our L2 blockchain message, err := r.tracker.GetDelayedMessage(0) if err != nil { @@ -396,10 +394,7 @@ func (r *InboxReader) run(ctx context.Context, hadError bool) error { // There's nothing to do from = arbmath.BigAddByUint(currentHeight, 1) blocksToFetch = config.DefaultBlocksToRead - r.lastReadMutex.Lock() - r.lastReadBlock = currentHeight.Uint64() - r.lastReadBatchCount = checkingBatchCount - r.lastReadMutex.Unlock() + atomic.StoreUint64(&r.lastReadBatchCount, checkingBatchCount) storeSeenBatchCount() if !r.caughtUp && readMode == "latest" { r.caughtUp = true @@ -531,10 +526,7 @@ func (r *InboxReader) run(ctx context.Context, hadError bool) error { } if len(sequencerBatches) > 0 { readAnyBatches = true - r.lastReadMutex.Lock() - r.lastReadBlock = to.Uint64() - r.lastReadBatchCount = sequencerBatches[len(sequencerBatches)-1].SequenceNumber + 1 - r.lastReadMutex.Unlock() + atomic.StoreUint64(&r.lastReadBatchCount, sequencerBatches[len(sequencerBatches)-1].SequenceNumber+1) storeSeenBatchCount() } } @@ -561,10 +553,7 @@ func (r *InboxReader) run(ctx context.Context, hadError bool) error { } if !readAnyBatches { - r.lastReadMutex.Lock() - r.lastReadBlock = currentHeight.Uint64() - r.lastReadBatchCount = checkingBatchCount - r.lastReadMutex.Unlock() + atomic.StoreUint64(&r.lastReadBatchCount, checkingBatchCount) storeSeenBatchCount() } } @@ -635,10 +624,8 @@ func (r *InboxReader) GetSequencerMessageBytes(ctx context.Context, seqNum uint6 return nil, common.Hash{}, fmt.Errorf("sequencer batch %v not found in L1 block %v (found batches %v)", seqNum, metadata.ParentChainBlock, seenBatches) } -func (r *InboxReader) GetLastReadBlockAndBatchCount() (uint64, uint64) { - r.lastReadMutex.RLock() - defer r.lastReadMutex.RUnlock() - return r.lastReadBlock, r.lastReadBatchCount +func (r *InboxReader) GetLastReadBatchCount() uint64 { + return atomic.LoadUint64(&r.lastReadBatchCount) } // GetLastSeenBatchCount returns how many sequencer batches the inbox reader has read in from L1. diff --git a/arbnode/inbox_test.go b/arbnode/inbox_test.go index e979979dea..77db4e09b4 100644 --- a/arbnode/inbox_test.go +++ b/arbnode/inbox_test.go @@ -54,7 +54,7 @@ func NewTransactionStreamerForTest(t *testing.T, ownerAddress common.Address) (* arbDb := rawdb.NewMemoryDatabase() initReader := statetransfer.NewMemoryInitDataReader(&initData) - bc, err := gethexec.WriteOrTestBlockChain(chainDb, nil, initReader, chainConfig, arbostypes.TestInitMessage, gethexec.ConfigDefaultTest().TxLookupLimit, 0) + bc, err := gethexec.WriteOrTestBlockChain(chainDb, nil, initReader, chainConfig, arbostypes.TestInitMessage, gethexec.ConfigDefaultTest().TxLookupLimit, 0, nil) if err != nil { Fail(t, err) @@ -65,8 +65,9 @@ func NewTransactionStreamerForTest(t *testing.T, ownerAddress common.Address) (* if err != nil { Fail(t, err) } + execEngine.Initialize(gethexec.DefaultCachingConfig.StylusLRUCache) execSeq := &execClientWrapper{execEngine, t} - inbox, err := NewTransactionStreamer(arbDb, bc.Config(), execSeq, nil, make(chan error, 1), transactionStreamerConfigFetcher) + inbox, err := NewTransactionStreamer(arbDb, bc.Config(), execSeq, nil, make(chan error, 1), transactionStreamerConfigFetcher, &DefaultSnapSyncConfig) if err != nil { Fail(t, err) } @@ -233,7 +234,7 @@ func TestTransactionStreamer(t *testing.T) { Fail(t, "error getting block state", err) } haveBalance := state.GetBalance(acct) - if balance.Cmp(haveBalance) != 0 { + if balance.Cmp(haveBalance.ToBig()) != 0 { t.Error("unexpected balance for account", acct, "; expected", balance, "got", haveBalance) } } diff --git a/arbnode/inbox_tracker.go b/arbnode/inbox_tracker.go index f98f93a3eb..b950c1e1ef 100644 --- a/arbnode/inbox_tracker.go +++ b/arbnode/inbox_tracker.go @@ -20,6 +20,7 @@ import ( "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/broadcaster" m "github.com/offchainlabs/nitro/broadcaster/message" @@ -33,28 +34,24 @@ var ( ) type InboxTracker struct { - db ethdb.Database - txStreamer *TransactionStreamer - mutex sync.Mutex - validator *staker.BlockValidator - das arbstate.DataAvailabilityReader - blobReader arbstate.BlobReader + db ethdb.Database + txStreamer *TransactionStreamer + mutex sync.Mutex + validator *staker.BlockValidator + dapReaders []daprovider.Reader + snapSyncConfig SnapSyncConfig batchMetaMutex sync.Mutex batchMeta *containers.LruCache[uint64, BatchMetadata] } -func NewInboxTracker(db ethdb.Database, txStreamer *TransactionStreamer, das arbstate.DataAvailabilityReader, blobReader arbstate.BlobReader) (*InboxTracker, error) { - // We support a nil txStreamer for the pruning code - if txStreamer != nil && txStreamer.chainConfig.ArbitrumChainParams.DataAvailabilityCommittee && das == nil { - return nil, errors.New("data availability service required but unconfigured") - } +func NewInboxTracker(db ethdb.Database, txStreamer *TransactionStreamer, dapReaders []daprovider.Reader, snapSyncConfig SnapSyncConfig) (*InboxTracker, error) { tracker := &InboxTracker{ - db: db, - txStreamer: txStreamer, - das: das, - blobReader: blobReader, - batchMeta: containers.NewLruCache[uint64, BatchMetadata](1000), + db: db, + txStreamer: txStreamer, + dapReaders: dapReaders, + batchMeta: containers.NewLruCache[uint64, BatchMetadata](1000), + snapSyncConfig: snapSyncConfig, } return tracker, nil } @@ -204,6 +201,11 @@ func (t *InboxTracker) GetBatchMessageCount(seqNum uint64) (arbutil.MessageIndex return metadata.MessageCount, err } +func (t *InboxTracker) GetBatchParentChainBlock(seqNum uint64) (uint64, error) { + metadata, err := t.GetBatchMetadata(seqNum) + return metadata.ParentChainBlock, err +} + // GetBatchAcc is a convenience function wrapping GetBatchMetadata func (t *InboxTracker) GetBatchAcc(seqNum uint64) (common.Hash, error) { metadata, err := t.GetBatchMetadata(seqNum) @@ -223,6 +225,54 @@ func (t *InboxTracker) GetBatchCount() (uint64, error) { return count, nil } +// err will return unexpected/internal errors +// bool will be false if batch not found (meaning, block not yet posted on a batch) +func (t *InboxTracker) FindInboxBatchContainingMessage(pos arbutil.MessageIndex) (uint64, bool, error) { + batchCount, err := t.GetBatchCount() + if err != nil { + return 0, false, err + } + low := uint64(0) + high := batchCount - 1 + lastBatchMessageCount, err := t.GetBatchMessageCount(high) + if err != nil { + return 0, false, err + } + if lastBatchMessageCount <= pos { + return 0, false, nil + } + // Iteration preconditions: + // - high >= low + // - msgCount(low - 1) <= pos implies low <= target + // - msgCount(high) > pos implies high >= target + // Therefore, if low == high, then low == high == target + for { + // Due to integer rounding, mid >= low && mid < high + mid := (low + high) / 2 + count, err := t.GetBatchMessageCount(mid) + if err != nil { + return 0, false, err + } + if count < pos { + // Must narrow as mid >= low, therefore mid + 1 > low, therefore newLow > oldLow + // Keeps low precondition as msgCount(mid) < pos + low = mid + 1 + } else if count == pos { + return mid + 1, true, nil + } else if count == pos+1 || mid == low { // implied: count > pos + return mid, true, nil + } else { + // implied: count > pos + 1 + // Must narrow as mid < high, therefore newHigh < oldHigh + // Keeps high precondition as msgCount(mid) > pos + high = mid + } + if high == low { + return high, true, nil + } + } +} + func (t *InboxTracker) PopulateFeedBacklog(broadcastServer *broadcaster.Broadcaster) error { batchCount, err := t.GetBatchCount() if err != nil { @@ -249,7 +299,14 @@ func (t *InboxTracker) PopulateFeedBacklog(broadcastServer *broadcaster.Broadcas if err != nil { return fmt.Errorf("error getting message %v: %w", seqNum, err) } - feedMessage, err := broadcastServer.NewBroadcastFeedMessage(*message, seqNum) + + msgResult, err := t.txStreamer.ResultAtCount(seqNum) + var blockHash *common.Hash + if err == nil { + blockHash = &msgResult.BlockHash + } + + feedMessage, err := broadcastServer.NewBroadcastFeedMessage(*message, seqNum, blockHash) if err != nil { return fmt.Errorf("error creating broadcast feed message %v: %w", seqNum, err) } @@ -330,16 +387,40 @@ func (t *InboxTracker) GetDelayedMessageBytes(seqNum uint64) ([]byte, error) { } func (t *InboxTracker) AddDelayedMessages(messages []*DelayedInboxMessage, hardReorg bool) error { + var nextAcc common.Hash + firstDelayedMsgToKeep := uint64(0) if len(messages) == 0 { return nil } - t.mutex.Lock() - defer t.mutex.Unlock() - pos, err := messages[0].Message.Header.SeqNum() if err != nil { return err } + if t.snapSyncConfig.Enabled && pos < t.snapSyncConfig.DelayedCount { + firstDelayedMsgToKeep = t.snapSyncConfig.DelayedCount + if firstDelayedMsgToKeep > 0 { + firstDelayedMsgToKeep-- + } + for { + if len(messages) == 0 { + return nil + } + pos, err = messages[0].Message.Header.SeqNum() + if err != nil { + return err + } + if pos+1 == firstDelayedMsgToKeep { + nextAcc = messages[0].AfterInboxAcc() + } + if pos < firstDelayedMsgToKeep { + messages = messages[1:] + } else { + break + } + } + } + t.mutex.Lock() + defer t.mutex.Unlock() if !hardReorg { // This math is safe to do as we know len(messages) > 0 @@ -354,8 +435,7 @@ func (t *InboxTracker) AddDelayedMessages(messages []*DelayedInboxMessage, hardR } } - var nextAcc common.Hash - if pos > 0 { + if pos > firstDelayedMsgToKeep { var err error nextAcc, err = t.GetDelayedAcc(pos - 1) if err != nil { @@ -543,17 +623,44 @@ func (b *multiplexerBackend) ReadDelayedInbox(seqNum uint64) (*arbostypes.L1Inco var delayedMessagesMismatch = errors.New("sequencer batch delayed messages missing or different") func (t *InboxTracker) AddSequencerBatches(ctx context.Context, client arbutil.L1Interface, batches []*SequencerInboxBatch) error { + var nextAcc common.Hash + var prevbatchmeta BatchMetadata + sequenceNumberToKeep := uint64(0) if len(batches) == 0 { return nil } + if t.snapSyncConfig.Enabled && batches[0].SequenceNumber < t.snapSyncConfig.BatchCount { + sequenceNumberToKeep = t.snapSyncConfig.BatchCount + if sequenceNumberToKeep > 0 { + sequenceNumberToKeep-- + } + for { + if len(batches) == 0 { + return nil + } + if batches[0].SequenceNumber+1 == sequenceNumberToKeep { + nextAcc = batches[0].AfterInboxAcc + prevbatchmeta = BatchMetadata{ + Accumulator: batches[0].AfterInboxAcc, + DelayedMessageCount: batches[0].AfterDelayedCount, + MessageCount: arbutil.MessageIndex(t.snapSyncConfig.PrevBatchMessageCount), + ParentChainBlock: batches[0].ParentChainBlockNumber, + } + } + if batches[0].SequenceNumber < sequenceNumberToKeep { + batches = batches[1:] + } else { + break + } + } + } t.mutex.Lock() defer t.mutex.Unlock() pos := batches[0].SequenceNumber startPos := pos - var nextAcc common.Hash - var prevbatchmeta BatchMetadata - if pos > 0 { + + if pos > sequenceNumberToKeep { var err error prevbatchmeta, err = t.GetBatchMetadata(pos - 1) nextAcc = prevbatchmeta.Accumulator @@ -606,14 +713,7 @@ func (t *InboxTracker) AddSequencerBatches(ctx context.Context, client arbutil.L ctx: ctx, client: client, } - var daProviders []arbstate.DataAvailabilityProvider - if t.das != nil { - daProviders = append(daProviders, arbstate.NewDAProviderDAS(t.das)) - } - if t.blobReader != nil { - daProviders = append(daProviders, arbstate.NewDAProviderBlobReader(t.blobReader)) - } - multiplexer := arbstate.NewInboxMultiplexer(backend, prevbatchmeta.DelayedMessageCount, daProviders, arbstate.KeysetValidate) + multiplexer := arbstate.NewInboxMultiplexer(backend, prevbatchmeta.DelayedMessageCount, t.dapReaders, daprovider.KeysetValidate) batchMessageCounts := make(map[uint64]arbutil.MessageIndex) currentpos := prevbatchmeta.MessageCount + 1 for { diff --git a/arbnode/message_pruner.go b/arbnode/message_pruner.go index 31bf1a63ff..5d18341a27 100644 --- a/arbnode/message_pruner.go +++ b/arbnode/message_pruner.go @@ -23,13 +23,14 @@ import ( type MessagePruner struct { stopwaiter.StopWaiter - transactionStreamer *TransactionStreamer - inboxTracker *InboxTracker - config MessagePrunerConfigFetcher - pruningLock sync.Mutex - lastPruneDone time.Time - cachedPrunedMessages uint64 - cachedPrunedDelayedMessages uint64 + transactionStreamer *TransactionStreamer + inboxTracker *InboxTracker + config MessagePrunerConfigFetcher + pruningLock sync.Mutex + lastPruneDone time.Time + cachedPrunedMessages uint64 + cachedPrunedBlockHashesInputFeed uint64 + cachedPrunedDelayedMessages uint64 } type MessagePrunerConfig struct { @@ -115,7 +116,15 @@ func (m *MessagePruner) prune(ctx context.Context, count arbutil.MessageIndex, g } func (m *MessagePruner) deleteOldMessagesFromDB(ctx context.Context, messageCount arbutil.MessageIndex, delayedMessageCount uint64) error { - prunedKeysRange, err := deleteFromLastPrunedUptoEndKey(ctx, m.transactionStreamer.db, messagePrefix, &m.cachedPrunedMessages, uint64(messageCount)) + prunedKeysRange, err := deleteFromLastPrunedUptoEndKey(ctx, m.transactionStreamer.db, blockHashInputFeedPrefix, &m.cachedPrunedBlockHashesInputFeed, uint64(messageCount)) + if err != nil { + return fmt.Errorf("error deleting expected block hashes: %w", err) + } + if len(prunedKeysRange) > 0 { + log.Info("Pruned expected block hashes:", "first pruned key", prunedKeysRange[0], "last pruned key", prunedKeysRange[len(prunedKeysRange)-1]) + } + + prunedKeysRange, err = deleteFromLastPrunedUptoEndKey(ctx, m.transactionStreamer.db, messagePrefix, &m.cachedPrunedMessages, uint64(messageCount)) if err != nil { return fmt.Errorf("error deleting last batch messages: %w", err) } diff --git a/arbnode/message_pruner_test.go b/arbnode/message_pruner_test.go index 0212ed2364..ed85c0ebce 100644 --- a/arbnode/message_pruner_test.go +++ b/arbnode/message_pruner_test.go @@ -22,8 +22,8 @@ func TestMessagePrunerWithPruningEligibleMessagePresent(t *testing.T) { Require(t, err) checkDbKeys(t, messagesCount, transactionStreamerDb, messagePrefix) + checkDbKeys(t, messagesCount, transactionStreamerDb, blockHashInputFeedPrefix) checkDbKeys(t, messagesCount, inboxTrackerDb, rlpDelayedMessagePrefix) - } func TestMessagePrunerTwoHalves(t *testing.T) { @@ -71,16 +71,18 @@ func TestMessagePrunerWithNoPruningEligibleMessagePresent(t *testing.T) { Require(t, err) checkDbKeys(t, uint64(messagesCount), transactionStreamerDb, messagePrefix) + checkDbKeys(t, uint64(messagesCount), transactionStreamerDb, blockHashInputFeedPrefix) checkDbKeys(t, messagesCount, inboxTrackerDb, rlpDelayedMessagePrefix) } func setupDatabase(t *testing.T, messageCount, delayedMessageCount uint64) (ethdb.Database, ethdb.Database, *MessagePruner) { - transactionStreamerDb := rawdb.NewMemoryDatabase() for i := uint64(0); i < uint64(messageCount); i++ { err := transactionStreamerDb.Put(dbKey(messagePrefix, i), []byte{}) Require(t, err) + err = transactionStreamerDb.Put(dbKey(blockHashInputFeedPrefix, i), []byte{}) + Require(t, err) } inboxTrackerDb := rawdb.NewMemoryDatabase() diff --git a/arbnode/node.go b/arbnode/node.go index 6cab94f1d9..a15b41439b 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -26,7 +26,8 @@ import ( "github.com/offchainlabs/nitro/arbnode/dataposter" "github.com/offchainlabs/nitro/arbnode/dataposter/storage" "github.com/offchainlabs/nitro/arbnode/resourcemanager" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbos/arbostypes" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/broadcastclient" "github.com/offchainlabs/nitro/broadcastclients" @@ -93,6 +94,8 @@ type Config struct { TransactionStreamer TransactionStreamerConfig `koanf:"transaction-streamer" reload:"hot"` Maintenance MaintenanceConfig `koanf:"maintenance" reload:"hot"` ResourceMgmt resourcemanager.Config `koanf:"resource-mgmt" reload:"hot"` + // SnapSyncConfig is only used for testing purposes, these should not be configured in production. + SnapSyncTest SnapSyncConfig } func (c *Config) Validate() error { @@ -177,6 +180,7 @@ var ConfigDefault = Config{ TransactionStreamer: DefaultTransactionStreamerConfig, ResourceMgmt: resourcemanager.DefaultConfig, Maintenance: DefaultMaintenanceConfig, + SnapSyncTest: DefaultSnapSyncConfig, } func ConfigDefaultL1Test() *Config { @@ -199,6 +203,7 @@ func ConfigDefaultL1NonSequencerTest() *Config { config.BatchPoster.Enable = false config.SeqCoordinator.Enable = false config.BlockValidator = staker.TestBlockValidatorConfig + config.SyncMonitor = TestSyncMonitorConfig config.Staker = staker.TestL1ValidatorConfig config.Staker.Enable = false config.BlockValidator.ValidationServerConfigs = []rpcclient.ClientConfig{{URL: ""}} @@ -216,6 +221,7 @@ func ConfigDefaultL2Test() *Config { config.SeqCoordinator.Signer.ECDSA.AcceptSequencer = false config.SeqCoordinator.Signer.ECDSA.Dangerous.AcceptMissing = true config.Staker = staker.TestL1ValidatorConfig + config.SyncMonitor = TestSyncMonitorConfig config.Staker.Enable = false config.BlockValidator.ValidationServerConfigs = []rpcclient.ClientConfig{{URL: ""}} config.TransactionStreamer = DefaultTransactionStreamerConfig @@ -254,7 +260,7 @@ type Node struct { L1Reader *headerreader.HeaderReader TxStreamer *TransactionStreamer DeployInfo *chaininfo.RollupAddresses - BlobReader arbstate.BlobReader + BlobReader daprovider.BlobReader InboxReader *InboxReader InboxTracker *InboxTracker DelayedSequencer *DelayedSequencer @@ -268,12 +274,27 @@ type Node struct { SeqCoordinator *SeqCoordinator MaintenanceRunner *MaintenanceRunner DASLifecycleManager *das.LifecycleManager - ClassicOutboxRetriever *ClassicOutboxRetriever SyncMonitor *SyncMonitor configFetcher ConfigFetcher ctx context.Context } +type SnapSyncConfig struct { + Enabled bool + PrevBatchMessageCount uint64 + PrevDelayedRead uint64 + BatchCount uint64 + DelayedCount uint64 +} + +var DefaultSnapSyncConfig = SnapSyncConfig{ + Enabled: false, + PrevBatchMessageCount: 0, + BatchCount: 0, + DelayedCount: 0, + PrevDelayedRead: 0, +} + type ConfigFetcher interface { Get() *Config Start(context.Context) @@ -373,7 +394,7 @@ func createNodeImpl( dataSigner signature.DataSignerFunc, fatalErrChan chan error, parentChainID *big.Int, - blobReader arbstate.BlobReader, + blobReader daprovider.BlobReader, ) (*Node, error) { config := configFetcher.Get() @@ -384,17 +405,10 @@ func createNodeImpl( l2ChainId := l2Config.ChainID.Uint64() - syncMonitor := NewSyncMonitor(&config.SyncMonitor) - var classicOutbox *ClassicOutboxRetriever - classicMsgDb, err := stack.OpenDatabase("classic-msg", 0, 0, "", true) - if err != nil { - if l2Config.ArbitrumChainParams.GenesisBlockNum > 0 { - log.Warn("Classic Msg Database not found", "err", err) - } - classicOutbox = nil - } else { - classicOutbox = NewClassicOutboxRetriever(classicMsgDb) + syncConfigFetcher := func() *SyncMonitorConfig { + return &configFetcher.Get().SyncMonitor } + syncMonitor := NewSyncMonitor(syncConfigFetcher) var l1Reader *headerreader.HeaderReader if config.ParentChainReader.Enable { @@ -418,7 +432,7 @@ func createNodeImpl( } transactionStreamerConfigFetcher := func() *TransactionStreamerConfig { return &configFetcher.Get().TransactionStreamer } - txStreamer, err := NewTransactionStreamer(arbDb, l2Config, exec, broadcastServer, fatalErrChan, transactionStreamerConfigFetcher) + txStreamer, err := NewTransactionStreamer(arbDb, l2Config, exec, broadcastServer, fatalErrChan, transactionStreamerConfigFetcher, &configFetcher.Get().SnapSyncTest) if err != nil { return nil, err } @@ -491,7 +505,6 @@ func createNodeImpl( SeqCoordinator: coordinator, MaintenanceRunner: maintenanceRunner, DASLifecycleManager: nil, - ClassicOutboxRetriever: classicOutbox, SyncMonitor: syncMonitor, configFetcher: configFetcher, ctx: ctx, @@ -538,7 +551,18 @@ func createNodeImpl( return nil, errors.New("a data availability service is required for this chain, but it was not configured") } - inboxTracker, err := NewInboxTracker(arbDb, txStreamer, daReader, blobReader) + // We support a nil txStreamer for the pruning code + if txStreamer != nil && txStreamer.chainConfig.ArbitrumChainParams.DataAvailabilityCommittee && daReader == nil { + return nil, errors.New("data availability service required but unconfigured") + } + var dapReaders []daprovider.Reader + if daReader != nil { + dapReaders = append(dapReaders, daprovider.NewReaderForDAS(daReader)) + } + if blobReader != nil { + dapReaders = append(dapReaders, daprovider.NewReaderForBlobReader(blobReader)) + } + inboxTracker, err := NewInboxTracker(arbDb, txStreamer, dapReaders, config.SnapSyncTest) if err != nil { return nil, err } @@ -549,15 +573,14 @@ func createNodeImpl( txStreamer.SetInboxReaders(inboxReader, delayedBridge) var statelessBlockValidator *staker.StatelessBlockValidator - if config.BlockValidator.ValidationServerConfigs[0].URL != "" { + if config.BlockValidator.RedisValidationClientConfig.Enabled() || config.BlockValidator.ValidationServerConfigs[0].URL != "" { statelessBlockValidator, err = staker.NewStatelessBlockValidator( inboxReader, inboxTracker, txStreamer, exec, rawdb.NewTable(arbDb, storage.BlockValidatorPrefix), - daReader, - blobReader, + dapReaders, func() *staker.BlockValidatorConfig { return &configFetcher.Get().BlockValidator }, stack, ) @@ -664,6 +687,10 @@ func createNodeImpl( if txOptsBatchPoster == nil && config.BatchPoster.DataPoster.ExternalSigner.URL == "" { return nil, errors.New("batchposter, but no TxOpts") } + var dapWriter daprovider.Writer + if daWriter != nil { + dapWriter = daprovider.NewWriterForDAS(daWriter) + } batchPoster, err = NewBatchPoster(ctx, &BatchPosterOpts{ DataPosterDB: rawdb.NewTable(arbDb, storage.BatchPosterPrefix), L1Reader: l1Reader, @@ -674,7 +701,7 @@ func createNodeImpl( Config: func() *BatchPosterConfig { return &configFetcher.Get().BatchPoster }, DeployInfo: deployInfo, TransactOpts: txOptsBatchPoster, - DAWriter: daWriter, + DAPWriter: dapWriter, ParentChainID: parentChainID, }) if err != nil { @@ -709,7 +736,6 @@ func createNodeImpl( SeqCoordinator: coordinator, MaintenanceRunner: maintenanceRunner, DASLifecycleManager: dasLifecycleManager, - ClassicOutboxRetriever: classicOutbox, SyncMonitor: syncMonitor, configFetcher: configFetcher, ctx: ctx, @@ -735,7 +761,7 @@ func CreateNode( dataSigner signature.DataSignerFunc, fatalErrChan chan error, parentChainID *big.Int, - blobReader arbstate.BlobReader, + blobReader daprovider.BlobReader, ) (*Node, error) { currentNode, err := createNodeImpl(ctx, stack, exec, arbDb, configFetcher, l2Config, l1client, deployInfo, txOptsValidator, txOptsBatchPoster, dataSigner, fatalErrChan, parentChainID, blobReader) if err != nil { @@ -766,22 +792,36 @@ func CreateNode( return currentNode, nil } +func (n *Node) CacheL1PriceDataOfMsg(pos arbutil.MessageIndex, callDataUnits uint64, l1GasCharged uint64) { + n.TxStreamer.CacheL1PriceDataOfMsg(pos, callDataUnits, l1GasCharged) +} + +func (n *Node) BacklogL1GasCharged() uint64 { + return n.TxStreamer.BacklogL1GasCharged() +} +func (n *Node) BacklogCallDataUnits() uint64 { + return n.TxStreamer.BacklogCallDataUnits() +} + func (n *Node) Start(ctx context.Context) error { execClient, ok := n.Execution.(*gethexec.ExecutionNode) if !ok { execClient = nil } if execClient != nil { - err := execClient.Initialize(ctx, n, n.SyncMonitor) + err := execClient.Initialize(ctx) if err != nil { return fmt.Errorf("error initializing exec client: %w", err) } } - n.SyncMonitor.Initialize(n.InboxReader, n.TxStreamer, n.SeqCoordinator, n.Execution) + n.SyncMonitor.Initialize(n.InboxReader, n.TxStreamer, n.SeqCoordinator) err := n.Stack.Start() if err != nil { return fmt.Errorf("error starting geth stack: %w", err) } + if execClient != nil { + execClient.SetConsensusClient(n) + } err = n.Execution.Start(ctx) if err != nil { return fmt.Errorf("error starting exec client: %w", err) @@ -894,6 +934,7 @@ func (n *Node) Start(ctx context.Context) error { if n.configFetcher != nil { n.configFetcher.Start(ctx) } + n.SyncMonitor.Start(ctx) return nil } @@ -947,6 +988,7 @@ func (n *Node) StopAndWait() { // Just stops the redis client (most other stuff was stopped earlier) n.SeqCoordinator.StopAndWait() } + n.SyncMonitor.StopAndWait() if n.DASLifecycleManager != nil { n.DASLifecycleManager.StopAndWaitUntil(2 * time.Second) } @@ -957,3 +999,51 @@ func (n *Node) StopAndWait() { log.Error("error on stack close", "err", err) } } + +func (n *Node) FetchBatch(ctx context.Context, batchNum uint64) ([]byte, common.Hash, error) { + return n.InboxReader.GetSequencerMessageBytes(ctx, batchNum) +} + +func (n *Node) FindInboxBatchContainingMessage(message arbutil.MessageIndex) (uint64, bool, error) { + return n.InboxTracker.FindInboxBatchContainingMessage(message) +} + +func (n *Node) GetBatchParentChainBlock(seqNum uint64) (uint64, error) { + return n.InboxTracker.GetBatchParentChainBlock(seqNum) +} + +func (n *Node) FullSyncProgressMap() map[string]interface{} { + return n.SyncMonitor.FullSyncProgressMap() +} + +func (n *Node) Synced() bool { + return n.SyncMonitor.Synced() +} + +func (n *Node) SyncTargetMessageCount() arbutil.MessageIndex { + return n.SyncMonitor.SyncTargetMessageCount() +} + +// TODO: switch from pulling to pushing safe/finalized +func (n *Node) GetSafeMsgCount(ctx context.Context) (arbutil.MessageIndex, error) { + return n.InboxReader.GetSafeMsgCount(ctx) +} + +func (n *Node) GetFinalizedMsgCount(ctx context.Context) (arbutil.MessageIndex, error) { + return n.InboxReader.GetFinalizedMsgCount(ctx) +} + +func (n *Node) WriteMessageFromSequencer(pos arbutil.MessageIndex, msgWithMeta arbostypes.MessageWithMetadata, msgResult execution.MessageResult) error { + return n.TxStreamer.WriteMessageFromSequencer(pos, msgWithMeta, msgResult) +} + +func (n *Node) ExpectChosenSequencer() error { + return n.TxStreamer.ExpectChosenSequencer() +} + +func (n *Node) ValidatedMessageCount() (arbutil.MessageIndex, error) { + if n.BlockValidator == nil { + return 0, errors.New("validator not set up") + } + return n.BlockValidator.GetValidated(), nil +} diff --git a/arbnode/schema.go b/arbnode/schema.go index ddc7cf54fd..2854b7e785 100644 --- a/arbnode/schema.go +++ b/arbnode/schema.go @@ -5,6 +5,7 @@ package arbnode var ( messagePrefix []byte = []byte("m") // maps a message sequence number to a message + blockHashInputFeedPrefix []byte = []byte("b") // maps a message sequence number to a block hash received through the input feed legacyDelayedMessagePrefix []byte = []byte("d") // maps a delayed sequence number to an accumulator and a message as serialized on L1 rlpDelayedMessagePrefix []byte = []byte("e") // maps a delayed sequence number to an accumulator and an RLP encoded message parentChainBlockNumberPrefix []byte = []byte("p") // maps a delayed sequence number to a parent chain block number diff --git a/arbnode/sequencer_inbox.go b/arbnode/sequencer_inbox.go index edda4e5512..46e1edb78b 100644 --- a/arbnode/sequencer_inbox.go +++ b/arbnode/sequencer_inbox.go @@ -15,7 +15,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/solgen/go/bridgegen" @@ -159,7 +159,7 @@ func (m *SequencerInboxBatch) getSequencerData(ctx context.Context, client arbut if len(tx.BlobHashes()) == 0 { return nil, fmt.Errorf("blob batch transaction %v has no blobs", tx.Hash()) } - data := []byte{arbstate.BlobHashesHeaderFlag} + data := []byte{daprovider.BlobHashesHeaderFlag} for _, h := range tx.BlobHashes() { data = append(data, h[:]...) } diff --git a/arbnode/sync_monitor.go b/arbnode/sync_monitor.go index 99a66abde2..d3b9a7e1c6 100644 --- a/arbnode/sync_monitor.go +++ b/arbnode/sync_monitor.go @@ -2,120 +2,146 @@ package arbnode import ( "context" - "errors" - "sync/atomic" + "sync" + "time" + "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/arbutil" - "github.com/offchainlabs/nitro/execution" + "github.com/offchainlabs/nitro/util/stopwaiter" flag "github.com/spf13/pflag" ) type SyncMonitor struct { - config *SyncMonitorConfig + stopwaiter.StopWaiter + config func() *SyncMonitorConfig inboxReader *InboxReader txStreamer *TransactionStreamer coordinator *SeqCoordinator - exec execution.FullExecutionClient initialized bool + + syncTargetLock sync.Mutex + nextSyncTarget arbutil.MessageIndex + syncTarget arbutil.MessageIndex } -func NewSyncMonitor(config *SyncMonitorConfig) *SyncMonitor { +func NewSyncMonitor(config func() *SyncMonitorConfig) *SyncMonitor { return &SyncMonitor{ config: config, } } type SyncMonitorConfig struct { - BlockBuildLag uint64 `koanf:"block-build-lag"` - BlockBuildSequencerInboxLag uint64 `koanf:"block-build-sequencer-inbox-lag"` - CoordinatorMsgLag uint64 `koanf:"coordinator-msg-lag"` - SafeBlockWaitForBlockValidator bool `koanf:"safe-block-wait-for-block-validator"` - FinalizedBlockWaitForBlockValidator bool `koanf:"finalized-block-wait-for-block-validator"` + MsgLag time.Duration `koanf:"msg-lag"` } var DefaultSyncMonitorConfig = SyncMonitorConfig{ - BlockBuildLag: 20, - BlockBuildSequencerInboxLag: 0, - CoordinatorMsgLag: 15, - SafeBlockWaitForBlockValidator: false, - FinalizedBlockWaitForBlockValidator: false, + MsgLag: time.Second, +} + +var TestSyncMonitorConfig = SyncMonitorConfig{ + MsgLag: time.Millisecond * 10, } func SyncMonitorConfigAddOptions(prefix string, f *flag.FlagSet) { - f.Uint64(prefix+".block-build-lag", DefaultSyncMonitorConfig.BlockBuildLag, "allowed lag between messages read and blocks built") - f.Uint64(prefix+".block-build-sequencer-inbox-lag", DefaultSyncMonitorConfig.BlockBuildSequencerInboxLag, "allowed lag between messages read from sequencer inbox and blocks built") - f.Uint64(prefix+".coordinator-msg-lag", DefaultSyncMonitorConfig.CoordinatorMsgLag, "allowed lag between local and remote messages") - f.Bool(prefix+".safe-block-wait-for-block-validator", DefaultSyncMonitorConfig.SafeBlockWaitForBlockValidator, "wait for block validator to complete before returning safe block number") - f.Bool(prefix+".finalized-block-wait-for-block-validator", DefaultSyncMonitorConfig.FinalizedBlockWaitForBlockValidator, "wait for block validator to complete before returning finalized block number") + f.Duration(prefix+".msg-lag", DefaultSyncMonitorConfig.MsgLag, "allowed msg lag while still considered in sync") } -func (s *SyncMonitor) Initialize(inboxReader *InboxReader, txStreamer *TransactionStreamer, coordinator *SeqCoordinator, exec execution.FullExecutionClient) { +func (s *SyncMonitor) Initialize(inboxReader *InboxReader, txStreamer *TransactionStreamer, coordinator *SeqCoordinator) { s.inboxReader = inboxReader s.txStreamer = txStreamer s.coordinator = coordinator - s.exec = exec s.initialized = true } -func (s *SyncMonitor) SyncProgressMap() map[string]interface{} { - syncing := false - res := make(map[string]interface{}) +func (s *SyncMonitor) updateSyncTarget(ctx context.Context) time.Duration { + nextSyncTarget, err := s.maxMessageCount() + if err != nil { + log.Warn("failed readin max msg count", "err", err) + return s.config().MsgLag + } + s.syncTargetLock.Lock() + defer s.syncTargetLock.Unlock() + s.syncTarget = s.nextSyncTarget + s.nextSyncTarget = nextSyncTarget + return s.config().MsgLag +} - if !s.initialized { - res["err"] = "uninitialized" - return res +func (s *SyncMonitor) SyncTargetMessageCount() arbutil.MessageIndex { + s.syncTargetLock.Lock() + defer s.syncTargetLock.Unlock() + return s.syncTarget +} + +func (s *SyncMonitor) maxMessageCount() (arbutil.MessageIndex, error) { + msgCount, err := s.txStreamer.GetMessageCount() + if err != nil { + return 0, err } - broadcasterQueuedMessagesPos := atomic.LoadUint64(&(s.txStreamer.broadcasterQueuedMessagesPos)) + pending := s.txStreamer.FeedPendingMessageCount() + if pending > msgCount { + msgCount = pending + } - if broadcasterQueuedMessagesPos != 0 { // unprocessed feed - syncing = true + if s.inboxReader != nil { + batchProcessed := s.inboxReader.GetLastReadBatchCount() + + if batchProcessed > 0 { + batchMsgCount, err := s.inboxReader.Tracker().GetBatchMessageCount(batchProcessed - 1) + if err != nil { + return msgCount, err + } + if batchMsgCount > msgCount { + msgCount = batchMsgCount + } + } } - res["broadcasterQueuedMessagesPos"] = broadcasterQueuedMessagesPos - builtMessageCount, err := s.exec.HeadMessageNumber() - if err != nil { - res["builtMessageCountError"] = err.Error() - syncing = true - builtMessageCount = 0 - } else { - blockNum := s.exec.MessageIndexToBlockNumber(builtMessageCount) - res["blockNum"] = blockNum - builtMessageCount++ - res["messageOfLastBlock"] = builtMessageCount + if s.coordinator != nil { + coordinatorMessageCount, err := s.coordinator.GetRemoteMsgCount() //NOTE: this creates a remote call + if err != nil { + return msgCount, err + } + if coordinatorMessageCount > msgCount { + msgCount = coordinatorMessageCount + } } + return msgCount, nil +} + +func (s *SyncMonitor) FullSyncProgressMap() map[string]interface{} { + res := make(map[string]interface{}) + + if !s.initialized { + res["err"] = "uninitialized" + return res + } + + syncTarget := s.SyncTargetMessageCount() + res["syncTargetMsgCount"] = syncTarget + msgCount, err := s.txStreamer.GetMessageCount() if err != nil { res["msgCountError"] = err.Error() - syncing = true - } else { - res["msgCount"] = msgCount - if builtMessageCount+arbutil.MessageIndex(s.config.BlockBuildLag) < msgCount { - syncing = true - } + return res } + res["msgCount"] = msgCount + + res["feedPendingMessageCount"] = s.txStreamer.FeedPendingMessageCount() if s.inboxReader != nil { batchSeen := s.inboxReader.GetLastSeenBatchCount() - _, batchProcessed := s.inboxReader.GetLastReadBlockAndBatchCount() - - if (batchSeen == 0) || // error or not yet read inbox - (batchProcessed < batchSeen) { // unprocessed inbox messages - syncing = true - } res["batchSeen"] = batchSeen + + batchProcessed := s.inboxReader.GetLastReadBatchCount() res["batchProcessed"] = batchProcessed - processedMetadata, err := s.inboxReader.Tracker().GetBatchMetadata(batchProcessed - 1) + processedBatchMsgs, err := s.inboxReader.Tracker().GetBatchMessageCount(batchProcessed - 1) if err != nil { res["batchMetadataError"] = err.Error() - syncing = true } else { - res["messageOfProcessedBatch"] = processedMetadata.MessageCount - if builtMessageCount+arbutil.MessageIndex(s.config.BlockBuildSequencerInboxLag) < processedMetadata.MessageCount { - syncing = true - } + res["messageOfProcessedBatch"] = processedBatchMsgs } l1reader := s.inboxReader.l1Reader @@ -135,73 +161,55 @@ func (s *SyncMonitor) SyncProgressMap() map[string]interface{} { coordinatorMessageCount, err := s.coordinator.GetRemoteMsgCount() //NOTE: this creates a remote call if err != nil { res["coordinatorMsgCountError"] = err.Error() - syncing = true } else { res["coordinatorMessageCount"] = coordinatorMessageCount - if msgCount+arbutil.MessageIndex(s.config.CoordinatorMsgLag) < coordinatorMessageCount { - syncing = true - } } } - if !syncing { - return make(map[string]interface{}) - } - return res } -func (s *SyncMonitor) SafeBlockNumber(ctx context.Context) (uint64, error) { - if s.inboxReader == nil || !s.initialized { - return 0, errors.New("not set up for safeblock") - } - msg, err := s.inboxReader.GetSafeMsgCount(ctx) - if err != nil { - return 0, err - } - // If SafeBlockWaitForBlockValidator is true, we want to wait for the block validator to finish - if s.config.SafeBlockWaitForBlockValidator { - latestValidatedCount, err := s.getLatestValidatedCount() - if err != nil { - return 0, err - } - if msg > latestValidatedCount { - msg = latestValidatedCount - } +func (s *SyncMonitor) SyncProgressMap() map[string]interface{} { + if s.Synced() { + return make(map[string]interface{}) } - block := s.exec.MessageIndexToBlockNumber(msg - 1) - return block, nil + + return s.FullSyncProgressMap() } -func (s *SyncMonitor) getLatestValidatedCount() (arbutil.MessageIndex, error) { - if s.txStreamer.validator == nil { - return 0, errors.New("validator not set up") - } - return s.txStreamer.validator.GetValidated(), nil +func (s *SyncMonitor) Start(ctx_in context.Context) { + s.StopWaiter.Start(ctx_in, s) + s.CallIteratively(s.updateSyncTarget) } -func (s *SyncMonitor) FinalizedBlockNumber(ctx context.Context) (uint64, error) { - if s.inboxReader == nil || !s.initialized { - return 0, errors.New("not set up for safeblock") +func (s *SyncMonitor) Synced() bool { + if !s.initialized { + return false } - msg, err := s.inboxReader.GetFinalizedMsgCount(ctx) + if !s.Started() { + return false + } + syncTarget := s.SyncTargetMessageCount() + + msgCount, err := s.txStreamer.GetMessageCount() if err != nil { - return 0, err + return false } - // If FinalizedBlockWaitForBlockValidator is true, we want to wait for the block validator to finish - if s.config.FinalizedBlockWaitForBlockValidator { - latestValidatedCount, err := s.getLatestValidatedCount() - if err != nil { - return 0, err + + if syncTarget > msgCount { + return false + } + + if s.inboxReader != nil { + batchSeen := s.inboxReader.GetLastSeenBatchCount() + if batchSeen == 0 { + return false } - if msg > latestValidatedCount { - msg = latestValidatedCount + batchProcessed := s.inboxReader.GetLastReadBatchCount() + + if batchProcessed < batchSeen { + return false } } - block := s.exec.MessageIndexToBlockNumber(msg - 1) - return block, nil -} - -func (s *SyncMonitor) Synced() bool { - return len(s.SyncProgressMap()) == 0 + return true } diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 7e9cf1dbad..c948bd8169 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -50,9 +50,10 @@ type TransactionStreamer struct { execLastMsgCount arbutil.MessageIndex validator *staker.BlockValidator - db ethdb.Database - fatalErrChan chan<- error - config TransactionStreamerConfigFetcher + db ethdb.Database + fatalErrChan chan<- error + config TransactionStreamerConfigFetcher + snapSyncConfig *SnapSyncConfig insertionMutex sync.Mutex // cannot be acquired while reorgMutex is held reorgMutex sync.RWMutex @@ -60,7 +61,7 @@ type TransactionStreamer struct { nextAllowedFeedReorgLog time.Time - broadcasterQueuedMessages []arbostypes.MessageWithMetadata + broadcasterQueuedMessages []arbostypes.MessageWithMetadataAndBlockHash broadcasterQueuedMessagesPos uint64 broadcasterQueuedMessagesActiveReorg bool @@ -68,6 +69,9 @@ type TransactionStreamer struct { broadcastServer *broadcaster.Broadcaster inboxReader *InboxReader delayedBridge *DelayedBridge + + cachedL1PriceDataMutex sync.RWMutex + cachedL1PriceData *L1PriceData } type TransactionStreamerConfig struct { @@ -103,6 +107,7 @@ func NewTransactionStreamer( broadcastServer *broadcaster.Broadcaster, fatalErrChan chan<- error, config TransactionStreamerConfigFetcher, + snapSyncConfig *SnapSyncConfig, ) (*TransactionStreamer, error) { streamer := &TransactionStreamer{ exec: exec, @@ -112,8 +117,11 @@ func NewTransactionStreamer( broadcastServer: broadcastServer, fatalErrChan: fatalErrChan, config: config, + snapSyncConfig: snapSyncConfig, + cachedL1PriceData: &L1PriceData{ + msgToL1PriceData: []L1PriceDataOfMsg{}, + }, } - streamer.exec.SetTransactionStreamer(streamer) err := streamer.cleanupInconsistentState() if err != nil { return nil, err @@ -121,6 +129,130 @@ func NewTransactionStreamer( return streamer, nil } +type L1PriceDataOfMsg struct { + callDataUnits uint64 + cummulativeCallDataUnits uint64 + l1GasCharged uint64 + cummulativeL1GasCharged uint64 +} + +type L1PriceData struct { + startOfL1PriceDataCache arbutil.MessageIndex + endOfL1PriceDataCache arbutil.MessageIndex + msgToL1PriceData []L1PriceDataOfMsg + currentEstimateOfL1GasPrice uint64 +} + +// Represents a block's hash in the database. +// Necessary because RLP decoder doesn't produce nil values by default. +type blockHashDBValue struct { + BlockHash *common.Hash `rlp:"nil"` +} + +const ( + BlockHashMismatchLogMsg = "BlockHash from feed doesn't match locally computed hash. Check feed source." +) + +func (s *TransactionStreamer) CurrentEstimateOfL1GasPrice() uint64 { + s.cachedL1PriceDataMutex.Lock() + defer s.cachedL1PriceDataMutex.Unlock() + + currentEstimate, err := s.exec.GetL1GasPriceEstimate() + if err != nil { + log.Error("error fetching current L2 estimate of L1 gas price hence reusing cached estimate", "err", err) + } else { + s.cachedL1PriceData.currentEstimateOfL1GasPrice = currentEstimate + } + return s.cachedL1PriceData.currentEstimateOfL1GasPrice +} + +func (s *TransactionStreamer) BacklogCallDataUnits() uint64 { + s.cachedL1PriceDataMutex.RLock() + defer s.cachedL1PriceDataMutex.RUnlock() + + size := len(s.cachedL1PriceData.msgToL1PriceData) + if size == 0 { + return 0 + } + return (s.cachedL1PriceData.msgToL1PriceData[size-1].cummulativeCallDataUnits - + s.cachedL1PriceData.msgToL1PriceData[0].cummulativeCallDataUnits + + s.cachedL1PriceData.msgToL1PriceData[0].callDataUnits) +} + +func (s *TransactionStreamer) BacklogL1GasCharged() uint64 { + s.cachedL1PriceDataMutex.RLock() + defer s.cachedL1PriceDataMutex.RUnlock() + + size := len(s.cachedL1PriceData.msgToL1PriceData) + if size == 0 { + return 0 + } + return (s.cachedL1PriceData.msgToL1PriceData[size-1].cummulativeL1GasCharged - + s.cachedL1PriceData.msgToL1PriceData[0].cummulativeL1GasCharged + + s.cachedL1PriceData.msgToL1PriceData[0].l1GasCharged) +} + +func (s *TransactionStreamer) TrimCache(to arbutil.MessageIndex) { + s.cachedL1PriceDataMutex.Lock() + defer s.cachedL1PriceDataMutex.Unlock() + + if to < s.cachedL1PriceData.startOfL1PriceDataCache { + log.Info("trying to trim older cache which doesnt exist anymore") + } else if to >= s.cachedL1PriceData.endOfL1PriceDataCache { + s.cachedL1PriceData.startOfL1PriceDataCache = 0 + s.cachedL1PriceData.endOfL1PriceDataCache = 0 + s.cachedL1PriceData.msgToL1PriceData = []L1PriceDataOfMsg{} + } else { + newStart := to - s.cachedL1PriceData.startOfL1PriceDataCache + 1 + s.cachedL1PriceData.msgToL1PriceData = s.cachedL1PriceData.msgToL1PriceData[newStart:] + s.cachedL1PriceData.startOfL1PriceDataCache = to + 1 + } +} + +func (s *TransactionStreamer) CacheL1PriceDataOfMsg(seqNum arbutil.MessageIndex, callDataUnits uint64, l1GasCharged uint64) { + s.cachedL1PriceDataMutex.Lock() + defer s.cachedL1PriceDataMutex.Unlock() + + resetCache := func() { + s.cachedL1PriceData.startOfL1PriceDataCache = seqNum + s.cachedL1PriceData.endOfL1PriceDataCache = seqNum + s.cachedL1PriceData.msgToL1PriceData = []L1PriceDataOfMsg{{ + callDataUnits: callDataUnits, + cummulativeCallDataUnits: callDataUnits, + l1GasCharged: l1GasCharged, + cummulativeL1GasCharged: l1GasCharged, + }} + } + size := len(s.cachedL1PriceData.msgToL1PriceData) + if size == 0 || + s.cachedL1PriceData.startOfL1PriceDataCache == 0 || + s.cachedL1PriceData.endOfL1PriceDataCache == 0 || + arbutil.MessageIndex(size) != s.cachedL1PriceData.endOfL1PriceDataCache-s.cachedL1PriceData.startOfL1PriceDataCache+1 { + resetCache() + return + } + if seqNum != s.cachedL1PriceData.endOfL1PriceDataCache+1 { + if seqNum > s.cachedL1PriceData.endOfL1PriceDataCache+1 { + log.Info("message position higher then current end of l1 price data cache, resetting cache to this message") + resetCache() + } else if seqNum < s.cachedL1PriceData.startOfL1PriceDataCache { + log.Info("message position lower than start of l1 price data cache, ignoring") + } else { + log.Info("message position already seen in l1 price data cache, ignoring") + } + } else { + cummulativeCallDataUnits := s.cachedL1PriceData.msgToL1PriceData[size-1].cummulativeCallDataUnits + cummulativeL1GasCharged := s.cachedL1PriceData.msgToL1PriceData[size-1].cummulativeL1GasCharged + s.cachedL1PriceData.msgToL1PriceData = append(s.cachedL1PriceData.msgToL1PriceData, L1PriceDataOfMsg{ + callDataUnits: callDataUnits, + cummulativeCallDataUnits: cummulativeCallDataUnits + callDataUnits, + l1GasCharged: l1GasCharged, + cummulativeL1GasCharged: cummulativeL1GasCharged + l1GasCharged, + }) + s.cachedL1PriceData.endOfL1PriceDataCache = seqNum + } +} + // Encodes a uint64 as bytes in a lexically sortable manner for database iteration. // Generally this is only used for database keys, which need sorted. // A shorter RLP encoding is usually used for database values. @@ -161,6 +293,10 @@ func (s *TransactionStreamer) SetInboxReaders(inboxReader *InboxReader, delayedB s.delayedBridge = delayedBridge } +func (s *TransactionStreamer) ChainConfig() *params.ChainConfig { + return s.chainConfig +} + func (s *TransactionStreamer) cleanupInconsistentState() error { // If it doesn't exist yet, set the message count to 0 hasMessageCount, err := s.db.Has(messageCountKey) @@ -248,7 +384,7 @@ func deleteFromRange(ctx context.Context, db ethdb.Database, prefix []byte, star // The insertion mutex must be held. This acquires the reorg mutex. // Note: oldMessages will be empty if reorgHook is nil -func (s *TransactionStreamer) reorg(batch ethdb.Batch, count arbutil.MessageIndex, newMessages []arbostypes.MessageWithMetadata) error { +func (s *TransactionStreamer) reorg(batch ethdb.Batch, count arbutil.MessageIndex, newMessages []arbostypes.MessageWithMetadataAndBlockHash) error { if count == 0 { return errors.New("cannot reorg out init message") } @@ -337,11 +473,20 @@ func (s *TransactionStreamer) reorg(batch ethdb.Batch, count arbutil.MessageInde s.reorgMutex.Lock() defer s.reorgMutex.Unlock() - err = s.exec.Reorg(count, newMessages, oldMessages) + messagesResults, err := s.exec.Reorg(count, newMessages, oldMessages) if err != nil { return err } + messagesWithComputedBlockHash := make([]arbostypes.MessageWithMetadataAndBlockHash, 0, len(messagesResults)) + for i := 0; i < len(messagesResults); i++ { + messagesWithComputedBlockHash = append(messagesWithComputedBlockHash, arbostypes.MessageWithMetadataAndBlockHash{ + MessageWithMeta: newMessages[i].MessageWithMeta, + BlockHash: &messagesResults[i].BlockHash, + }) + } + s.broadcastMessages(messagesWithComputedBlockHash, count) + if s.validator != nil { err = s.validator.Reorg(s.GetContext(), count) if err != nil { @@ -349,6 +494,10 @@ func (s *TransactionStreamer) reorg(batch ethdb.Batch, count arbutil.MessageInde } } + err = deleteStartingAt(s.db, batch, blockHashInputFeedPrefix, uint64ToKey(uint64(count))) + if err != nil { + return err + } err = deleteStartingAt(s.db, batch, messagePrefix, uint64ToKey(uint64(count))) if err != nil { return err @@ -378,6 +527,10 @@ func dbKey(prefix []byte, pos uint64) []byte { return key } +func isErrNotFound(err error) bool { + return errors.Is(err, leveldb.ErrNotFound) || errors.Is(err, pebble.ErrNotFound) +} + // Note: if changed to acquire the mutex, some internal users may need to be updated to a non-locking version. func (s *TransactionStreamer) GetMessage(seqNum arbutil.MessageIndex) (*arbostypes.MessageWithMetadata, error) { key := dbKey(messagePrefix, uint64(seqNum)) @@ -394,6 +547,36 @@ func (s *TransactionStreamer) GetMessage(seqNum arbutil.MessageIndex) (*arbostyp return &message, nil } +func (s *TransactionStreamer) getMessageWithMetadataAndBlockHash(seqNum arbutil.MessageIndex) (*arbostypes.MessageWithMetadataAndBlockHash, error) { + msg, err := s.GetMessage(seqNum) + if err != nil { + return nil, err + } + + // Get block hash. + // To keep it backwards compatible, since it is possible that a message related + // to a sequence number exists in the database, but the block hash doesn't. + key := dbKey(blockHashInputFeedPrefix, uint64(seqNum)) + var blockHash *common.Hash + data, err := s.db.Get(key) + if err == nil { + var blockHashDBVal blockHashDBValue + err = rlp.DecodeBytes(data, &blockHashDBVal) + if err != nil { + return nil, err + } + blockHash = blockHashDBVal.BlockHash + } else if !isErrNotFound(err) { + return nil, err + } + + msgWithBlockHash := arbostypes.MessageWithMetadataAndBlockHash{ + MessageWithMeta: *msg, + BlockHash: blockHash, + } + return &msgWithBlockHash, nil +} + // Note: if changed to acquire the mutex, some internal users may need to be updated to a non-locking version. func (s *TransactionStreamer) GetMessageCount() (arbutil.MessageIndex, error) { posBytes, err := s.db.Get(messageCountKey) @@ -427,12 +610,27 @@ func (s *TransactionStreamer) AddMessages(pos arbutil.MessageIndex, messagesAreC return s.AddMessagesAndEndBatch(pos, messagesAreConfirmed, messages, nil) } +func (s *TransactionStreamer) FeedPendingMessageCount() arbutil.MessageIndex { + pos := atomic.LoadUint64(&s.broadcasterQueuedMessagesPos) + if pos == 0 { + return 0 + } + + s.insertionMutex.Lock() + defer s.insertionMutex.Unlock() + pos = atomic.LoadUint64(&s.broadcasterQueuedMessagesPos) + if pos == 0 { + return 0 + } + return arbutil.MessageIndex(pos + uint64(len(s.broadcasterQueuedMessages))) +} + func (s *TransactionStreamer) AddBroadcastMessages(feedMessages []*m.BroadcastFeedMessage) error { if len(feedMessages) == 0 { return nil } broadcastStartPos := feedMessages[0].SequenceNumber - var messages []arbostypes.MessageWithMetadata + var messages []arbostypes.MessageWithMetadataAndBlockHash broadcastAfterPos := broadcastStartPos for _, feedMessage := range feedMessages { if broadcastAfterPos != feedMessage.SequenceNumber { @@ -441,7 +639,11 @@ func (s *TransactionStreamer) AddBroadcastMessages(feedMessages []*m.BroadcastFe if feedMessage.Message.Message == nil || feedMessage.Message.Message.Header == nil { return fmt.Errorf("invalid feed message at sequence number %v", feedMessage.SequenceNumber) } - messages = append(messages, feedMessage.Message) + msgWithBlockHash := arbostypes.MessageWithMetadataAndBlockHash{ + MessageWithMeta: feedMessage.Message, + BlockHash: feedMessage.BlockHash, + } + messages = append(messages, msgWithBlockHash) broadcastAfterPos++ } @@ -460,7 +662,7 @@ func (s *TransactionStreamer) AddBroadcastMessages(feedMessages []*m.BroadcastFe messages = messages[dups:] broadcastStartPos += arbutil.MessageIndex(dups) if oldMsg != nil { - s.logReorg(broadcastStartPos, oldMsg, &messages[0], false) + s.logReorg(broadcastStartPos, oldMsg, &messages[0].MessageWithMeta, false) } if len(messages) == 0 { // No new messages received @@ -510,7 +712,7 @@ func (s *TransactionStreamer) AddBroadcastMessages(feedMessages []*m.BroadcastFe if broadcastStartPos > 0 { _, err := s.GetMessage(broadcastStartPos - 1) if err != nil { - if !errors.Is(err, leveldb.ErrNotFound) && !errors.Is(err, pebble.ErrNotFound) { + if !isErrNotFound(err) { return err } // Message before current message doesn't exist in database, so don't add current messages yet @@ -562,9 +764,18 @@ func endBatch(batch ethdb.Batch) error { } func (s *TransactionStreamer) AddMessagesAndEndBatch(pos arbutil.MessageIndex, messagesAreConfirmed bool, messages []arbostypes.MessageWithMetadata, batch ethdb.Batch) error { + messagesWithBlockHash := make([]arbostypes.MessageWithMetadataAndBlockHash, 0, len(messages)) + for _, message := range messages { + messagesWithBlockHash = append(messagesWithBlockHash, arbostypes.MessageWithMetadataAndBlockHash{ + MessageWithMeta: message, + }) + } + if messagesAreConfirmed { + // Trim confirmed messages from l1pricedataCache + s.TrimCache(pos + arbutil.MessageIndex(len(messages))) s.reorgMutex.RLock() - dups, _, _, err := s.countDuplicateMessages(pos, messages, nil) + dups, _, _, err := s.countDuplicateMessages(pos, messagesWithBlockHash, nil) s.reorgMutex.RUnlock() if err != nil { return err @@ -581,10 +792,13 @@ func (s *TransactionStreamer) AddMessagesAndEndBatch(pos arbutil.MessageIndex, m s.insertionMutex.Lock() defer s.insertionMutex.Unlock() - return s.addMessagesAndEndBatchImpl(pos, messagesAreConfirmed, messages, batch) + return s.addMessagesAndEndBatchImpl(pos, messagesAreConfirmed, messagesWithBlockHash, batch) } func (s *TransactionStreamer) getPrevPrevDelayedRead(pos arbutil.MessageIndex) (uint64, error) { + if s.snapSyncConfig.Enabled && uint64(pos) == s.snapSyncConfig.PrevBatchMessageCount { + return s.snapSyncConfig.PrevDelayedRead, nil + } var prevDelayedRead uint64 if pos > 0 { prevMsg, err := s.GetMessage(pos - 1) @@ -599,7 +813,7 @@ func (s *TransactionStreamer) getPrevPrevDelayedRead(pos arbutil.MessageIndex) ( func (s *TransactionStreamer) countDuplicateMessages( pos arbutil.MessageIndex, - messages []arbostypes.MessageWithMetadata, + messages []arbostypes.MessageWithMetadataAndBlockHash, batch *ethdb.Batch, ) (int, bool, *arbostypes.MessageWithMetadata, error) { curMsg := 0 @@ -620,7 +834,7 @@ func (s *TransactionStreamer) countDuplicateMessages( return 0, false, nil, err } nextMessage := messages[curMsg] - wantMessage, err := rlp.EncodeToBytes(nextMessage) + wantMessage, err := rlp.EncodeToBytes(nextMessage.MessageWithMeta) if err != nil { return 0, false, nil, err } @@ -636,12 +850,12 @@ func (s *TransactionStreamer) countDuplicateMessages( return curMsg, true, nil, nil } var duplicateMessage bool - if nextMessage.Message != nil { - if dbMessageParsed.Message.BatchGasCost == nil || nextMessage.Message.BatchGasCost == nil { + if nextMessage.MessageWithMeta.Message != nil { + if dbMessageParsed.Message.BatchGasCost == nil || nextMessage.MessageWithMeta.Message.BatchGasCost == nil { // Remove both of the batch gas costs and see if the messages still differ - nextMessageCopy := nextMessage + nextMessageCopy := nextMessage.MessageWithMeta nextMessageCopy.Message = new(arbostypes.L1IncomingMessage) - *nextMessageCopy.Message = *nextMessage.Message + *nextMessageCopy.Message = *nextMessage.MessageWithMeta.Message batchGasCostBkup := dbMessageParsed.Message.BatchGasCost dbMessageParsed.Message.BatchGasCost = nil nextMessageCopy.Message.BatchGasCost = nil @@ -649,7 +863,7 @@ func (s *TransactionStreamer) countDuplicateMessages( // Actually this isn't a reorg; only the batch gas costs differed duplicateMessage = true // If possible - update the message in the database to add the gas cost cache. - if batch != nil && nextMessage.Message.BatchGasCost != nil { + if batch != nil && nextMessage.MessageWithMeta.Message.BatchGasCost != nil { if *batch == nil { *batch = s.db.NewBatch() } @@ -693,7 +907,7 @@ func (s *TransactionStreamer) logReorg(pos arbutil.MessageIndex, dbMsg *arbostyp } -func (s *TransactionStreamer) addMessagesAndEndBatchImpl(messageStartPos arbutil.MessageIndex, messagesAreConfirmed bool, messages []arbostypes.MessageWithMetadata, batch ethdb.Batch) error { +func (s *TransactionStreamer) addMessagesAndEndBatchImpl(messageStartPos arbutil.MessageIndex, messagesAreConfirmed bool, messages []arbostypes.MessageWithMetadataAndBlockHash, batch ethdb.Batch) error { var confirmedReorg bool var oldMsg *arbostypes.MessageWithMetadata var lastDelayedRead uint64 @@ -711,7 +925,7 @@ func (s *TransactionStreamer) addMessagesAndEndBatchImpl(messageStartPos arbutil return err } if duplicates > 0 { - lastDelayedRead = messages[duplicates-1].DelayedMessagesRead + lastDelayedRead = messages[duplicates-1].MessageWithMeta.DelayedMessagesRead messages = messages[duplicates:] messageStartPos += arbutil.MessageIndex(duplicates) } @@ -749,13 +963,13 @@ func (s *TransactionStreamer) addMessagesAndEndBatchImpl(messageStartPos arbutil return err } if duplicates > 0 { - lastDelayedRead = messages[duplicates-1].DelayedMessagesRead + lastDelayedRead = messages[duplicates-1].MessageWithMeta.DelayedMessagesRead messages = messages[duplicates:] messageStartPos += arbutil.MessageIndex(duplicates) } } if oldMsg != nil { - s.logReorg(messageStartPos, oldMsg, &messages[0], confirmedReorg) + s.logReorg(messageStartPos, oldMsg, &messages[0].MessageWithMeta, confirmedReorg) } if feedReorg { @@ -775,12 +989,12 @@ func (s *TransactionStreamer) addMessagesAndEndBatchImpl(messageStartPos arbutil // Validate delayed message counts of remaining messages for i, msg := range messages { msgPos := messageStartPos + arbutil.MessageIndex(i) - diff := msg.DelayedMessagesRead - lastDelayedRead + diff := msg.MessageWithMeta.DelayedMessagesRead - lastDelayedRead if diff != 0 && diff != 1 { - return fmt.Errorf("attempted to insert jump from %v delayed messages read to %v delayed messages read at message index %v", lastDelayedRead, msg.DelayedMessagesRead, msgPos) + return fmt.Errorf("attempted to insert jump from %v delayed messages read to %v delayed messages read at message index %v", lastDelayedRead, msg.MessageWithMeta.DelayedMessagesRead, msgPos) } - lastDelayedRead = msg.DelayedMessagesRead - if msg.Message == nil { + lastDelayedRead = msg.MessageWithMeta.DelayedMessagesRead + if msg.MessageWithMeta.Message == nil { return fmt.Errorf("attempted to insert nil message at position %v", msgPos) } } @@ -820,10 +1034,6 @@ func (s *TransactionStreamer) addMessagesAndEndBatchImpl(messageStartPos arbutil return nil } -func (s *TransactionStreamer) FetchBatch(batchNum uint64) ([]byte, common.Hash, error) { - return s.inboxReader.GetSequencerMessageBytes(context.TODO(), batchNum) -} - // The caller must hold the insertionMutex func (s *TransactionStreamer) ExpectChosenSequencer() error { if s.coordinator != nil { @@ -834,7 +1044,11 @@ func (s *TransactionStreamer) ExpectChosenSequencer() error { return nil } -func (s *TransactionStreamer) WriteMessageFromSequencer(pos arbutil.MessageIndex, msgWithMeta arbostypes.MessageWithMetadata) error { +func (s *TransactionStreamer) WriteMessageFromSequencer( + pos arbutil.MessageIndex, + msgWithMeta arbostypes.MessageWithMetadata, + msgResult execution.MessageResult, +) error { if err := s.ExpectChosenSequencer(); err != nil { return err } @@ -858,17 +1072,19 @@ func (s *TransactionStreamer) WriteMessageFromSequencer(pos arbutil.MessageIndex } } - if err := s.writeMessages(pos, []arbostypes.MessageWithMetadata{msgWithMeta}, nil); err != nil { + msgWithBlockHash := arbostypes.MessageWithMetadataAndBlockHash{ + MessageWithMeta: msgWithMeta, + BlockHash: &msgResult.BlockHash, + } + + if err := s.writeMessages(pos, []arbostypes.MessageWithMetadataAndBlockHash{msgWithBlockHash}, nil); err != nil { return err } + s.broadcastMessages([]arbostypes.MessageWithMetadataAndBlockHash{msgWithBlockHash}, pos) return nil } -func (s *TransactionStreamer) GenesisBlockNumber() uint64 { - return s.chainConfig.ArbitrumChainParams.GenesisBlockNum -} - // PauseReorgs until a matching call to ResumeReorgs (may be called concurrently) func (s *TransactionStreamer) PauseReorgs() { s.reorgMutex.RLock() @@ -885,18 +1101,44 @@ func (s *TransactionStreamer) PopulateFeedBacklog() error { return s.inboxReader.tracker.PopulateFeedBacklog(s.broadcastServer) } -func (s *TransactionStreamer) writeMessage(pos arbutil.MessageIndex, msg arbostypes.MessageWithMetadata, batch ethdb.Batch) error { +func (s *TransactionStreamer) writeMessage(pos arbutil.MessageIndex, msg arbostypes.MessageWithMetadataAndBlockHash, batch ethdb.Batch) error { + // write message with metadata key := dbKey(messagePrefix, uint64(pos)) - msgBytes, err := rlp.EncodeToBytes(msg) + msgBytes, err := rlp.EncodeToBytes(msg.MessageWithMeta) + if err != nil { + return err + } + if err := batch.Put(key, msgBytes); err != nil { + return err + } + + // write block hash + blockHashDBVal := blockHashDBValue{ + BlockHash: msg.BlockHash, + } + key = dbKey(blockHashInputFeedPrefix, uint64(pos)) + msgBytes, err = rlp.EncodeToBytes(blockHashDBVal) if err != nil { return err } return batch.Put(key, msgBytes) } +func (s *TransactionStreamer) broadcastMessages( + msgs []arbostypes.MessageWithMetadataAndBlockHash, + pos arbutil.MessageIndex, +) { + if s.broadcastServer == nil { + return + } + if err := s.broadcastServer.BroadcastMessages(msgs, pos); err != nil { + log.Error("failed broadcasting messages", "pos", pos, "err", err) + } +} + // The mutex must be held, and pos must be the latest message count. // `batch` may be nil, which initializes a new batch. The batch is closed out in this function. -func (s *TransactionStreamer) writeMessages(pos arbutil.MessageIndex, messages []arbostypes.MessageWithMetadata, batch ethdb.Batch) error { +func (s *TransactionStreamer) writeMessages(pos arbutil.MessageIndex, messages []arbostypes.MessageWithMetadataAndBlockHash, batch ethdb.Batch) error { if batch == nil { batch = s.db.NewBatch() } @@ -921,12 +1163,6 @@ func (s *TransactionStreamer) writeMessages(pos arbutil.MessageIndex, messages [ default: } - if s.broadcastServer != nil { - if err := s.broadcastServer.BroadcastMessages(messages, pos); err != nil { - log.Error("failed broadcasting message", "pos", pos, "err", err) - } - } - return nil } @@ -938,8 +1174,23 @@ func (s *TransactionStreamer) ResultAtCount(count arbutil.MessageIndex) (*execut return s.exec.ResultAtPos(count - 1) } +func (s *TransactionStreamer) checkResult(msgResult *execution.MessageResult, expectedBlockHash *common.Hash) { + if expectedBlockHash == nil { + return + } + if msgResult.BlockHash != *expectedBlockHash { + log.Error( + BlockHashMismatchLogMsg, + "expected", expectedBlockHash, + "actual", msgResult.BlockHash, + ) + return + } +} + +// exposed for testing // return value: true if should be called again immediately -func (s *TransactionStreamer) executeNextMsg(ctx context.Context, exec execution.ExecutionSequencer) bool { +func (s *TransactionStreamer) ExecuteNextMsg(ctx context.Context, exec execution.ExecutionSequencer) bool { if ctx.Err() != nil { return false } @@ -963,7 +1214,7 @@ func (s *TransactionStreamer) executeNextMsg(ctx context.Context, exec execution if pos >= msgCount { return false } - msg, err := s.GetMessage(pos) + msgAndBlockHash, err := s.getMessageWithMetadataAndBlockHash(pos) if err != nil { log.Error("feedOneMsg failed to readMessage", "err", err, "pos", pos) return false @@ -977,7 +1228,8 @@ func (s *TransactionStreamer) executeNextMsg(ctx context.Context, exec execution } msgForPrefetch = msg } - if err = s.exec.DigestMessage(pos, msg, msgForPrefetch); err != nil { + msgResult, err := s.exec.DigestMessage(pos, &msgAndBlockHash.MessageWithMeta, msgForPrefetch) + if err != nil { logger := log.Warn if prevMessageCount < msgCount { logger = log.Debug @@ -985,11 +1237,20 @@ func (s *TransactionStreamer) executeNextMsg(ctx context.Context, exec execution logger("feedOneMsg failed to send message to execEngine", "err", err, "pos", pos) return false } + + s.checkResult(msgResult, msgAndBlockHash.BlockHash) + + msgWithBlockHash := arbostypes.MessageWithMetadataAndBlockHash{ + MessageWithMeta: msgAndBlockHash.MessageWithMeta, + BlockHash: &msgResult.BlockHash, + } + s.broadcastMessages([]arbostypes.MessageWithMetadataAndBlockHash{msgWithBlockHash}, pos) + return pos+1 < msgCount } func (s *TransactionStreamer) executeMessages(ctx context.Context, ignored struct{}) time.Duration { - if s.executeNextMsg(ctx, s.exec) { + if s.ExecuteNextMsg(ctx, s.exec) { return 0 } return s.config().ExecuteMessageLoopDelay diff --git a/arbos/activate_test.go b/arbos/activate_test.go new file mode 100644 index 0000000000..55440bb208 --- /dev/null +++ b/arbos/activate_test.go @@ -0,0 +1,106 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package arbos + +import ( + "math/rand" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/offchainlabs/nitro/arbos/arbosState" + "github.com/offchainlabs/nitro/arbos/programs" + "github.com/offchainlabs/nitro/util/arbmath" + "github.com/offchainlabs/nitro/util/colors" + "github.com/offchainlabs/nitro/util/testhelpers" +) + +func TestActivationDataFee(t *testing.T) { + rand.Seed(time.Now().UTC().UnixNano()) + state, _ := arbosState.NewArbosMemoryBackedArbOSState() + pricer := state.Programs().DataPricer() + time := uint64(time.Now().Unix()) + + assert := func(cond bool) { + t.Helper() + if !cond { + Fail(t, "assertion failed") + } + } + + hour := uint64(60 * 60) + commonSize := uint32(5 * 1024 * 1024) + + fee, _ := pricer.UpdateModel(0, time) + assert(fee.Uint64() == 0) + + firstHourlyFee, _ := pricer.UpdateModel(commonSize, time) + assert(firstHourlyFee.Uint64() > 0) + + capacity := uint32(programs.InitialHourlyBytes) + usage := uint32(0) + lastFee := common.Big0 + totalFees := common.Big0 + reset := func() { + capacity = uint32(programs.InitialHourlyBytes) + usage = uint32(0) + lastFee = common.Big0 + totalFees = common.Big0 + } + + reset() + for usage < capacity { + bytes := uint32(5 * 1024 * 1024) + fee, _ := pricer.UpdateModel(bytes, time+hour) + assert(arbmath.BigGreaterThan(fee, lastFee)) + + totalFees = arbmath.BigAdd(totalFees, fee) + usage += bytes + lastFee = fee + } + + // ensure the chain made enough money + minimumTotal := arbmath.UintToBig(uint64(capacity)) + minimumTotal = arbmath.BigMulByUint(minimumTotal, 59/10*1e9) + colors.PrintBlue("total ", totalFees.String(), " ", minimumTotal.String()) + assert(arbmath.BigGreaterThan(totalFees, minimumTotal)) + + // advance a bit past an hour to reset the pricer + fee, _ = pricer.UpdateModel(commonSize, time+2*hour+60) + assert(arbmath.BigEquals(fee, firstHourlyFee)) + + // buy all the capacity at once + fee, _ = pricer.UpdateModel(capacity, time+3*hour) + colors.PrintBlue("every ", fee.String(), " ", minimumTotal.String()) + assert(arbmath.BigGreaterThan(fee, minimumTotal)) + + reset() + for usage < capacity { + bytes := uint32(10 * 1024) + fee, _ := pricer.UpdateModel(bytes, time+5*hour) + assert(arbmath.BigGreaterThanOrEqual(fee, lastFee)) + + totalFees = arbmath.BigAdd(totalFees, fee) + usage += bytes + lastFee = fee + } + + // check small programs + colors.PrintBlue("small ", totalFees.String(), " ", minimumTotal.String()) + assert(arbmath.BigGreaterThan(totalFees, minimumTotal)) + + reset() + for usage < capacity { + bytes := testhelpers.RandomUint32(1, 1024*1024) + fee, _ := pricer.UpdateModel(bytes, time+7*hour) + + totalFees = arbmath.BigAdd(totalFees, fee) + usage += bytes + lastFee = fee + } + + // check random programs + colors.PrintBlue("rands ", totalFees.String(), " ", minimumTotal.String()) + assert(arbmath.BigGreaterThan(totalFees, minimumTotal)) +} diff --git a/arbos/arbosState/arbosstate.go b/arbos/arbosState/arbosstate.go index 9e3b90532e..0ac5d1380d 100644 --- a/arbos/arbosState/arbosstate.go +++ b/arbos/arbosState/arbosstate.go @@ -25,6 +25,7 @@ import ( "github.com/offchainlabs/nitro/arbos/l1pricing" "github.com/offchainlabs/nitro/arbos/l2pricing" "github.com/offchainlabs/nitro/arbos/merkleAccumulator" + "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/arbos/retryables" "github.com/offchainlabs/nitro/arbos/storage" "github.com/offchainlabs/nitro/arbos/util" @@ -36,24 +37,27 @@ import ( // persisted beyond the end of the test.) type ArbosState struct { - arbosVersion uint64 // version of the ArbOS storage format and semantics - upgradeVersion storage.StorageBackedUint64 // version we're planning to upgrade to, or 0 if not planning to upgrade - upgradeTimestamp storage.StorageBackedUint64 // when to do the planned upgrade - networkFeeAccount storage.StorageBackedAddress - l1PricingState *l1pricing.L1PricingState - l2PricingState *l2pricing.L2PricingState - retryableState *retryables.RetryableState - addressTable *addressTable.AddressTable - chainOwners *addressSet.AddressSet - sendMerkle *merkleAccumulator.MerkleAccumulator - blockhashes *blockhash.Blockhashes - chainId storage.StorageBackedBigInt - chainConfig storage.StorageBackedBytes - genesisBlockNum storage.StorageBackedUint64 - infraFeeAccount storage.StorageBackedAddress - brotliCompressionLevel storage.StorageBackedUint64 // brotli compression level used for pricing - backingStorage *storage.Storage - Burner burn.Burner + arbosVersion uint64 // version of the ArbOS storage format and semantics + maxArbosVersionSupported uint64 // maximum ArbOS version supported by this code + maxDebugArbosVersionSupported uint64 // maximum ArbOS version supported by this code in debug mode + upgradeVersion storage.StorageBackedUint64 // version we're planning to upgrade to, or 0 if not planning to upgrade + upgradeTimestamp storage.StorageBackedUint64 // when to do the planned upgrade + networkFeeAccount storage.StorageBackedAddress + l1PricingState *l1pricing.L1PricingState + l2PricingState *l2pricing.L2PricingState + retryableState *retryables.RetryableState + addressTable *addressTable.AddressTable + chainOwners *addressSet.AddressSet + sendMerkle *merkleAccumulator.MerkleAccumulator + programs *programs.Programs + blockhashes *blockhash.Blockhashes + chainId storage.StorageBackedBigInt + chainConfig storage.StorageBackedBytes + genesisBlockNum storage.StorageBackedUint64 + infraFeeAccount storage.StorageBackedAddress + brotliCompressionLevel storage.StorageBackedUint64 // brotli compression level used for pricing + backingStorage *storage.Storage + Burner burn.Burner } var ErrUninitializedArbOS = errors.New("ArbOS uninitialized") @@ -70,6 +74,8 @@ func OpenArbosState(stateDB vm.StateDB, burner burn.Burner) (*ArbosState, error) } return &ArbosState{ arbosVersion, + 30, + 30, backingStorage.OpenStorageBackedUint64(uint64(upgradeVersionOffset)), backingStorage.OpenStorageBackedUint64(uint64(upgradeTimestampOffset)), backingStorage.OpenStorageBackedAddress(uint64(networkFeeAccountOffset)), @@ -79,6 +85,7 @@ func OpenArbosState(stateDB vm.StateDB, burner burn.Burner) (*ArbosState, error) addressTable.Open(backingStorage.OpenCachedSubStorage(addressTableSubspace)), addressSet.OpenAddressSet(backingStorage.OpenCachedSubStorage(chainOwnerSubspace)), merkleAccumulator.OpenMerkleAccumulator(backingStorage.OpenCachedSubStorage(sendMerkleSubspace)), + programs.Open(backingStorage.OpenSubStorage(programsSubspace)), blockhash.OpenBlockhashes(backingStorage.OpenCachedSubStorage(blockhashesSubspace)), backingStorage.OpenStorageBackedBigInt(uint64(chainIdOffset)), backingStorage.OpenStorageBackedBytes(chainConfigSubspace), @@ -156,32 +163,10 @@ var ( sendMerkleSubspace SubspaceID = []byte{5} blockhashesSubspace SubspaceID = []byte{6} chainConfigSubspace SubspaceID = []byte{7} + programsSubspace SubspaceID = []byte{8} ) -// Returns a list of precompiles that only appear in Arbitrum chains (i.e. ArbOS precompiles) at the genesis block -func getArbitrumOnlyGenesisPrecompiles(chainConfig *params.ChainConfig) []common.Address { - rules := chainConfig.Rules(big.NewInt(0), false, 0, chainConfig.ArbitrumChainParams.InitialArbOSVersion) - arbPrecompiles := vm.ActivePrecompiles(rules) - rules.IsArbitrum = false - ethPrecompiles := vm.ActivePrecompiles(rules) - - ethPrecompilesSet := make(map[common.Address]bool) - for _, addr := range ethPrecompiles { - ethPrecompilesSet[addr] = true - } - - var arbOnlyPrecompiles []common.Address - for _, addr := range arbPrecompiles { - if !ethPrecompilesSet[addr] { - arbOnlyPrecompiles = append(arbOnlyPrecompiles, addr) - } - } - return arbOnlyPrecompiles -} - -// During early development we sometimes change the storage format of version 1, for convenience. But as soon as we -// start running long-lived chains, every change to the storage format will require defining a new version and -// providing upgrade code. +var PrecompileMinArbOSVersions = make(map[common.Address]uint64) func InitializeArbosState(stateDB vm.StateDB, burner burn.Burner, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage) (*ArbosState, error) { sto := storage.NewGeth(stateDB, burner) @@ -200,8 +185,10 @@ func InitializeArbosState(stateDB vm.StateDB, burner burn.Burner, chainConfig *p // Solidity requires call targets have code, but precompiles don't. // To work around this, we give precompiles fake code. - for _, genesisPrecompile := range getArbitrumOnlyGenesisPrecompiles(chainConfig) { - stateDB.SetCode(genesisPrecompile, []byte{byte(vm.INVALID)}) + for addr, version := range PrecompileMinArbOSVersions { + if version == 0 { + stateDB.SetCode(addr, []byte{byte(vm.INVALID)}) + } } // may be the zero address @@ -299,7 +286,8 @@ func (state *ArbosState) UpgradeArbosVersion( case 10: ensure(state.l1PricingState.SetL1FeesAvailable(stateDB.GetBalance( l1pricing.L1PricerFundsPoolAddress, - ))) + ).ToBig())) + case 11: // Update the PerBatchGasCost to a more accurate value compared to the old v6 default. ensure(state.l1PricingState.SetPerBatchGasCost(l1pricing.InitialPerBatchGasCostV12)) @@ -316,21 +304,35 @@ func (state *ArbosState) UpgradeArbosVersion( if !firstTime { ensure(state.chainOwners.ClearList()) } - // ArbOS versions 12 through 19 are left to Orbit chains for custom upgrades. + + case 12, 13, 14, 15, 16, 17, 18, 19: + // these versions are left to Orbit chains for custom upgrades. + case 20: // Update Brotli compression level for fast compression from 0 to 1 ensure(state.SetBrotliCompressionLevel(1)) + + case 21, 22, 23, 24, 25, 26, 27, 28, 29: + // these versions are left to Orbit chains for custom upgrades. + + case 30: + programs.Initialize(state.backingStorage.OpenSubStorage(programsSubspace)) + default: - if nextArbosVersion >= 12 && nextArbosVersion <= 19 { - // ArbOS versions 12 through 19 are left to Orbit chains for custom upgrades. - } else { - return fmt.Errorf( - "the chain is upgrading to unsupported ArbOS version %v, %w", - nextArbosVersion, - ErrFatalNodeOutOfDate, - ) + return fmt.Errorf( + "the chain is upgrading to unsupported ArbOS version %v, %w", + nextArbosVersion, + ErrFatalNodeOutOfDate, + ) + } + + // install any new precompiles + for addr, version := range PrecompileMinArbOSVersions { + if version == nextArbosVersion { + stateDB.SetCode(addr, []byte{byte(vm.INVALID)}) } } + state.arbosVersion = nextArbosVersion } @@ -400,6 +402,14 @@ func (state *ArbosState) RetryableState() *retryables.RetryableState { return state.retryableState } +func (state *ArbosState) MaxArbosVersionSupported() uint64 { + return state.maxArbosVersionSupported +} + +func (state *ArbosState) MaxDebugArbosVersionSupported() uint64 { + return state.maxDebugArbosVersionSupported +} + func (state *ArbosState) L1PricingState() *l1pricing.L1PricingState { return state.l1PricingState } @@ -423,6 +433,10 @@ func (state *ArbosState) SendMerkleAccumulator() *merkleAccumulator.MerkleAccumu return state.sendMerkle } +func (state *ArbosState) Programs() *programs.Programs { + return state.programs +} + func (state *ArbosState) Blockhashes() *blockhash.Blockhashes { return state.blockhashes } diff --git a/arbos/arbosState/initialization_test.go b/arbos/arbosState/initialization_test.go index 3de1fc5d38..0ef9cea4c5 100644 --- a/arbos/arbosState/initialization_test.go +++ b/arbos/arbosState/initialization_test.go @@ -151,7 +151,7 @@ func checkAccounts(db *state.StateDB, arbState *ArbosState, accts []statetransfe if db.GetNonce(addr) != acct.Nonce { t.Fatal() } - if db.GetBalance(addr).Cmp(acct.EthBalance) != 0 { + if db.GetBalance(addr).ToBig().Cmp(acct.EthBalance) != 0 { t.Fatal() } if acct.ContractInfo != nil { diff --git a/arbos/arbosState/initialize.go b/arbos/arbosState/initialize.go index 89a73b8c04..1cc2f63c4f 100644 --- a/arbos/arbosState/initialize.go +++ b/arbos/arbosState/initialize.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbos/l2pricing" @@ -142,7 +143,7 @@ func InitializeArbosInDatabase(db ethdb.Database, initData statetransfer.InitDat if err != nil { return common.Hash{}, err } - statedb.SetBalance(account.Addr, account.EthBalance, state.BalanceIncreaseGenesisBalance) + statedb.SetBalance(account.Addr, uint256.MustFromBig(account.EthBalance), state.BalanceIncreaseGenesisBalance) statedb.SetNonce(account.Addr, account.Nonce) if account.ContractInfo != nil { statedb.SetCode(account.Addr, account.ContractInfo.Code) @@ -173,7 +174,7 @@ func initializeRetryables(statedb *state.StateDB, rs *retryables.RetryableState, return err } if r.Timeout <= currentTimestamp { - statedb.AddBalance(r.Beneficiary, r.Callvalue, state.BalanceIncreaseGenesisBalance) + statedb.AddBalance(r.Beneficiary, uint256.MustFromBig(r.Callvalue), state.BalanceIncreaseGenesisBalance) continue } retryablesList = append(retryablesList, r) @@ -192,7 +193,7 @@ func initializeRetryables(statedb *state.StateDB, rs *retryables.RetryableState, addr := r.To to = &addr } - statedb.AddBalance(retryables.RetryableEscrowAddress(r.Id), r.Callvalue, state.BalanceIncreaseGenesisBalance) + statedb.AddBalance(retryables.RetryableEscrowAddress(r.Id), uint256.MustFromBig(r.Callvalue), state.BalanceIncreaseGenesisBalance) _, err := rs.CreateRetryable(r.Id, r.Timeout, r.From, to, r.Callvalue, r.Beneficiary, r.Calldata) if err != nil { return err diff --git a/arbos/arbostypes/incomingmessage.go b/arbos/arbostypes/incomingmessage.go index 1dc75c3e36..04ce8ebe2e 100644 --- a/arbos/arbostypes/incomingmessage.go +++ b/arbos/arbostypes/incomingmessage.go @@ -34,8 +34,6 @@ const ( const MaxL2MessageSize = 256 * 1024 -const ArbosVersion_FixRedeemGas = uint64(11) - type L1IncomingMessageHeader struct { Kind uint8 `json:"kind"` Poster common.Address `json:"sender"` diff --git a/arbos/arbostypes/messagewithmeta.go b/arbos/arbostypes/messagewithmeta.go index a3d4f5e3c3..79b7c4f9d2 100644 --- a/arbos/arbostypes/messagewithmeta.go +++ b/arbos/arbostypes/messagewithmeta.go @@ -18,6 +18,11 @@ type MessageWithMetadata struct { DelayedMessagesRead uint64 `json:"delayedMessagesRead"` } +type MessageWithMetadataAndBlockHash struct { + MessageWithMeta MessageWithMetadata + BlockHash *common.Hash +} + var EmptyTestMessageWithMetadata = MessageWithMetadata{ Message: &EmptyTestIncomingMessage, } diff --git a/arbos/block_processor.go b/arbos/block_processor.go index 52ee4708aa..c85f548fcc 100644 --- a/arbos/block_processor.go +++ b/arbos/block_processor.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package arbos @@ -14,7 +14,6 @@ import ( "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/l2pricing" "github.com/offchainlabs/nitro/arbos/util" - "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/util/arbmath" "github.com/ethereum/go-ethereum/arbitrum_types" @@ -25,7 +24,6 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" ) @@ -40,7 +38,6 @@ var L2ToL1TransactionEventID common.Hash var L2ToL1TxEventID common.Hash var EmitReedeemScheduledEvent func(*vm.EVM, uint64, uint64, [32]byte, [32]byte, common.Address, *big.Int, *big.Int) error var EmitTicketCreatedEvent func(*vm.EVM, [32]byte) error -var gasUsedSinceStartupCounter = metrics.NewRegisteredCounter("arb/gas_used", nil) // A helper struct that implements String() by marshalling to JSON. // This is useful for logging because it's lazy, so if the log level is too high to print the transaction, @@ -148,6 +145,7 @@ func ProduceBlock( chainContext core.ChainContext, chainConfig *params.ChainConfig, batchFetcher arbostypes.FallibleBatchFetcher, + isMsgForPrefetch bool, logger core.BlockchainLogger, ) (*types.Block, types.Receipts, error) { var batchFetchErr error @@ -174,7 +172,7 @@ func ProduceBlock( hooks := NoopSequencingHooks() return ProduceBlockAdvanced( - message.Header, txes, delayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, hooks, logger, + message.Header, txes, delayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, hooks, isMsgForPrefetch, logger, ) } @@ -188,6 +186,7 @@ func ProduceBlockAdvanced( chainContext core.ChainContext, chainConfig *params.ChainConfig, sequencingHooks *SequencingHooks, + isMsgForPrefetch bool, logger core.BlockchainLogger, ) (*types.Block, types.Receipts, error) { @@ -385,7 +384,9 @@ func ProduceBlockAdvanced( if chainConfig.DebugMode() { logLevel = log.Warn } - logLevel("error applying transaction", "tx", printTxAsJson{tx}, "err", err) + if !isMsgForPrefetch { + logLevel("error applying transaction", "tx", printTxAsJson{tx}, "err", err) + } if !hooks.DiscardInvalidTxsEarly { // we'll still deduct a TxGas's worth from the block-local rate limiter even if the tx was invalid blockGasLeft = arbmath.SaturatingUSub(blockGasLeft, params.TxGas) @@ -406,7 +407,7 @@ func ProduceBlockAdvanced( txGasUsed := header.GasUsed - preTxHeaderGasUsed arbosVer := types.DeserializeHeaderExtraInformation(header).ArbOSFormatVersion - if arbosVer >= arbostypes.ArbosVersion_FixRedeemGas { + if arbosVer >= params.ArbosVersion_FixRedeemGas { // subtract gas burned for future use for _, scheduledTx := range result.ScheduledTxes { switch inner := scheduledTx.GetInner().(type) { @@ -451,16 +452,14 @@ func ProduceBlockAdvanced( // L2->L1 withdrawals remove eth from the system switch txLog.Topics[0] { case L2ToL1TransactionEventID: - event := &precompilesgen.ArbSysL2ToL1Transaction{} - err := util.ParseL2ToL1TransactionLog(event, txLog) + event, err := util.ParseL2ToL1TransactionLog(txLog) if err != nil { log.Error("Failed to parse L2ToL1Transaction log", "err", err) } else { expectedBalanceDelta.Sub(expectedBalanceDelta, event.Callvalue) } case L2ToL1TxEventID: - event := &precompilesgen.ArbSysL2ToL1Tx{} - err := util.ParseL2ToL1TxLog(event, txLog) + event, err := util.ParseL2ToL1TxLog(txLog) if err != nil { log.Error("Failed to parse L2ToL1Tx log", "err", err) } else { @@ -472,10 +471,6 @@ func ProduceBlockAdvanced( blockGasLeft = arbmath.SaturatingUSub(blockGasLeft, computeUsed) - // Add gas used since startup to prometheus metric. - gasUsed := arbmath.SaturatingUSub(receipt.GasUsed, receipt.GasUsedForL1) - gasUsedSinceStartupCounter.Inc(arbmath.SaturatingCast(gasUsed)) - complete = append(complete, tx) receipts = append(receipts, receipt) diff --git a/arbos/burn/burn.go b/arbos/burn/burn.go index 730fed1a51..7d30ad12ec 100644 --- a/arbos/burn/burn.go +++ b/arbos/burn/burn.go @@ -13,6 +13,8 @@ import ( type Burner interface { Burn(amount uint64) error Burned() uint64 + GasLeft() *uint64 // `SystemBurner`s panic (no notion of GasLeft) + BurnOut() error Restrict(err error) HandleError(err error) error ReadOnly() bool @@ -41,6 +43,14 @@ func (burner *SystemBurner) Burned() uint64 { return burner.gasBurnt } +func (burner *SystemBurner) BurnOut() error { + panic("called BurnOut on a system burner") +} + +func (burner *SystemBurner) GasLeft() *uint64 { + panic("called GasLeft on a system burner") +} + func (burner *SystemBurner) Restrict(err error) { if err != nil { glog.Error("Restrict() received an error", "err", err) diff --git a/arbos/internal_tx.go b/arbos/internal_tx.go index cd6feb3906..9832ac8005 100644 --- a/arbos/internal_tx.go +++ b/arbos/internal_tx.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package arbos @@ -104,8 +104,8 @@ func ApplyInternalTxUpdate(tx *types.ArbitrumInternalTx, state *arbosState.Arbos if err != nil { log.Warn("L1Pricing PerBatchGas failed", "err", err) } - gasSpent := arbmath.SaturatingAdd(perBatchGas, arbmath.SaturatingCast(batchDataGas)) - weiSpent := arbmath.BigMulByUint(l1BaseFeeWei, arbmath.SaturatingUCast(gasSpent)) + gasSpent := arbmath.SaturatingAdd(perBatchGas, arbmath.SaturatingCast[int64](batchDataGas)) + weiSpent := arbmath.BigMulByUint(l1BaseFeeWei, arbmath.SaturatingUCast[uint64](gasSpent)) err = l1p.UpdateForBatchPosterSpending( evm.StateDB, evm, diff --git a/arbos/l1pricing/l1PricingOldVersions.go b/arbos/l1pricing/l1PricingOldVersions.go index 5c6b6ab7d6..821d743e7d 100644 --- a/arbos/l1pricing/l1PricingOldVersions.go +++ b/arbos/l1pricing/l1PricingOldVersions.go @@ -4,12 +4,13 @@ package l1pricing import ( + "math" + "math/big" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/offchainlabs/nitro/arbos/util" am "github.com/offchainlabs/nitro/util/arbmath" - "math" - "math/big" ) func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( @@ -105,8 +106,8 @@ func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( // pay rewards, as much as possible paymentForRewards := am.BigMulByUint(am.UintToBig(perUnitReward), unitsAllocated) availableFunds := statedb.GetBalance(L1PricerFundsPoolAddress) - if am.BigLessThan(availableFunds, paymentForRewards) { - paymentForRewards = availableFunds + if am.BigLessThan(availableFunds.ToBig(), paymentForRewards) { + paymentForRewards = availableFunds.ToBig() } fundsDueForRewards = am.BigSub(fundsDueForRewards, paymentForRewards) if err := ps.SetFundsDueForRewards(fundsDueForRewards); err != nil { @@ -130,8 +131,8 @@ func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( return err } balanceToTransfer := balanceDueToPoster - if am.BigLessThan(availableFunds, balanceToTransfer) { - balanceToTransfer = availableFunds + if am.BigLessThan(availableFunds.ToBig(), balanceToTransfer) { + balanceToTransfer = availableFunds.ToBig() } if balanceToTransfer.Sign() > 0 { addrToPay, err := posterState.PayTo() @@ -166,7 +167,7 @@ func (ps *L1PricingState) _preversion10_UpdateForBatchPosterSpending( if err != nil { return err } - surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress), am.BigAdd(totalFundsDue, fundsDueForRewards)) + surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress).ToBig(), am.BigAdd(totalFundsDue, fundsDueForRewards)) inertia, err := ps.Inertia() if err != nil { @@ -230,7 +231,7 @@ func (ps *L1PricingState) _preVersion2_UpdateForBatchPosterSpending( if err != nil { return err } - oldSurplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress), am.BigAdd(totalFundsDue, fundsDueForRewards)) + oldSurplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress).ToBig(), am.BigAdd(totalFundsDue, fundsDueForRewards)) // compute allocation fraction -- will allocate updateTimeDelta/timeDelta fraction of units and funds to this update lastUpdateTime, err := ps.LastUpdateTime() @@ -280,7 +281,7 @@ func (ps *L1PricingState) _preVersion2_UpdateForBatchPosterSpending( // allocate funds to this update collectedSinceUpdate := statedb.GetBalance(L1PricerFundsPoolAddress) - availableFunds := am.BigDivByUint(am.BigMulByUint(collectedSinceUpdate, allocationNumerator), allocationDenominator) + availableFunds := am.BigDivByUint(am.BigMulByUint(collectedSinceUpdate.ToBig(), allocationNumerator), allocationDenominator) // pay rewards, as much as possible paymentForRewards := am.BigMulByUint(am.UintToBig(perUnitReward), unitsAllocated) @@ -356,7 +357,7 @@ func (ps *L1PricingState) _preVersion2_UpdateForBatchPosterSpending( if err != nil { return err } - surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress), am.BigAdd(totalFundsDue, fundsDueForRewards)) + surplus := am.BigSub(statedb.GetBalance(L1PricerFundsPoolAddress).ToBig(), am.BigAdd(totalFundsDue, fundsDueForRewards)) inertia, err := ps.Inertia() if err != nil { diff --git a/arbos/l1pricing/l1pricing.go b/arbos/l1pricing/l1pricing.go index f2312c46d4..9e00eeb581 100644 --- a/arbos/l1pricing/l1pricing.go +++ b/arbos/l1pricing/l1pricing.go @@ -195,6 +195,23 @@ func (ps *L1PricingState) SetUnitsSinceUpdate(units uint64) error { return ps.unitsSinceUpdate.Set(units) } +func (ps *L1PricingState) GetL1PricingSurplus() (*big.Int, error) { + fundsDueForRefunds, err := ps.BatchPosterTable().TotalFundsDue() + if err != nil { + return nil, err + } + fundsDueForRewards, err := ps.FundsDueForRewards() + if err != nil { + return nil, err + } + haveFunds, err := ps.L1FeesAvailable() + if err != nil { + return nil, err + } + needFunds := arbmath.BigAdd(fundsDueForRefunds, fundsDueForRewards) + return arbmath.BigSub(haveFunds, needFunds), nil +} + func (ps *L1PricingState) LastSurplus() (*big.Int, error) { return ps.lastSurplus.Get() } diff --git a/arbos/l1pricing_test.go b/arbos/l1pricing_test.go index 219a2298be..51fe9de256 100644 --- a/arbos/l1pricing_test.go +++ b/arbos/l1pricing_test.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/vm" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/l1pricing" "github.com/offchainlabs/nitro/arbos/util" @@ -171,7 +172,7 @@ func _testL1PricingFundsDue(t *testing.T, testParams *l1PricingTest, expectedRes // create some fake collection balanceAdded := big.NewInt(int64(testParams.fundsCollectedPerSecond * 3)) unitsAdded := testParams.unitsPerSecond * 3 - evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, balanceAdded, state.BalanceChangeTransfer) + evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, uint256.MustFromBig(balanceAdded), state.BalanceChangeTransfer) err = l1p.SetL1FeesAvailable(balanceAdded) Require(t, err) err = l1p.SetUnitsSinceUpdate(unitsAdded) @@ -187,7 +188,7 @@ func _testL1PricingFundsDue(t *testing.T, testParams *l1PricingTest, expectedRes ) Require(t, err) rewardRecipientBalance := evm.StateDB.GetBalance(rewardAddress) - if !arbmath.BigEquals(rewardRecipientBalance, expectedResults.rewardRecipientBalance) { + if !arbmath.BigEquals(rewardRecipientBalance.ToBig(), expectedResults.rewardRecipientBalance) { Fail(t, rewardRecipientBalance, expectedResults.rewardRecipientBalance) } unitsRemaining, err := l1p.UnitsSinceUpdate() @@ -196,16 +197,16 @@ func _testL1PricingFundsDue(t *testing.T, testParams *l1PricingTest, expectedRes Fail(t, unitsRemaining, expectedResults.unitsRemaining) } fundsReceived := evm.StateDB.GetBalance(firstPayTo) - if !arbmath.BigEquals(fundsReceived, expectedResults.fundsReceived) { + if !arbmath.BigEquals(fundsReceived.ToBig(), expectedResults.fundsReceived) { Fail(t, fundsReceived, expectedResults.fundsReceived) } fundsStillHeld := evm.StateDB.GetBalance(l1pricing.L1PricerFundsPoolAddress) - if !arbmath.BigEquals(fundsStillHeld, expectedResults.fundsStillHeld) { + if !arbmath.BigEquals(fundsStillHeld.ToBig(), expectedResults.fundsStillHeld) { Fail(t, fundsStillHeld, expectedResults.fundsStillHeld) } fundsAvail, err := l1p.L1FeesAvailable() Require(t, err) - if fundsStillHeld.Cmp(fundsAvail) != 0 { + if fundsStillHeld.ToBig().Cmp(fundsAvail) != 0 { Fail(t, fundsStillHeld, fundsAvail) } } diff --git a/arbos/l2pricing/model.go b/arbos/l2pricing/model.go index effa6354a5..131af2c2cf 100644 --- a/arbos/l2pricing/model.go +++ b/arbos/l2pricing/model.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package l2pricing @@ -30,7 +30,7 @@ func (ps *L2PricingState) AddToGasPool(gas int64) error { return err } // pay off some of the backlog with the added gas, stopping at 0 - backlog = arbmath.SaturatingUCast(arbmath.SaturatingSub(int64(backlog), gas)) + backlog = arbmath.SaturatingUCast[uint64](arbmath.SaturatingSub(int64(backlog), gas)) return ps.SetGasBacklog(backlog) } @@ -46,7 +46,7 @@ func (ps *L2PricingState) UpdatePricingModel(l2BaseFee *big.Int, timePassed uint if backlog > tolerance*speedLimit { excess := int64(backlog - tolerance*speedLimit) exponentBips := arbmath.NaturalToBips(excess) / arbmath.Bips(inertia*speedLimit) - baseFee = arbmath.BigMulByBips(minBaseFee, arbmath.ApproxExpBasisPoints(exponentBips)) + baseFee = arbmath.BigMulByBips(minBaseFee, arbmath.ApproxExpBasisPoints(exponentBips, 4)) } _ = ps.SetBaseFeeWei(baseFee) } diff --git a/arbos/programs/api.go b/arbos/programs/api.go new file mode 100644 index 0000000000..c8241a72b5 --- /dev/null +++ b/arbos/programs/api.go @@ -0,0 +1,427 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package programs + +import ( + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" + "github.com/offchainlabs/nitro/arbos/util" + "github.com/offchainlabs/nitro/util/arbmath" + am "github.com/offchainlabs/nitro/util/arbmath" +) + +type RequestHandler func(req RequestType, input []byte) ([]byte, []byte, uint64) + +type RequestType int +type u256 = uint256.Int + +const ( + GetBytes32 RequestType = iota + SetTrieSlots + GetTransientBytes32 + SetTransientBytes32 + ContractCall + DelegateCall + StaticCall + Create1 + Create2 + EmitLog + AccountBalance + AccountCode + AccountCodeHash + AddPages + CaptureHostIO +) + +type apiStatus uint8 + +const ( + Success apiStatus = iota + Failure + OutOfGas + WriteProtection +) + +func (s apiStatus) to_slice() []byte { + return []byte{uint8(s)} +} + +const EvmApiMethodReqOffset = 0x10000000 + +func newApiClosures( + interpreter *vm.EVMInterpreter, + tracingInfo *util.TracingInfo, + scope *vm.ScopeContext, + memoryModel *MemoryModel, +) RequestHandler { + contract := scope.Contract + actingAddress := contract.Address() // not necessarily WASM + readOnly := interpreter.ReadOnly() + evm := interpreter.Evm() + depth := evm.Depth() + db := evm.StateDB + chainConfig := evm.ChainConfig() + + getBytes32 := func(key common.Hash) (common.Hash, uint64) { + if tracingInfo != nil { + tracingInfo.RecordStorageGet(key) + } + cost := vm.WasmStateLoadCost(db, actingAddress, key) + return db.GetState(actingAddress, key), cost + } + setTrieSlots := func(data []byte, gasLeft *uint64) apiStatus { + for len(data) > 0 { + key := common.BytesToHash(data[:32]) + value := common.BytesToHash(data[32:64]) + data = data[64:] + + if tracingInfo != nil { + tracingInfo.RecordStorageSet(key, value) + } + if readOnly { + return WriteProtection + } + + cost := vm.WasmStateStoreCost(db, actingAddress, key, value) + if cost > *gasLeft { + *gasLeft = 0 + return OutOfGas + } + *gasLeft -= cost + db.SetState(actingAddress, key, value) + } + return Success + } + getTransientBytes32 := func(key common.Hash) common.Hash { + return db.GetTransientState(actingAddress, key) + } + setTransientBytes32 := func(key, value common.Hash) apiStatus { + if readOnly { + return WriteProtection + } + db.SetTransientState(actingAddress, key, value) + return Success + } + doCall := func( + contract common.Address, opcode vm.OpCode, input []byte, gasLeft, gasReq uint64, value *u256, + ) ([]byte, uint64, error) { + // This closure can perform each kind of contract call based on the opcode passed in. + // The implementation for each should match that of the EVM. + // + // Note that while the Yellow Paper is authoritative, the following go-ethereum + // functions provide corresponding implementations in the vm package. + // - operations_acl.go makeCallVariantGasCallEIP2929() + // - gas_table.go gasCall() gasDelegateCall() gasStaticCall() + // - instructions.go opCall() opDelegateCall() opStaticCall() + // + + // read-only calls are not payable (opCall) + if readOnly && value.Sign() != 0 { + return nil, 0, vm.ErrWriteProtection + } + + // computes makeCallVariantGasCallEIP2929 and gasCall/gasDelegateCall/gasStaticCall + baseCost, err := vm.WasmCallCost(db, contract, value, gasLeft) + if err != nil { + return nil, gasLeft, err + } + + // apply the 63/64ths rule + startGas := am.SaturatingUSub(gasLeft, baseCost) * 63 / 64 + gas := am.MinInt(startGas, gasReq) + + // Tracing: emit the call (value transfer is done later in evm.Call) + if tracingInfo != nil { + tracingInfo.Tracer.CaptureState(0, opcode, startGas, baseCost+gas, scope, []byte{}, depth, nil) + } + + // EVM rule: calls that pay get a stipend (opCall) + if value.Sign() != 0 { + gas = am.SaturatingUAdd(gas, params.CallStipend) + } + + var ret []byte + var returnGas uint64 + + switch opcode { + case vm.CALL: + ret, returnGas, err = evm.Call(scope.Contract, contract, input, gas, value) + case vm.DELEGATECALL: + ret, returnGas, err = evm.DelegateCall(scope.Contract, contract, input, gas) + case vm.STATICCALL: + ret, returnGas, err = evm.StaticCall(scope.Contract, contract, input, gas) + default: + log.Crit("unsupported call type", "opcode", opcode) + } + + interpreter.SetReturnData(ret) + cost := am.SaturatingUAdd(baseCost, am.SaturatingUSub(gas, returnGas)) + return ret, cost, err + } + create := func(code []byte, endowment, salt *u256, gas uint64) (common.Address, []byte, uint64, error) { + // This closure can perform both kinds of contract creation based on the salt passed in. + // The implementation for each should match that of the EVM. + // + // Note that while the Yellow Paper is authoritative, the following go-ethereum + // functions provide corresponding implementations in the vm package. + // - instructions.go opCreate() opCreate2() + // - gas_table.go gasCreate() gasCreate2() + // + + opcode := vm.CREATE + if salt != nil { + opcode = vm.CREATE2 + } + zeroAddr := common.Address{} + startGas := gas + + if readOnly { + return zeroAddr, nil, 0, vm.ErrWriteProtection + } + + // pay for static and dynamic costs (gasCreate and gasCreate2) + baseCost := params.CreateGas + if opcode == vm.CREATE2 { + keccakWords := am.WordsForBytes(uint64(len(code))) + keccakCost := am.SaturatingUMul(params.Keccak256WordGas, keccakWords) + baseCost = am.SaturatingUAdd(baseCost, keccakCost) + } + if gas < baseCost { + return zeroAddr, nil, gas, vm.ErrOutOfGas + } + gas -= baseCost + + // apply the 63/64ths rule + one64th := gas / 64 + gas -= one64th + + // Tracing: emit the create + if tracingInfo != nil { + tracingInfo.Tracer.CaptureState(0, opcode, startGas, baseCost+gas, scope, []byte{}, depth, nil) + } + + var res []byte + var addr common.Address // zero on failure + var returnGas uint64 + var suberr error + + if opcode == vm.CREATE { + res, addr, returnGas, suberr = evm.Create(contract, code, gas, endowment) + } else { + res, addr, returnGas, suberr = evm.Create2(contract, code, gas, endowment, salt) + } + if suberr != nil { + addr = zeroAddr + } + if !errors.Is(vm.ErrExecutionReverted, suberr) { + res = nil // returnData is only provided in the revert case (opCreate) + } + interpreter.SetReturnData(res) + cost := arbmath.SaturatingUSub(startGas, returnGas+one64th) // user gets 1/64th back + return addr, res, cost, nil + } + emitLog := func(topics []common.Hash, data []byte) error { + if readOnly { + return vm.ErrWriteProtection + } + event := &types.Log{ + Address: actingAddress, + Topics: topics, + Data: data, + BlockNumber: evm.Context.BlockNumber.Uint64(), + // Geth will set other fields + } + db.AddLog(event) + return nil + } + accountBalance := func(address common.Address) (common.Hash, uint64) { + cost := vm.WasmAccountTouchCost(chainConfig, evm.StateDB, address, false) + balance := evm.StateDB.GetBalance(address) + return balance.Bytes32(), cost + } + accountCode := func(address common.Address, gas uint64) ([]byte, uint64) { + // In the future it'll be possible to know the size of a contract before loading it. + // For now, require the worst case before doing the load. + + cost := vm.WasmAccountTouchCost(chainConfig, evm.StateDB, address, true) + if gas < cost { + return []byte{}, cost + } + return evm.StateDB.GetCode(address), cost + } + accountCodehash := func(address common.Address) (common.Hash, uint64) { + cost := vm.WasmAccountTouchCost(chainConfig, evm.StateDB, address, false) + return evm.StateDB.GetCodeHash(address), cost + } + addPages := func(pages uint16) uint64 { + open, ever := db.AddStylusPages(pages) + return memoryModel.GasCost(pages, open, ever) + } + captureHostio := func(name string, args, outs []byte, startInk, endInk uint64) { + tracingInfo.Tracer.CaptureStylusHostio(name, args, outs, startInk, endInk) + } + + return func(req RequestType, input []byte) ([]byte, []byte, uint64) { + original := input + + crash := func(reason string) { + log.Crit("bad API call", "reason", reason, "request", req, "len", len(original), "remaining", len(input)) + } + takeInput := func(needed int, reason string) []byte { + if len(input) < needed { + crash(reason) + } + data := input[:needed] + input = input[needed:] + return data + } + defer func() { + if len(input) > 0 { + crash("extra input") + } + }() + + takeAddress := func() common.Address { + return common.BytesToAddress(takeInput(20, "expected address")) + } + takeHash := func() common.Hash { + return common.BytesToHash(takeInput(32, "expected hash")) + } + takeU256 := func() *u256 { + return am.BytesToUint256(takeInput(32, "expected big")) + } + takeU64 := func() uint64 { + return am.BytesToUint(takeInput(8, "expected u64")) + } + takeU32 := func() uint32 { + return am.BytesToUint32(takeInput(4, "expected u32")) + } + takeU16 := func() uint16 { + return am.BytesToUint16(takeInput(2, "expected u16")) + } + takeFixed := func(needed int) []byte { + return takeInput(needed, "expected value with known length") + } + takeRest := func() []byte { + data := input + input = []byte{} + return data + } + + switch req { + case GetBytes32: + key := takeHash() + out, cost := getBytes32(key) + return out[:], nil, cost + case SetTrieSlots: + gasLeft := takeU64() + gas := gasLeft + status := setTrieSlots(takeRest(), &gas) + return status.to_slice(), nil, gasLeft - gas + case GetTransientBytes32: + key := takeHash() + out := getTransientBytes32(key) + return out[:], nil, 0 + case SetTransientBytes32: + key := takeHash() + value := takeHash() + status := setTransientBytes32(key, value) + return status.to_slice(), nil, 0 + case ContractCall, DelegateCall, StaticCall: + var opcode vm.OpCode + switch req { + case ContractCall: + opcode = vm.CALL + case DelegateCall: + opcode = vm.DELEGATECALL + case StaticCall: + opcode = vm.STATICCALL + default: + log.Crit("unsupported call type", "opcode", opcode) + } + contract := takeAddress() + value := takeU256() + gasLeft := takeU64() + gasReq := takeU64() + calldata := takeRest() + + ret, cost, err := doCall(contract, opcode, calldata, gasLeft, gasReq, value) + statusByte := byte(0) + if err != nil { + statusByte = 2 // TODO: err value + } + return []byte{statusByte}, ret, cost + case Create1, Create2: + gas := takeU64() + endowment := takeU256() + var salt *u256 + if req == Create2 { + salt = takeU256() + } + code := takeRest() + + address, retVal, cost, err := create(code, endowment, salt, gas) + if err != nil { + res := append([]byte{0}, []byte(err.Error())...) + return res, nil, gas + } + res := append([]byte{1}, address.Bytes()...) + return res, retVal, cost + case EmitLog: + topics := takeU32() + hashes := make([]common.Hash, topics) + for i := uint32(0); i < topics; i++ { + hashes[i] = takeHash() + } + + err := emitLog(hashes, takeRest()) + if err != nil { + return []byte(err.Error()), nil, 0 + } + return []byte{}, nil, 0 + case AccountBalance: + address := takeAddress() + balance, cost := accountBalance(address) + return balance[:], nil, cost + case AccountCode: + address := takeAddress() + gas := takeU64() + code, cost := accountCode(address, gas) + return nil, code, cost + case AccountCodeHash: + address := takeAddress() + codeHash, cost := accountCodehash(address) + return codeHash[:], nil, cost + case AddPages: + pages := takeU16() + cost := addPages(pages) + return []byte{}, nil, cost + case CaptureHostIO: + if tracingInfo == nil { + takeRest() // drop any input + return []byte{}, nil, 0 + } + startInk := takeU64() + endInk := takeU64() + nameLen := takeU16() + argsLen := takeU16() + outsLen := takeU16() + name := string(takeFixed(int(nameLen))) + args := takeFixed(int(argsLen)) + outs := takeFixed(int(outsLen)) + + captureHostio(name, args, outs, startInk, endInk) + return []byte{}, nil, 0 + default: + log.Crit("unsupported call type", "req", req) + return []byte{}, nil, 0 + } + } +} diff --git a/arbos/programs/constant_test.go b/arbos/programs/constant_test.go new file mode 100644 index 0000000000..fe29bcf3d9 --- /dev/null +++ b/arbos/programs/constant_test.go @@ -0,0 +1,13 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package programs + +import "testing" + +func TestConstants(t *testing.T) { + err := testConstants() + if err != nil { + t.Fatal(err) + } +} diff --git a/arbos/programs/data_pricer.go b/arbos/programs/data_pricer.go new file mode 100644 index 0000000000..ed7c98556d --- /dev/null +++ b/arbos/programs/data_pricer.go @@ -0,0 +1,90 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package programs + +import ( + "math/big" + + "github.com/offchainlabs/nitro/arbos/storage" + "github.com/offchainlabs/nitro/util/arbmath" +) + +type DataPricer struct { + backingStorage *storage.Storage + demand storage.StorageBackedUint32 + bytesPerSecond storage.StorageBackedUint32 + lastUpdateTime storage.StorageBackedUint64 + minPrice storage.StorageBackedUint32 + inertia storage.StorageBackedUint32 +} + +const ( + demandOffset uint64 = iota + bytesPerSecondOffset + lastUpdateTimeOffset + minPriceOffset + inertiaOffset +) + +const ArbitrumStartTime = 1421388000 // the day it all began + +const initialDemand = 0 // no demand +const InitialHourlyBytes = 1 * (1 << 40) / (365 * 24) // 1Tb total footprint +const initialBytesPerSecond = InitialHourlyBytes / (60 * 60) // refill each second +const initialLastUpdateTime = ArbitrumStartTime +const initialMinPrice = 82928201 // 5Mb = $1 +const initialInertia = 21360419 // expensive at 1Tb + +func initDataPricer(sto *storage.Storage) { + demand := sto.OpenStorageBackedUint32(demandOffset) + bytesPerSecond := sto.OpenStorageBackedUint32(bytesPerSecondOffset) + lastUpdateTime := sto.OpenStorageBackedUint64(lastUpdateTimeOffset) + minPrice := sto.OpenStorageBackedUint32(minPriceOffset) + inertia := sto.OpenStorageBackedUint32(inertiaOffset) + _ = demand.Set(initialDemand) + _ = bytesPerSecond.Set(initialBytesPerSecond) + _ = lastUpdateTime.Set(initialLastUpdateTime) + _ = minPrice.Set(initialMinPrice) + _ = inertia.Set(initialInertia) +} + +func openDataPricer(sto *storage.Storage) *DataPricer { + return &DataPricer{ + backingStorage: sto, + demand: sto.OpenStorageBackedUint32(demandOffset), + bytesPerSecond: sto.OpenStorageBackedUint32(bytesPerSecondOffset), + lastUpdateTime: sto.OpenStorageBackedUint64(lastUpdateTimeOffset), + minPrice: sto.OpenStorageBackedUint32(minPriceOffset), + inertia: sto.OpenStorageBackedUint32(inertiaOffset), + } +} + +func (p *DataPricer) UpdateModel(tempBytes uint32, time uint64) (*big.Int, error) { + demand, _ := p.demand.Get() + bytesPerSecond, _ := p.bytesPerSecond.Get() + lastUpdateTime, _ := p.lastUpdateTime.Get() + minPrice, _ := p.minPrice.Get() + inertia, err := p.inertia.Get() + if err != nil { + return nil, err + } + + passed := arbmath.SaturatingUUCast[uint32](time - lastUpdateTime) + credit := arbmath.SaturatingUMul(bytesPerSecond, passed) + demand = arbmath.SaturatingUSub(demand, credit) + demand = arbmath.SaturatingUAdd(demand, tempBytes) + + if err := p.demand.Set(demand); err != nil { + return nil, err + } + if err := p.lastUpdateTime.Set(time); err != nil { + return nil, err + } + + exponent := arbmath.OneInBips * arbmath.Bips(demand) / arbmath.Bips(inertia) + multiplier := arbmath.ApproxExpBasisPoints(exponent, 12).Uint64() + costPerByte := arbmath.SaturatingUMul(uint64(minPrice), multiplier) / 10000 + costInWei := arbmath.SaturatingUMul(costPerByte, uint64(tempBytes)) + return arbmath.UintToBig(costInWei), nil +} diff --git a/arbos/programs/memory.go b/arbos/programs/memory.go new file mode 100644 index 0000000000..60da3c076d --- /dev/null +++ b/arbos/programs/memory.go @@ -0,0 +1,60 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package programs + +import ( + "math" + + "github.com/offchainlabs/nitro/util/arbmath" +) + +type MemoryModel struct { + freePages uint16 // number of pages the tx gets for free + pageGas uint16 // base gas to charge per wasm page +} + +func NewMemoryModel(freePages uint16, pageGas uint16) *MemoryModel { + return &MemoryModel{ + freePages: freePages, + pageGas: pageGas, + } +} + +// Determines the gas cost of allocating `new` pages given `open` are active and `ever` have ever been. +func (model *MemoryModel) GasCost(new, open, ever uint16) uint64 { + newOpen := arbmath.SaturatingUAdd(open, new) + newEver := arbmath.MaxInt(ever, newOpen) + + // free until expansion beyond the first few + if newEver <= model.freePages { + return 0 + } + subFree := func(pages uint16) uint16 { + return arbmath.SaturatingUSub(pages, model.freePages) + } + + adding := arbmath.SaturatingUSub(subFree(newOpen), subFree(open)) + linear := arbmath.SaturatingUMul(uint64(adding), uint64(model.pageGas)) + expand := model.exp(newEver) - model.exp(ever) + return arbmath.SaturatingUAdd(linear, expand) +} + +func (model *MemoryModel) exp(pages uint16) uint64 { + if int(pages) < len(memoryExponents) { + return uint64(memoryExponents[pages]) + } + return math.MaxUint64 +} + +var memoryExponents = [129]uint32{ + 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9, 11, 12, 14, 17, 19, 22, 25, 29, 33, 38, + 43, 50, 57, 65, 75, 85, 98, 112, 128, 147, 168, 193, 221, 253, 289, 331, 379, 434, 497, 569, + 651, 745, 853, 976, 1117, 1279, 1463, 1675, 1917, 2194, 2511, 2874, 3290, 3765, 4309, 4932, + 5645, 6461, 7395, 8464, 9687, 11087, 12689, 14523, 16621, 19024, 21773, 24919, 28521, 32642, + 37359, 42758, 48938, 56010, 64104, 73368, 83971, 96106, 109994, 125890, 144082, 164904, 188735, + 216010, 247226, 282953, 323844, 370643, 424206, 485509, 555672, 635973, 727880, 833067, 953456, + 1091243, 1248941, 1429429, 1636000, 1872423, 2143012, 2452704, 2807151, 3212820, 3677113, + 4208502, 4816684, 5512756, 6309419, 7221210, 8264766, 9459129, 10826093, 12390601, 14181199, + 16230562, 18576084, 21260563, 24332984, 27849408, 31873999, +} diff --git a/arbos/programs/memory_test.go b/arbos/programs/memory_test.go new file mode 100644 index 0000000000..322311363a --- /dev/null +++ b/arbos/programs/memory_test.go @@ -0,0 +1,87 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package programs + +import ( + "math" + "testing" + + "github.com/offchainlabs/nitro/util/arbmath" + "github.com/offchainlabs/nitro/util/testhelpers" +) + +func TestTables(t *testing.T) { + model := NewMemoryModel(2, 1000) + base := math.Exp(math.Log(31_874_000) / 128) + for p := uint16(0); p < 129; p++ { + value := uint64(math.Pow(base, float64(p))) + correct := model.exp(p) + + if value != correct { + Fail(t, "wrong value for ", p, value, correct) + } + } + if model.exp(129) != math.MaxUint64 || model.exp(math.MaxUint16) != math.MaxUint64 { + Fail(t) + } +} + +func TestModel(t *testing.T) { + model := NewMemoryModel(2, 1000) + + for jump := uint16(1); jump <= 128; jump++ { + total := uint64(0) + pages := uint16(0) + for pages < 128 { + jump := arbmath.MinInt(jump, 128-pages) + total += model.GasCost(jump, pages, pages) + pages += jump + } + AssertEq(t, total, 31999998) + } + + for jump := uint16(1); jump <= 128; jump++ { + total := uint64(0) + open := uint16(0) + ever := uint16(0) + adds := uint64(0) + for ever < 128 { + jump := arbmath.MinInt(jump, 128-open) + total += model.GasCost(jump, open, ever) + open += jump + ever = arbmath.MaxInt(ever, open) + + if ever > model.freePages { + adds += uint64(arbmath.MinInt(jump, ever-model.freePages)) + } + + // pretend we've deallocated some pages + open -= jump / 2 + } + expected := 31873998 + adds*uint64(model.pageGas) + AssertEq(t, total, expected) + } + + // check saturation + AssertEq(t, math.MaxUint64, model.GasCost(129, 0, 0)) + AssertEq(t, math.MaxUint64, model.GasCost(math.MaxUint16, 0, 0)) + + // check free pages + model = NewMemoryModel(128, 1000) + AssertEq(t, 0, model.GasCost(128, 0, 0)) + AssertEq(t, 0, model.GasCost(128, 0, 128)) + AssertEq(t, math.MaxUint64, model.GasCost(129, 0, 0)) +} + +func Fail(t *testing.T, printables ...interface{}) { + t.Helper() + testhelpers.FailImpl(t, printables...) +} + +func AssertEq[T comparable](t *testing.T, a T, b T) { + t.Helper() + if a != b { + Fail(t, a, "!=", b) + } +} diff --git a/arbos/programs/native.go b/arbos/programs/native.go new file mode 100644 index 0000000000..f24dcac64d --- /dev/null +++ b/arbos/programs/native.go @@ -0,0 +1,343 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +//go:build !wasm +// +build !wasm + +package programs + +/* +#cgo CFLAGS: -g -Wall -I../../target/include/ +#cgo LDFLAGS: ${SRCDIR}/../../target/lib/libstylus.a -ldl -lm +#include "arbitrator.h" + +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef size_t usize; +*/ +import "C" +import ( + "errors" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/arbos/burn" + "github.com/offchainlabs/nitro/arbos/util" + "github.com/offchainlabs/nitro/arbutil" +) + +type u8 = C.uint8_t +type u16 = C.uint16_t +type u32 = C.uint32_t +type u64 = C.uint64_t +type usize = C.size_t +type cbool = C._Bool +type bytes20 = C.Bytes20 +type bytes32 = C.Bytes32 +type rustBytes = C.RustBytes +type rustSlice = C.RustSlice + +func activateProgram( + db vm.StateDB, + program common.Address, + codehash common.Hash, + wasm []byte, + page_limit uint16, + version uint16, + debug bool, + burner burn.Burner, +) (*activationInfo, error) { + info, asm, module, err := activateProgramInternal(db, program, codehash, wasm, page_limit, version, debug, burner.GasLeft()) + if err != nil { + return nil, err + } + db.ActivateWasm(info.moduleHash, asm, module) + return info, nil +} + +func activateProgramInternal( + db vm.StateDB, + program common.Address, + codehash common.Hash, + wasm []byte, + page_limit uint16, + version uint16, + debug bool, + gasLeft *uint64, +) (*activationInfo, []byte, []byte, error) { + output := &rustBytes{} + asmLen := usize(0) + moduleHash := &bytes32{} + stylusData := &C.StylusData{} + codeHash := hashToBytes32(codehash) + + status := userStatus(C.stylus_activate( + goSlice(wasm), + u16(page_limit), + u16(version), + cbool(debug), + output, + &asmLen, + &codeHash, + moduleHash, + stylusData, + (*u64)(gasLeft), + )) + + data, msg, err := status.toResult(output.intoBytes(), debug) + if err != nil { + if debug { + log.Warn("activation failed", "err", err, "msg", msg, "program", program) + } + if errors.Is(err, vm.ErrExecutionReverted) { + return nil, nil, nil, fmt.Errorf("%w: %s", ErrProgramActivation, msg) + } + return nil, nil, nil, err + } + + hash := moduleHash.toHash() + split := int(asmLen) + asm := data[:split] + module := data[split:] + + info := &activationInfo{ + moduleHash: hash, + initGas: uint16(stylusData.init_cost), + cachedInitGas: uint16(stylusData.cached_init_cost), + asmEstimate: uint32(stylusData.asm_estimate), + footprint: uint16(stylusData.footprint), + } + return info, asm, module, err +} + +func getLocalAsm(statedb vm.StateDB, moduleHash common.Hash, address common.Address, pagelimit uint16, time uint64, debugMode bool, program Program) ([]byte, error) { + localAsm, err := statedb.TryGetActivatedAsm(moduleHash) + if err == nil && len(localAsm) > 0 { + return localAsm, nil + } + + codeHash := statedb.GetCodeHash(address) + + wasm, err := getWasm(statedb, address) + if err != nil { + log.Error("Failed to reactivate program: getWasm", "address", address, "expected moduleHash", moduleHash, "err", err) + return nil, fmt.Errorf("failed to reactivate program address: %v err: %w", address, err) + } + + unlimitedGas := uint64(0xffffffffffff) + // we know program is activated, so it must be in correct version and not use too much memory + info, asm, module, err := activateProgramInternal(statedb, address, codeHash, wasm, pagelimit, program.version, debugMode, &unlimitedGas) + if err != nil { + log.Error("failed to reactivate program", "address", address, "expected moduleHash", moduleHash, "err", err) + return nil, fmt.Errorf("failed to reactivate program address: %v err: %w", address, err) + } + + if info.moduleHash != moduleHash { + log.Error("failed to reactivate program", "address", address, "expected moduleHash", moduleHash, "got", info.moduleHash) + return nil, fmt.Errorf("failed to reactivate program. address: %v, expected ModuleHash: %v", address, moduleHash) + } + + currentHoursSince := hoursSinceArbitrum(time) + if currentHoursSince > program.activatedAt { + // stylus program is active on-chain, and was activated in the past + // so we store it directly to database + batch := statedb.Database().WasmStore().NewBatch() + rawdb.WriteActivation(batch, moduleHash, asm, module) + if err := batch.Write(); err != nil { + log.Error("failed writing re-activation to state", "address", address, "err", err) + } + } else { + // program activated recently, possibly in this eth_call + // store it to statedb. It will be stored to database if statedb is commited + statedb.ActivateWasm(info.moduleHash, asm, module) + } + return asm, nil +} + +func callProgram( + address common.Address, + moduleHash common.Hash, + localAsm []byte, + scope *vm.ScopeContext, + interpreter *vm.EVMInterpreter, + tracingInfo *util.TracingInfo, + calldata []byte, + evmData *EvmData, + stylusParams *ProgParams, + memoryModel *MemoryModel, + arbos_tag uint32, +) ([]byte, error) { + db := interpreter.Evm().StateDB + debug := stylusParams.DebugMode + + if len(localAsm) == 0 { + log.Error("missing asm", "program", address, "module", moduleHash) + panic("missing asm") + } + + if db, ok := db.(*state.StateDB); ok { + db.RecordProgram(moduleHash) + } + + evmApi := newApi(interpreter, tracingInfo, scope, memoryModel) + defer evmApi.drop() + + output := &rustBytes{} + status := userStatus(C.stylus_call( + goSlice(localAsm), + goSlice(calldata), + stylusParams.encode(), + evmApi.cNative, + evmData.encode(), + cbool(debug), + output, + (*u64)(&scope.Contract.Gas), + u32(arbos_tag), + )) + + depth := interpreter.Depth() + data, msg, err := status.toResult(output.intoBytes(), debug) + if status == userFailure && debug { + log.Warn("program failure", "err", err, "msg", msg, "program", address, "depth", depth) + } + return data, err +} + +//export handleReqImpl +func handleReqImpl(apiId usize, req_type u32, data *rustSlice, costPtr *u64, out_response *C.GoSliceData, out_raw_data *C.GoSliceData) { + api := getApi(apiId) + reqData := data.read() + reqType := RequestType(req_type - EvmApiMethodReqOffset) + response, raw_data, cost := api.handler(reqType, reqData) + *costPtr = u64(cost) + api.pinAndRef(response, out_response) + api.pinAndRef(raw_data, out_raw_data) +} + +// Caches a program in Rust. We write a record so that we can undo on revert. +// For gas estimation and eth_call, we ignore permanent updates and rely on Rust's LRU. +func cacheProgram(db vm.StateDB, module common.Hash, program Program, params *StylusParams, debug bool, time uint64, runMode core.MessageRunMode) { + if runMode == core.MessageCommitMode { + // address is only used for logging + asm, err := getLocalAsm(db, module, common.Address{}, params.PageLimit, time, debug, program) + if err != nil { + panic("unable to recreate wasm") + } + tag := db.Database().WasmCacheTag() + state.CacheWasmRust(asm, module, program.version, tag, debug) + db.RecordCacheWasm(state.CacheWasm{ModuleHash: module, Version: program.version, Tag: tag, Debug: debug}) + } +} + +// Evicts a program in Rust. We write a record so that we can undo on revert, unless we don't need to (e.g. expired) +// For gas estimation and eth_call, we ignore permanent updates and rely on Rust's LRU. +func evictProgram(db vm.StateDB, module common.Hash, version uint16, debug bool, runMode core.MessageRunMode, forever bool) { + if runMode == core.MessageCommitMode { + tag := db.Database().WasmCacheTag() + state.EvictWasmRust(module, version, tag, debug) + if !forever { + db.RecordEvictWasm(state.EvictWasm{ModuleHash: module, Version: version, Tag: tag, Debug: debug}) + } + } +} + +func init() { + state.CacheWasmRust = func(asm []byte, moduleHash common.Hash, version uint16, tag uint32, debug bool) { + C.stylus_cache_module(goSlice(asm), hashToBytes32(moduleHash), u16(version), u32(tag), cbool(debug)) + } + state.EvictWasmRust = func(moduleHash common.Hash, version uint16, tag uint32, debug bool) { + C.stylus_evict_module(hashToBytes32(moduleHash), u16(version), u32(tag), cbool(debug)) + } +} + +func ResizeWasmLruCache(size uint32) { + C.stylus_cache_lru_resize(u32(size)) +} + +func (value bytes32) toHash() common.Hash { + hash := common.Hash{} + for index, b := range value.bytes { + hash[index] = byte(b) + } + return hash +} + +func hashToBytes32(hash common.Hash) bytes32 { + value := bytes32{} + for index, b := range hash.Bytes() { + value.bytes[index] = u8(b) + } + return value +} + +func addressToBytes20(addr common.Address) bytes20 { + value := bytes20{} + for index, b := range addr.Bytes() { + value.bytes[index] = u8(b) + } + return value +} + +func (slice *rustSlice) read() []byte { + return arbutil.PointerToSlice((*byte)(slice.ptr), int(slice.len)) +} + +func (vec *rustBytes) read() []byte { + return arbutil.PointerToSlice((*byte)(vec.ptr), int(vec.len)) +} + +func (vec *rustBytes) intoBytes() []byte { + slice := vec.read() + vec.drop() + return slice +} + +func (vec *rustBytes) drop() { + C.stylus_drop_vec(*vec) +} + +func goSlice(slice []byte) C.GoSliceData { + return C.GoSliceData{ + ptr: (*u8)(arbutil.SliceToPointer(slice)), + len: usize(len(slice)), + } +} + +func (params *ProgParams) encode() C.StylusConfig { + pricing := C.PricingParams{ + ink_price: u32(params.InkPrice.ToUint32()), + } + return C.StylusConfig{ + version: u16(params.Version), + max_depth: u32(params.MaxDepth), + pricing: pricing, + } +} + +func (data *EvmData) encode() C.EvmData { + return C.EvmData{ + block_basefee: hashToBytes32(data.blockBasefee), + chainid: u64(data.chainId), + block_coinbase: addressToBytes20(data.blockCoinbase), + block_gas_limit: u64(data.blockGasLimit), + block_number: u64(data.blockNumber), + block_timestamp: u64(data.blockTimestamp), + contract_address: addressToBytes20(data.contractAddress), + module_hash: hashToBytes32(data.moduleHash), + msg_sender: addressToBytes20(data.msgSender), + msg_value: hashToBytes32(data.msgValue), + tx_gas_price: hashToBytes32(data.txGasPrice), + tx_origin: addressToBytes20(data.txOrigin), + reentrant: u32(data.reentrant), + return_data_len: 0, + cached: cbool(data.cached), + tracing: cbool(data.tracing), + } +} diff --git a/arbos/programs/native_api.go b/arbos/programs/native_api.go new file mode 100644 index 0000000000..136f74c964 --- /dev/null +++ b/arbos/programs/native_api.go @@ -0,0 +1,95 @@ +// Copyright 2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +//go:build !wasm +// +build !wasm + +package programs + +/* +#cgo CFLAGS: -g -Wall -I../../target/include/ +#cgo LDFLAGS: ${SRCDIR}/../../target/lib/libstylus.a -ldl -lm +#include "arbitrator.h" + +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef size_t usize; + +void handleReqImpl(usize api, u32 req_type, RustSlice *data, u64 *out_cost, GoSliceData *out_result, GoSliceData *out_raw_data); +void handleReqWrap(usize api, u32 req_type, RustSlice *data, u64 *out_cost, GoSliceData *out_result, GoSliceData *out_raw_data) { + return handleReqImpl(api, req_type, data, out_cost, out_result, out_raw_data); +} +*/ +import "C" +import ( + "runtime" + "sync" + "sync/atomic" + + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/arbos/util" + "github.com/offchainlabs/nitro/arbutil" +) + +var apiObjects sync.Map +var apiIds uintptr // atomic and sequential + +type NativeApi struct { + handler RequestHandler + cNative C.NativeRequestHandler + pinner runtime.Pinner +} + +func newApi( + interpreter *vm.EVMInterpreter, + tracingInfo *util.TracingInfo, + scope *vm.ScopeContext, + memoryModel *MemoryModel, +) NativeApi { + handler := newApiClosures(interpreter, tracingInfo, scope, memoryModel) + apiId := atomic.AddUintptr(&apiIds, 1) + id := usize(apiId) + api := NativeApi{ + handler: handler, + cNative: C.NativeRequestHandler{ + handle_request_fptr: (*[0]byte)(C.handleReqWrap), + id: id, + }, + pinner: runtime.Pinner{}, + } + api.pinner.Pin(&api) + apiObjects.Store(apiId, api) + return api +} + +func getApi(id usize) NativeApi { + any, ok := apiObjects.Load(uintptr(id)) + if !ok { + log.Crit("failed to load stylus Go API", "id", id) + } + api, ok := any.(NativeApi) + if !ok { + log.Crit("wrong type for stylus Go API", "id", id) + } + return api +} + +// Free the API object, and any saved request payloads. +func (api *NativeApi) drop() { + api.pinner.Unpin() + apiObjects.Delete(uintptr(api.cNative.id)) +} + +// Pins a slice until program exit during the call to `drop`. +func (api *NativeApi) pinAndRef(data []byte, goSlice *C.GoSliceData) { + if len(data) > 0 { + dataPointer := arbutil.SliceToPointer(data) + api.pinner.Pin(dataPointer) + goSlice.ptr = (*u8)(dataPointer) + } else { + goSlice.ptr = (*u8)(nil) + } + goSlice.len = usize(len(data)) +} diff --git a/arbos/programs/params.go b/arbos/programs/params.go new file mode 100644 index 0000000000..6138e36033 --- /dev/null +++ b/arbos/programs/params.go @@ -0,0 +1,159 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package programs + +import ( + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "github.com/offchainlabs/nitro/arbos/storage" + "github.com/offchainlabs/nitro/arbos/util" + am "github.com/offchainlabs/nitro/util/arbmath" +) + +const MaxWasmSize = 128 * 1024 // max decompressed wasm size (programs are also bounded by compressed size) +const initialStackDepth = 4 * 65536 // 4 page stack. +const InitialFreePages = 2 // 2 pages come free (per tx). +const InitialPageGas = 1000 // linear cost per allocation. +const initialPageRamp = 620674314 // targets 8MB costing 32 million gas, minus the linear term. +const initialPageLimit = 128 // reject wasms with memories larger than 8MB. +const initialInkPrice = 10000 // 1 evm gas buys 10k ink. +const initialMinInitGas = 72 // charge 72 * 128 = 9216 gas. +const initialMinCachedGas = 11 // charge 11 * 32 = 352 gas. +const initialInitCostScalar = 50 // scale costs 1:1 (100%) +const initialCachedCostScalar = 50 // scale costs 1:1 (100%) +const initialExpiryDays = 365 // deactivate after 1 year. +const initialKeepaliveDays = 31 // wait a month before allowing reactivation. +const initialRecentCacheSize = 32 // cache the 32 most recent programs. + +const MinCachedGasUnits = 32 /// 32 gas for each unit +const MinInitGasUnits = 128 // 128 gas for each unit +const CostScalarPercent = 2 // 2% for each unit + +// This struct exists to collect the many Stylus configuration parameters into a single word. +// The items here must only be modified in ArbOwner precompile methods (or in ArbOS upgrades). +type StylusParams struct { + backingStorage *storage.Storage + Version uint16 // must only be changed during ArbOS upgrades + InkPrice uint24 + MaxStackDepth uint32 + FreePages uint16 + PageGas uint16 + PageRamp uint64 + PageLimit uint16 + MinInitGas uint8 // measured in 128-gas increments + MinCachedInitGas uint8 // measured in 32-gas increments + InitCostScalar uint8 // measured in 2% increments + CachedCostScalar uint8 // measured in 2% increments + ExpiryDays uint16 + KeepaliveDays uint16 + BlockCacheSize uint16 +} + +// Provides a view of the Stylus parameters. Call Save() to persist. +// Note: this method never returns nil. +func (p Programs) Params() (*StylusParams, error) { + sto := p.backingStorage.OpenCachedSubStorage(paramsKey) + + // assume reads are warm due to the frequency of access + if err := sto.Burner().Burn(1 * params.WarmStorageReadCostEIP2929); err != nil { + return &StylusParams{}, err + } + + // paid for the reads above + next := uint64(0) + data := []byte{} + take := func(count int) []byte { + if len(data) < count { + word := sto.GetFree(util.UintToHash(next)) + data = word[:] + next += 1 + } + value := data[:count] + data = data[count:] + return value + } + + // order matters! + return &StylusParams{ + backingStorage: sto, + Version: am.BytesToUint16(take(2)), + InkPrice: am.BytesToUint24(take(3)), + MaxStackDepth: am.BytesToUint32(take(4)), + FreePages: am.BytesToUint16(take(2)), + PageGas: am.BytesToUint16(take(2)), + PageRamp: initialPageRamp, + PageLimit: am.BytesToUint16(take(2)), + MinInitGas: am.BytesToUint8(take(1)), + MinCachedInitGas: am.BytesToUint8(take(1)), + InitCostScalar: am.BytesToUint8(take(1)), + CachedCostScalar: am.BytesToUint8(take(1)), + ExpiryDays: am.BytesToUint16(take(2)), + KeepaliveDays: am.BytesToUint16(take(2)), + BlockCacheSize: am.BytesToUint16(take(2)), + }, nil +} + +// Writes the params to permanent storage. +func (p *StylusParams) Save() error { + if p.backingStorage == nil { + log.Error("tried to Save invalid StylusParams") + return errors.New("invalid StylusParams") + } + + // order matters! + data := am.ConcatByteSlices( + am.Uint16ToBytes(p.Version), + am.Uint24ToBytes(p.InkPrice), + am.Uint32ToBytes(p.MaxStackDepth), + am.Uint16ToBytes(p.FreePages), + am.Uint16ToBytes(p.PageGas), + am.Uint16ToBytes(p.PageLimit), + am.Uint8ToBytes(p.MinInitGas), + am.Uint8ToBytes(p.MinCachedInitGas), + am.Uint8ToBytes(p.InitCostScalar), + am.Uint8ToBytes(p.CachedCostScalar), + am.Uint16ToBytes(p.ExpiryDays), + am.Uint16ToBytes(p.KeepaliveDays), + am.Uint16ToBytes(p.BlockCacheSize), + ) + + slot := uint64(0) + for len(data) != 0 { + next := am.MinInt(32, len(data)) + info := data[:next] + data = data[next:] + + word := common.Hash{} + copy(word[:], info) // right-pad with zeros + if err := p.backingStorage.SetByUint64(slot, word); err != nil { + return err + } + slot += 1 + } + return nil +} + +func initStylusParams(sto *storage.Storage) { + params := &StylusParams{ + backingStorage: sto, + Version: 1, + InkPrice: initialInkPrice, + MaxStackDepth: initialStackDepth, + FreePages: InitialFreePages, + PageGas: InitialPageGas, + PageRamp: initialPageRamp, + PageLimit: initialPageLimit, + MinInitGas: initialMinInitGas, + MinCachedInitGas: initialMinCachedGas, + InitCostScalar: initialInitCostScalar, + CachedCostScalar: initialCachedCostScalar, + ExpiryDays: initialExpiryDays, + KeepaliveDays: initialKeepaliveDays, + BlockCacheSize: initialRecentCacheSize, + } + _ = params.Save() +} diff --git a/arbos/programs/programs.go b/arbos/programs/programs.go new file mode 100644 index 0000000000..97b5477afc --- /dev/null +++ b/arbos/programs/programs.go @@ -0,0 +1,543 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package programs + +import ( + "errors" + "fmt" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/arbcompress" + "github.com/offchainlabs/nitro/arbos/addressSet" + "github.com/offchainlabs/nitro/arbos/storage" + "github.com/offchainlabs/nitro/arbos/util" + "github.com/offchainlabs/nitro/arbutil" + am "github.com/offchainlabs/nitro/util/arbmath" +) + +type Programs struct { + backingStorage *storage.Storage + programs *storage.Storage + moduleHashes *storage.Storage + dataPricer *DataPricer + cacheManagers *addressSet.AddressSet +} + +type Program struct { + version uint16 + initCost uint16 + cachedCost uint16 + footprint uint16 + asmEstimateKb uint24 // Predicted size of the asm + activatedAt uint24 // Hours since Arbitrum began + ageSeconds uint64 // Not stored in state + cached bool +} + +type uint24 = am.Uint24 + +var paramsKey = []byte{0} +var programDataKey = []byte{1} +var moduleHashesKey = []byte{2} +var dataPricerKey = []byte{3} +var cacheManagersKey = []byte{4} + +var ErrProgramActivation = errors.New("program activation failed") + +var ProgramNotWasmError func() error +var ProgramNotActivatedError func() error +var ProgramNeedsUpgradeError func(version, stylusVersion uint16) error +var ProgramExpiredError func(age uint64) error +var ProgramUpToDateError func() error +var ProgramKeepaliveTooSoon func(age uint64) error + +func Initialize(sto *storage.Storage) { + initStylusParams(sto.OpenSubStorage(paramsKey)) + initDataPricer(sto.OpenSubStorage(dataPricerKey)) + _ = addressSet.Initialize(sto.OpenCachedSubStorage(cacheManagersKey)) +} + +func Open(sto *storage.Storage) *Programs { + return &Programs{ + backingStorage: sto, + programs: sto.OpenSubStorage(programDataKey), + moduleHashes: sto.OpenSubStorage(moduleHashesKey), + dataPricer: openDataPricer(sto.OpenCachedSubStorage(dataPricerKey)), + cacheManagers: addressSet.OpenAddressSet(sto.OpenCachedSubStorage(cacheManagersKey)), + } +} + +func (p Programs) DataPricer() *DataPricer { + return p.dataPricer +} + +func (p Programs) CacheManagers() *addressSet.AddressSet { + return p.cacheManagers +} + +func (p Programs) ActivateProgram(evm *vm.EVM, address common.Address, runMode core.MessageRunMode, debugMode bool) ( + uint16, common.Hash, common.Hash, *big.Int, bool, error, +) { + statedb := evm.StateDB + codeHash := statedb.GetCodeHash(address) + burner := p.programs.Burner() + time := evm.Context.Time + + if statedb.HasSelfDestructed(address) { + return 0, codeHash, common.Hash{}, nil, false, errors.New("self destructed") + } + + params, err := p.Params() + if err != nil { + return 0, codeHash, common.Hash{}, nil, false, err + } + + stylusVersion := params.Version + currentVersion, expired, cached, err := p.programExists(codeHash, time, params) + if err != nil { + return 0, codeHash, common.Hash{}, nil, false, err + } + if currentVersion == stylusVersion && !expired { + // already activated and up to date + return 0, codeHash, common.Hash{}, nil, false, ProgramUpToDateError() + } + wasm, err := getWasm(statedb, address) + if err != nil { + return 0, codeHash, common.Hash{}, nil, false, err + } + + // require the program's footprint not exceed the remaining memory budget + pageLimit := am.SaturatingUSub(params.PageLimit, statedb.GetStylusPagesOpen()) + + info, err := activateProgram(statedb, address, codeHash, wasm, pageLimit, stylusVersion, debugMode, burner) + if err != nil { + return 0, codeHash, common.Hash{}, nil, true, err + } + + // remove prev asm + if cached { + oldModuleHash, err := p.moduleHashes.Get(codeHash) + if err != nil { + return 0, codeHash, common.Hash{}, nil, true, err + } + evictProgram(statedb, oldModuleHash, currentVersion, debugMode, runMode, expired) + } + if err := p.moduleHashes.Set(codeHash, info.moduleHash); err != nil { + return 0, codeHash, common.Hash{}, nil, true, err + } + + estimateKb, err := am.IntToUint24(am.DivCeil(info.asmEstimate, 1024)) // stored in kilobytes + if err != nil { + return 0, codeHash, common.Hash{}, nil, true, err + } + + dataFee, err := p.dataPricer.UpdateModel(info.asmEstimate, time) + if err != nil { + return 0, codeHash, common.Hash{}, nil, true, err + } + + programData := Program{ + version: stylusVersion, + initCost: info.initGas, + cachedCost: info.cachedInitGas, + footprint: info.footprint, + asmEstimateKb: estimateKb, + activatedAt: hoursSinceArbitrum(time), + cached: cached, + } + // replace the cached asm + if cached { + cacheProgram(statedb, info.moduleHash, programData, params, debugMode, time, runMode) + } + + return stylusVersion, codeHash, info.moduleHash, dataFee, false, p.setProgram(codeHash, programData) +} + +func (p Programs) CallProgram( + scope *vm.ScopeContext, + statedb vm.StateDB, + interpreter *vm.EVMInterpreter, + tracingInfo *util.TracingInfo, + calldata []byte, + reentrant bool, + runmode core.MessageRunMode, +) ([]byte, error) { + evm := interpreter.Evm() + contract := scope.Contract + codeHash := contract.CodeHash + debugMode := evm.ChainConfig().DebugMode() + + params, err := p.Params() + if err != nil { + return nil, err + } + + program, err := p.getActiveProgram(codeHash, evm.Context.Time, params) + if err != nil { + return nil, err + } + moduleHash, err := p.moduleHashes.Get(codeHash) + if err != nil { + return nil, err + } + goParams := p.progParams(program.version, debugMode, params) + l1BlockNumber, err := evm.ProcessingHook.L1BlockNumber(evm.Context) + if err != nil { + return nil, err + } + + // pay for memory init + open, ever := statedb.GetStylusPages() + model := NewMemoryModel(params.FreePages, params.PageGas) + callCost := model.GasCost(program.footprint, open, ever) + + // pay for program init + cached := program.cached || statedb.GetRecentWasms().Insert(codeHash, params.BlockCacheSize) + if cached { + callCost = am.SaturatingUAdd(callCost, program.cachedGas(params)) + } else { + callCost = am.SaturatingUAdd(callCost, program.initGas(params)) + } + if err := contract.BurnGas(callCost); err != nil { + return nil, err + } + statedb.AddStylusPages(program.footprint) + defer statedb.SetStylusPagesOpen(open) + + localAsm, err := getLocalAsm(statedb, moduleHash, contract.Address(), params.PageLimit, evm.Context.Time, debugMode, program) + if err != nil { + log.Crit("failed to get local wasm for activated program", "program", contract.Address()) + return nil, err + } + + evmData := &EvmData{ + blockBasefee: common.BigToHash(evm.Context.BaseFee), + chainId: evm.ChainConfig().ChainID.Uint64(), + blockCoinbase: evm.Context.Coinbase, + blockGasLimit: evm.Context.GasLimit, + blockNumber: l1BlockNumber, + blockTimestamp: evm.Context.Time, + contractAddress: scope.Contract.Address(), + moduleHash: moduleHash, + msgSender: scope.Contract.Caller(), + msgValue: scope.Contract.Value().Bytes32(), + txGasPrice: common.BigToHash(evm.TxContext.GasPrice), + txOrigin: evm.TxContext.Origin, + reentrant: am.BoolToUint32(reentrant), + cached: program.cached, + tracing: tracingInfo != nil, + } + + address := contract.Address() + if contract.CodeAddr != nil { + address = *contract.CodeAddr + } + var arbos_tag uint32 + if runmode == core.MessageCommitMode { + arbos_tag = statedb.Database().WasmCacheTag() + } + return callProgram(address, moduleHash, localAsm, scope, interpreter, tracingInfo, calldata, evmData, goParams, model, arbos_tag) +} + +func getWasm(statedb vm.StateDB, program common.Address) ([]byte, error) { + prefixedWasm := statedb.GetCode(program) + if prefixedWasm == nil { + return nil, ProgramNotWasmError() + } + wasm, dictByte, err := state.StripStylusPrefix(prefixedWasm) + if err != nil { + return nil, err + } + + var dict arbcompress.Dictionary + switch dictByte { + case 0: + dict = arbcompress.EmptyDictionary + case 1: + dict = arbcompress.StylusProgramDictionary + default: + return nil, fmt.Errorf("unsupported dictionary %v", dictByte) + } + return arbcompress.DecompressWithDictionary(wasm, MaxWasmSize, dict) +} + +// Gets a program entry, which may be expired or not yet activated. +func (p Programs) getProgram(codeHash common.Hash, time uint64) (Program, error) { + data, err := p.programs.Get(codeHash) + program := Program{ + version: am.BytesToUint16(data[:2]), + initCost: am.BytesToUint16(data[2:4]), + cachedCost: am.BytesToUint16(data[4:6]), + footprint: am.BytesToUint16(data[6:8]), + activatedAt: am.BytesToUint24(data[8:11]), + asmEstimateKb: am.BytesToUint24(data[11:14]), + cached: am.BytesToBool(data[14:15]), + } + program.ageSeconds = hoursToAge(time, program.activatedAt) + return program, err +} + +// Gets a program entry. Errors if not active. +func (p Programs) getActiveProgram(codeHash common.Hash, time uint64, params *StylusParams) (Program, error) { + program, err := p.getProgram(codeHash, time) + if err != nil { + return program, err + } + if program.version == 0 { + return program, ProgramNotActivatedError() + } + + // check that the program is up to date + stylusVersion := params.Version + if program.version != stylusVersion { + return program, ProgramNeedsUpgradeError(program.version, stylusVersion) + } + + // ensure the program hasn't expired + if program.ageSeconds > am.DaysToSeconds(params.ExpiryDays) { + return program, ProgramExpiredError(program.ageSeconds) + } + return program, nil +} + +func (p Programs) setProgram(codehash common.Hash, program Program) error { + data := common.Hash{} + copy(data[0:], am.Uint16ToBytes(program.version)) + copy(data[2:], am.Uint16ToBytes(program.initCost)) + copy(data[4:], am.Uint16ToBytes(program.cachedCost)) + copy(data[6:], am.Uint16ToBytes(program.footprint)) + copy(data[8:], am.Uint24ToBytes(program.activatedAt)) + copy(data[11:], am.Uint24ToBytes(program.asmEstimateKb)) + copy(data[14:], am.BoolToBytes(program.cached)) + return p.programs.Set(codehash, data) +} + +func (p Programs) programExists(codeHash common.Hash, time uint64, params *StylusParams) (uint16, bool, bool, error) { + program, err := p.getProgram(codeHash, time) + if err != nil { + return 0, false, false, err + } + activatedAt := program.activatedAt + expired := activatedAt == 0 || hoursToAge(time, activatedAt) > am.DaysToSeconds(params.ExpiryDays) + return program.version, expired, program.cached, err +} + +func (p Programs) ProgramKeepalive(codeHash common.Hash, time uint64, params *StylusParams) (*big.Int, error) { + program, err := p.getActiveProgram(codeHash, time, params) + if err != nil { + return nil, err + } + if program.ageSeconds < am.DaysToSeconds(params.KeepaliveDays) { + return nil, ProgramKeepaliveTooSoon(program.ageSeconds) + } + + stylusVersion := params.Version + if program.version != stylusVersion { + return nil, ProgramNeedsUpgradeError(program.version, stylusVersion) + } + + dataFee, err := p.dataPricer.UpdateModel(program.asmSize(), time) + if err != nil { + return nil, err + } + program.activatedAt = hoursSinceArbitrum(time) + return dataFee, p.setProgram(codeHash, program) +} + +// Gets whether a program is cached. Note that the program may be expired. +func (p Programs) ProgramCached(codeHash common.Hash) (bool, error) { + data, err := p.programs.Get(codeHash) + return am.BytesToBool(data[14:15]), err +} + +// Sets whether a program is cached. Errors if trying to cache an expired program. +func (p Programs) SetProgramCached( + emitEvent func() error, + db vm.StateDB, + codeHash common.Hash, + cache bool, + time uint64, + params *StylusParams, + runMode core.MessageRunMode, + debug bool, +) error { + program, err := p.getProgram(codeHash, time) + if err != nil { + return err + } + expired := program.ageSeconds > am.DaysToSeconds(params.ExpiryDays) + + if program.version == 0 && cache { + return ProgramNeedsUpgradeError(0, params.Version) + } + if expired && cache { + return ProgramExpiredError(program.ageSeconds) + } + if program.cached == cache { + return nil + } + if err := emitEvent(); err != nil { + return err + } + + // pay to cache the program, or to re-cache in case of upcoming revert + if err := p.programs.Burner().Burn(uint64(program.initCost)); err != nil { + return err + } + moduleHash, err := p.moduleHashes.Get(codeHash) + if err != nil { + return err + } + if cache { + cacheProgram(db, moduleHash, program, params, debug, time, runMode) + } else { + evictProgram(db, moduleHash, program.version, debug, runMode, expired) + } + program.cached = cache + return p.setProgram(codeHash, program) +} + +func (p Programs) CodehashVersion(codeHash common.Hash, time uint64, params *StylusParams) (uint16, error) { + program, err := p.getActiveProgram(codeHash, time, params) + if err != nil { + return 0, err + } + return program.version, nil +} + +// Gets the number of seconds left until expiration. Errors if it's already happened. +func (p Programs) ProgramTimeLeft(codeHash common.Hash, time uint64, params *StylusParams) (uint64, error) { + program, err := p.getActiveProgram(codeHash, time, params) + if err != nil { + return 0, err + } + age := hoursToAge(time, program.activatedAt) + expirySeconds := am.DaysToSeconds(params.ExpiryDays) + if age > expirySeconds { + return 0, ProgramExpiredError(age) + } + return am.SaturatingUSub(expirySeconds, age), nil +} + +func (p Programs) ProgramInitGas(codeHash common.Hash, time uint64, params *StylusParams) (uint64, uint64, error) { + program, err := p.getActiveProgram(codeHash, time, params) + return program.initGas(params), program.cachedGas(params), err +} + +func (p Programs) ProgramMemoryFootprint(codeHash common.Hash, time uint64, params *StylusParams) (uint16, error) { + program, err := p.getActiveProgram(codeHash, time, params) + return program.footprint, err +} + +func (p Programs) ProgramAsmSize(codeHash common.Hash, time uint64, params *StylusParams) (uint32, error) { + program, err := p.getActiveProgram(codeHash, time, params) + if err != nil { + return 0, err + } + return program.asmSize(), nil +} + +func (p Program) asmSize() uint32 { + return am.SaturatingUMul(p.asmEstimateKb.ToUint32(), 1024) +} + +func (p Program) initGas(params *StylusParams) uint64 { + base := uint64(params.MinInitGas) * MinInitGasUnits + dyno := am.SaturatingUMul(uint64(p.initCost), uint64(params.InitCostScalar)*CostScalarPercent) + return am.SaturatingUAdd(base, am.DivCeil(dyno, 100)) +} + +func (p Program) cachedGas(params *StylusParams) uint64 { + base := uint64(params.MinCachedInitGas) * MinCachedGasUnits + dyno := am.SaturatingUMul(uint64(p.cachedCost), uint64(params.CachedCostScalar)*CostScalarPercent) + return am.SaturatingUAdd(base, am.DivCeil(dyno, 100)) +} + +type ProgParams struct { + Version uint16 + MaxDepth uint32 + InkPrice uint24 + DebugMode bool +} + +func (p Programs) progParams(version uint16, debug bool, params *StylusParams) *ProgParams { + return &ProgParams{ + Version: version, + MaxDepth: params.MaxStackDepth, + InkPrice: params.InkPrice, + DebugMode: debug, + } +} + +type EvmData struct { + blockBasefee common.Hash + chainId uint64 + blockCoinbase common.Address + blockGasLimit uint64 + blockNumber uint64 + blockTimestamp uint64 + contractAddress common.Address + moduleHash common.Hash + msgSender common.Address + msgValue common.Hash + txGasPrice common.Hash + txOrigin common.Address + reentrant uint32 + cached bool + tracing bool +} + +type activationInfo struct { + moduleHash common.Hash + initGas uint16 + cachedInitGas uint16 + asmEstimate uint32 + footprint uint16 +} + +type userStatus uint8 + +const ( + userSuccess userStatus = iota + userRevert + userFailure + userOutOfInk + userOutOfStack +) + +func (status userStatus) toResult(data []byte, debug bool) ([]byte, string, error) { + msg := arbutil.ToStringOrHex(data) + switch status { + case userSuccess: + return data, "", nil + case userRevert: + return data, msg, vm.ErrExecutionReverted + case userFailure: + return nil, msg, vm.ErrExecutionReverted + case userOutOfInk: + return nil, "", vm.ErrOutOfGas + case userOutOfStack: + return nil, "", vm.ErrDepth + default: + log.Error("program errored with unknown status", "status", status, "data", msg) + return nil, msg, vm.ErrExecutionReverted + } +} + +// Hours since Arbitrum began, rounded down. +func hoursSinceArbitrum(time uint64) uint24 { + return am.SaturatingUUCast[uint24]((am.SaturatingUSub(time, ArbitrumStartTime)) / 3600) +} + +// Computes program age in seconds from the hours passed since Arbitrum began. +func hoursToAge(time uint64, hours uint24) uint64 { + seconds := am.SaturatingUMul(uint64(hours), 3600) + activatedAt := am.SaturatingUAdd(ArbitrumStartTime, seconds) + return am.SaturatingUSub(time, activatedAt) +} diff --git a/arbos/programs/testconstants.go b/arbos/programs/testconstants.go new file mode 100644 index 0000000000..1ab0e6e93b --- /dev/null +++ b/arbos/programs/testconstants.go @@ -0,0 +1,101 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +//go:build !wasm +// +build !wasm + +package programs + +// This file exists because cgo isn't allowed in tests + +/* +#cgo CFLAGS: -g -Wall -I../../target/include/ +#include "arbitrator.h" +*/ +import "C" +import "fmt" + +func testConstants() error { + + // this closure exists to avoid polluting the package namespace + index := 1 + errIfNotEq := func(a RequestType, b uint32) error { + if uint32(a) != b { + return fmt.Errorf("constant test %d failed! %d != %d", index, a, b) + } + index += 1 + return nil + } + + if err := errIfNotEq(GetBytes32, C.EvmApiMethod_GetBytes32); err != nil { + return err + } + if err := errIfNotEq(SetTrieSlots, C.EvmApiMethod_SetTrieSlots); err != nil { + return err + } + if err := errIfNotEq(GetTransientBytes32, C.EvmApiMethod_GetTransientBytes32); err != nil { + return err + } + if err := errIfNotEq(SetTransientBytes32, C.EvmApiMethod_SetTransientBytes32); err != nil { + return err + } + if err := errIfNotEq(ContractCall, C.EvmApiMethod_ContractCall); err != nil { + return err + } + if err := errIfNotEq(DelegateCall, C.EvmApiMethod_DelegateCall); err != nil { + return err + } + if err := errIfNotEq(StaticCall, C.EvmApiMethod_StaticCall); err != nil { + return err + } + if err := errIfNotEq(Create1, C.EvmApiMethod_Create1); err != nil { + return err + } + if err := errIfNotEq(Create2, C.EvmApiMethod_Create2); err != nil { + return err + } + if err := errIfNotEq(EmitLog, C.EvmApiMethod_EmitLog); err != nil { + return err + } + if err := errIfNotEq(AccountBalance, C.EvmApiMethod_AccountBalance); err != nil { + return err + } + if err := errIfNotEq(AccountCode, C.EvmApiMethod_AccountCode); err != nil { + return err + } + if err := errIfNotEq(AccountCodeHash, C.EvmApiMethod_AccountCodeHash); err != nil { + return err + } + if err := errIfNotEq(AddPages, C.EvmApiMethod_AddPages); err != nil { + return err + } + if err := errIfNotEq(CaptureHostIO, C.EvmApiMethod_CaptureHostIO); err != nil { + return err + } + if err := errIfNotEq(EvmApiMethodReqOffset, C.EVM_API_METHOD_REQ_OFFSET); err != nil { + return err + } + + index = 0 + assertEq := func(a apiStatus, b uint32) error { + if uint32(a) != b { + return fmt.Errorf("constant test %d failed! %d != %d", index, a, b) + } + index += 1 + return nil + } + + if err := assertEq(Success, C.EvmApiStatus_Success); err != nil { + return err + } + if err := assertEq(Failure, C.EvmApiStatus_Failure); err != nil { + return err + } + if err := assertEq(OutOfGas, C.EvmApiStatus_OutOfGas); err != nil { + return err + } + if err := assertEq(WriteProtection, C.EvmApiStatus_WriteProtection); err != nil { + return err + } + return nil +} diff --git a/arbos/programs/wasm.go b/arbos/programs/wasm.go new file mode 100644 index 0000000000..4bc978a2b6 --- /dev/null +++ b/arbos/programs/wasm.go @@ -0,0 +1,200 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +//go:build wasm +// +build wasm + +package programs + +import ( + "errors" + "unsafe" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/arbos/burn" + "github.com/offchainlabs/nitro/arbos/util" + "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/util/arbmath" +) + +type addr = common.Address +type hash = common.Hash + +// rust types +type u8 = uint8 +type u16 = uint16 +type u32 = uint32 +type u64 = uint64 +type usize = uintptr + +// opaque types +type rustVec byte +type rustConfig byte +type rustModule byte +type rustEvmData byte + +//go:wasmimport programs activate +func programActivate( + wasm_ptr unsafe.Pointer, + wasm_size uint32, + pages_ptr unsafe.Pointer, + asm_estimation_ptr unsafe.Pointer, + init_gas_ptr unsafe.Pointer, + cached_init_gas_ptr unsafe.Pointer, + version uint32, + debug uint32, + codehash unsafe.Pointer, + module_hash_ptr unsafe.Pointer, + gas_ptr unsafe.Pointer, + err_buf unsafe.Pointer, + err_buf_len uint32, +) uint32 + +func activateProgram( + db vm.StateDB, + program addr, + codehash common.Hash, + wasm []byte, + pageLimit u16, + version u16, + debug bool, + burner burn.Burner, +) (*activationInfo, error) { + errBuf := make([]byte, 1024) + debugMode := arbmath.BoolToUint32(debug) + moduleHash := common.Hash{} + gasPtr := burner.GasLeft() + asmEstimate := uint32(0) + initGas := uint16(0) + cachedInitGas := uint16(0) + + footprint := uint16(pageLimit) + errLen := programActivate( + arbutil.SliceToUnsafePointer(wasm), + uint32(len(wasm)), + unsafe.Pointer(&footprint), + unsafe.Pointer(&asmEstimate), + unsafe.Pointer(&initGas), + unsafe.Pointer(&cachedInitGas), + uint32(version), + debugMode, + arbutil.SliceToUnsafePointer(codehash[:]), + arbutil.SliceToUnsafePointer(moduleHash[:]), + unsafe.Pointer(gasPtr), + arbutil.SliceToUnsafePointer(errBuf), + uint32(len(errBuf)), + ) + if errLen != 0 { + err := errors.New(string(errBuf[:errLen])) + return nil, err + } + return &activationInfo{moduleHash, initGas, cachedInitGas, asmEstimate, footprint}, nil +} + +// stub any non-consensus, Rust-side caching updates +func cacheProgram(db vm.StateDB, module common.Hash, program Program, params *StylusParams, debug bool, time uint64, runMode core.MessageRunMode) { +} +func evictProgram(db vm.StateDB, module common.Hash, version uint16, debug bool, mode core.MessageRunMode, forever bool) { +} + +//go:wasmimport programs new_program +func newProgram( + hashPtr unsafe.Pointer, + callDataPtr unsafe.Pointer, + callDataSize uint32, + configHandler stylusConfigHandler, + evmHandler evmDataHandler, + gas uint64, +) uint32 + +//go:wasmimport programs pop +func popProgram() + +//go:wasmimport programs set_response +func setResponse(id uint32, gas uint64, result unsafe.Pointer, result_len uint32, raw_data unsafe.Pointer, raw_data_len uint32) + +//go:wasmimport programs get_request +func getRequest(id uint32, reqLen unsafe.Pointer) uint32 + +//go:wasmimport programs get_request_data +func getRequestData(id uint32, dataPtr unsafe.Pointer) + +//go:wasmimport programs start_program +func startProgram(module uint32) uint32 + +//go:wasmimport programs send_response +func sendResponse(req_id uint32) uint32 + +func getLocalAsm(statedb vm.StateDB, moduleHash common.Hash, address common.Address, pagelimit uint16, time uint64, debugMode bool, program Program) ([]byte, error) { + return nil, nil +} + +func callProgram( + address common.Address, + moduleHash common.Hash, + _localAsm []byte, + scope *vm.ScopeContext, + interpreter *vm.EVMInterpreter, + tracingInfo *util.TracingInfo, + calldata []byte, + evmData *EvmData, + params *ProgParams, + memoryModel *MemoryModel, + _arbos_tag uint32, +) ([]byte, error) { + reqHandler := newApiClosures(interpreter, tracingInfo, scope, memoryModel) + gasLeft, retData, err := CallProgramLoop(moduleHash, calldata, scope.Contract.Gas, evmData, params, reqHandler) + scope.Contract.Gas = gasLeft + return retData, err +} + +func CallProgramLoop( + moduleHash common.Hash, + calldata []byte, + gas uint64, + evmData *EvmData, + params *ProgParams, + reqHandler RequestHandler) (uint64, []byte, error) { + configHandler := params.createHandler() + dataHandler := evmData.createHandler() + debug := params.DebugMode + + module := newProgram( + unsafe.Pointer(&moduleHash[0]), + arbutil.SliceToUnsafePointer(calldata), + uint32(len(calldata)), + configHandler, + dataHandler, + gas, + ) + reqId := startProgram(module) + for { + var reqLen uint32 + reqTypeId := getRequest(reqId, unsafe.Pointer(&reqLen)) + reqData := make([]byte, reqLen) + getRequestData(reqId, arbutil.SliceToUnsafePointer(reqData)) + if reqTypeId < EvmApiMethodReqOffset { + popProgram() + status := userStatus(reqTypeId) + gasLeft := arbmath.BytesToUint(reqData[:8]) + data, msg, err := status.toResult(reqData[8:], debug) + if status == userFailure && debug { + log.Warn("program failure", "err", err, "msg", msg, "moduleHash", moduleHash) + } + return gasLeft, data, err + } + + reqType := RequestType(reqTypeId - EvmApiMethodReqOffset) + result, rawData, cost := reqHandler(reqType, reqData) + setResponse( + reqId, + cost, + arbutil.SliceToUnsafePointer(result), uint32(len(result)), + arbutil.SliceToUnsafePointer(rawData), uint32(len(rawData)), + ) + reqId = sendResponse(reqId) + } +} diff --git a/arbos/programs/wasm_api.go b/arbos/programs/wasm_api.go new file mode 100644 index 0000000000..d7bac056c0 --- /dev/null +++ b/arbos/programs/wasm_api.go @@ -0,0 +1,63 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +//go:build wasm +// +build wasm + +package programs + +import ( + "unsafe" + + "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/util/arbmath" +) + +type stylusConfigHandler uint64 + +//go:wasmimport programs create_stylus_config +func createStylusConfig(version uint32, max_depth uint32, ink_price uint32, debug uint32) stylusConfigHandler + +type evmDataHandler uint64 + +//go:wasmimport programs create_evm_data +func createEvmData( + blockBaseFee unsafe.Pointer, + chainid uint64, + blockCoinbase unsafe.Pointer, + gasLimit uint64, + blockNumber uint64, + blockTimestamp uint64, + contractAddress unsafe.Pointer, + moduleHash unsafe.Pointer, + msgSender unsafe.Pointer, + msgValue unsafe.Pointer, + txGasPrice unsafe.Pointer, + txOrigin unsafe.Pointer, + cached uint32, + reentrant uint32, +) evmDataHandler + +func (params *ProgParams) createHandler() stylusConfigHandler { + debug := arbmath.BoolToUint32(params.DebugMode) + return createStylusConfig(uint32(params.Version), params.MaxDepth, params.InkPrice.ToUint32(), debug) +} + +func (data *EvmData) createHandler() evmDataHandler { + return createEvmData( + arbutil.SliceToUnsafePointer(data.blockBasefee[:]), + data.chainId, + arbutil.SliceToUnsafePointer(data.blockCoinbase[:]), + data.blockGasLimit, + data.blockNumber, + data.blockTimestamp, + arbutil.SliceToUnsafePointer(data.contractAddress[:]), + arbutil.SliceToUnsafePointer(data.moduleHash[:]), + arbutil.SliceToUnsafePointer(data.msgSender[:]), + arbutil.SliceToUnsafePointer(data.msgValue[:]), + arbutil.SliceToUnsafePointer(data.txGasPrice[:]), + arbutil.SliceToUnsafePointer(data.txOrigin[:]), + arbmath.BoolToUint32(data.cached), + data.reentrant, + ) +} diff --git a/arbos/retryables/retryable.go b/arbos/retryables/retryable.go index 6984e41904..e1cfe48bcf 100644 --- a/arbos/retryables/retryable.go +++ b/arbos/retryables/retryable.go @@ -145,7 +145,7 @@ func (rs *RetryableState) DeleteRetryable(id common.Hash, evm *vm.EVM, scenario escrowAddress := RetryableEscrowAddress(id) beneficiaryAddress := common.BytesToAddress(beneficiary[:]) amount := evm.StateDB.GetBalance(escrowAddress) - err = util.TransferBalance(&escrowAddress, &beneficiaryAddress, amount, evm, scenario, "escrow") + err = util.TransferBalance(&escrowAddress, &beneficiaryAddress, amount.ToBig(), evm, scenario, "escrow") if err != nil { return false, err } diff --git a/arbos/storage/storage.go b/arbos/storage/storage.go index 63987b91f8..158b8896c1 100644 --- a/arbos/storage/storage.go +++ b/arbos/storage/storage.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package storage @@ -6,6 +6,7 @@ package storage import ( "bytes" "fmt" + "math" "math/big" "sync/atomic" @@ -52,6 +53,7 @@ type Storage struct { const StorageReadCost = params.SloadGasEIP2200 const StorageWriteCost = params.SstoreSetGasEIP2200 const StorageWriteZeroCost = params.SstoreResetGasEIP2200 +const StorageCodeHashCost = params.ColdAccountAccessCostEIP2929 const storageKeyCacheSize = 1024 @@ -119,7 +121,12 @@ func (s *Storage) Get(key common.Hash) (common.Hash, error) { if info := s.burner.TracingInfo(); info != nil { info.RecordStorageGet(key) } - return s.db.GetState(s.account, s.mapAddress(key)), nil + return s.GetFree(key), nil +} + +// Gets a storage slot for free. Dangerous due to DoS potential. +func (s *Storage) GetFree(key common.Hash) common.Hash { + return s.db.GetState(s.account, s.mapAddress(key)) } func (s *Storage) GetStorageSlot(key common.Hash) common.Hash { @@ -139,6 +146,11 @@ func (s *Storage) GetUint64ByUint64(key uint64) (uint64, error) { return s.GetUint64(util.UintToHash(key)) } +func (s *Storage) GetUint32(key common.Hash) (uint32, error) { + value, err := s.Get(key) + return uint32(value.Big().Uint64()), err +} + func (s *Storage) Set(key common.Hash, value common.Hash) error { if s.burner.ReadOnly() { log.Error("Read-only burner attempted to mutate state", "key", key, "value", value) @@ -155,6 +167,10 @@ func (s *Storage) Set(key common.Hash, value common.Hash) error { return nil } +func (s *Storage) SetUint64(key common.Hash, value uint64) error { + return s.Set(key, util.UintToHash(value)) +} + func (s *Storage) SetByUint64(key uint64, value common.Hash) error { return s.Set(util.UintToHash(key), value) } @@ -163,6 +179,14 @@ func (s *Storage) SetUint64ByUint64(key uint64, value uint64) error { return s.Set(util.UintToHash(key), util.UintToHash(value)) } +func (s *Storage) SetUint32(key common.Hash, value uint32) error { + return s.Set(key, util.UintToHash(uint64(value))) +} + +func (s *Storage) SetByUint32(key uint32, value common.Hash) error { + return s.Set(util.UintToHash(uint64(key)), value) +} + func (s *Storage) Clear(key common.Hash) error { return s.Set(key, common.Hash{}) } @@ -280,6 +304,14 @@ func (s *Storage) ClearBytes() error { return s.ClearByUint64(0) } +func (s *Storage) GetCodeHash(address common.Address) (common.Hash, error) { + err := s.burner.Burn(StorageCodeHashCost) + if err != nil { + return common.Hash{}, err + } + return s.db.GetCodeHash(address), nil +} + func (s *Storage) Burner() burn.Burner { return s.burner // not public because these should never be changed once set } @@ -403,6 +435,86 @@ func (sbu *StorageBackedBips) Set(bips arbmath.Bips) error { return sbu.backing.Set(int64(bips)) } +// StorageBackedUBips represents an unsigned number of basis points +type StorageBackedUBips struct { + backing StorageBackedUint64 +} + +func (s *Storage) OpenStorageBackedUBips(offset uint64) StorageBackedUBips { + return StorageBackedUBips{StorageBackedUint64{s.NewSlot(offset)}} +} + +func (sbu *StorageBackedUBips) Get() (arbmath.UBips, error) { + value, err := sbu.backing.Get() + return arbmath.UBips(value), err +} + +func (sbu *StorageBackedUBips) Set(bips arbmath.UBips) error { + return sbu.backing.Set(bips.Uint64()) +} + +type StorageBackedUint16 struct { + StorageSlot +} + +func (s *Storage) OpenStorageBackedUint16(offset uint64) StorageBackedUint16 { + return StorageBackedUint16{s.NewSlot(offset)} +} + +func (sbu *StorageBackedUint16) Get() (uint16, error) { + raw, err := sbu.StorageSlot.Get() + big := raw.Big() + if !big.IsUint64() || big.Uint64() > math.MaxUint16 { + panic("expected uint16 compatible value in storage") + } + return uint16(big.Uint64()), err +} + +func (sbu *StorageBackedUint16) Set(value uint16) error { + bigValue := new(big.Int).SetUint64(uint64(value)) + return sbu.StorageSlot.Set(common.BigToHash(bigValue)) +} + +type StorageBackedUint24 struct { + StorageSlot +} + +func (s *Storage) OpenStorageBackedUint24(offset uint64) StorageBackedUint24 { + return StorageBackedUint24{s.NewSlot(offset)} +} + +func (sbu *StorageBackedUint24) Get() (arbmath.Uint24, error) { + raw, err := sbu.StorageSlot.Get() + value := arbmath.BigToUint24OrPanic(raw.Big()) + return value, err +} + +func (sbu *StorageBackedUint24) Set(value arbmath.Uint24) error { + return sbu.StorageSlot.Set(common.BigToHash(value.ToBig())) +} + +type StorageBackedUint32 struct { + StorageSlot +} + +func (s *Storage) OpenStorageBackedUint32(offset uint64) StorageBackedUint32 { + return StorageBackedUint32{s.NewSlot(offset)} +} + +func (sbu *StorageBackedUint32) Get() (uint32, error) { + raw, err := sbu.StorageSlot.Get() + big := raw.Big() + if !big.IsUint64() || big.Uint64() > math.MaxUint32 { + panic("expected uint32 compatible value in storage") + } + return uint32(big.Uint64()), err +} + +func (sbu *StorageBackedUint32) Set(value uint32) error { + bigValue := new(big.Int).SetUint64(uint64(value)) + return sbu.StorageSlot.Set(common.BigToHash(bigValue)) +} + type StorageBackedUint64 struct { StorageSlot } diff --git a/arbos/tx_processor.go b/arbos/tx_processor.go index 2a6de3b99d..59a6b19ba4 100644 --- a/arbos/tx_processor.go +++ b/arbos/tx_processor.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package arbos @@ -8,10 +8,10 @@ import ( "fmt" "math/big" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbos/l1pricing" "github.com/offchainlabs/nitro/arbos/util" - "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/util/arbmath" "github.com/ethereum/go-ethereum/core/types" @@ -42,8 +42,9 @@ type TxProcessor struct { posterGas uint64 computeHoldGas uint64 // amount of gas temporarily held to prevent compute from exceeding the gas limit delayedInbox bool // whether this tx was submitted through the delayed inbox - Callers []common.Address - TopTxType *byte // set once in StartTxHook + Contracts []*vm.Contract + Programs map[common.Address]uint // # of distinct context spans for each program + TopTxType *byte // set once in StartTxHook evm *vm.EVM CurrentRetryable *common.Hash CurrentRefundTo *common.Address @@ -63,7 +64,8 @@ func NewTxProcessor(evm *vm.EVM, msg *core.Message) *TxProcessor { PosterFee: new(big.Int), posterGas: 0, delayedInbox: evm.Context.Coinbase != l1pricing.BatchPosterAddress, - Callers: []common.Address{}, + Contracts: []*vm.Contract{}, + Programs: make(map[common.Address]uint), TopTxType: nil, evm: evm, CurrentRetryable: nil, @@ -73,12 +75,22 @@ func NewTxProcessor(evm *vm.EVM, msg *core.Message) *TxProcessor { } } -func (p *TxProcessor) PushCaller(addr common.Address) { - p.Callers = append(p.Callers, addr) +func (p *TxProcessor) PushContract(contract *vm.Contract) { + p.Contracts = append(p.Contracts, contract) + + if !contract.IsDelegateOrCallcode() { + p.Programs[contract.Address()]++ + } } -func (p *TxProcessor) PopCaller() { - p.Callers = p.Callers[:len(p.Callers)-1] +func (p *TxProcessor) PopContract() { + newLen := len(p.Contracts) - 1 + popped := p.Contracts[newLen] + p.Contracts = p.Contracts[:newLen] + + if !popped.IsDelegateOrCallcode() { + p.Programs[popped.Address()]-- + } } // Attempts to subtract up to `take` from `pool` without going negative. @@ -96,6 +108,30 @@ func takeFunds(pool *big.Int, take *big.Int) *big.Int { return new(big.Int).Set(take) } +func (p *TxProcessor) ExecuteWASM(scope *vm.ScopeContext, input []byte, interpreter *vm.EVMInterpreter) ([]byte, error) { + contract := scope.Contract + acting := contract.Address() + + var tracingInfo *util.TracingInfo + if interpreter.Config().Tracer != nil { + caller := contract.CallerAddress + tracingInfo = util.NewTracingInfo(interpreter.Evm(), caller, acting, util.TracingDuringEVM) + } + + // reentrant if more than one open same-actor context span exists + reentrant := p.Programs[acting] > 1 + + return p.state.Programs().CallProgram( + scope, + p.evm.StateDB, + interpreter, + tracingInfo, + input, + reentrant, + p.RunMode(), + ) +} + func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, returnData []byte) { // This hook is called before gas charging and will end the state transition if endTxNow is set to true // Hence, we must charge for any l2 resources if endTxNow is returned true @@ -150,7 +186,9 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r // We intentionally use the variant here that doesn't do tracing, // because this transfer is represented as the outer eth transaction. // This transfer is necessary because we don't actually invoke the EVM. - core.Transfer(evm.StateDB, from, *to, value) + // Since MintBalance already called AddBalance on `from`, + // we don't have EIP-161 concerns around not touching `from`. + core.Transfer(evm.StateDB, from, *to, uint256.MustFromBig(value)) return true, 0, nil, nil case *types.ArbitrumInternalTx: defer (startTracer())() @@ -179,7 +217,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r // check that the user has enough balance to pay for the max submission fee balanceAfterMint := evm.StateDB.GetBalance(tx.From) - if balanceAfterMint.Cmp(tx.MaxSubmissionFee) < 0 { + if balanceAfterMint.ToBig().Cmp(tx.MaxSubmissionFee) < 0 { err := fmt.Errorf( "insufficient funds for max submission fee: address %v have %v want %v", tx.From, balanceAfterMint, tx.MaxSubmissionFee, @@ -255,7 +293,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r effectiveBaseFee := evm.Context.BaseFee usergas := p.msg.GasLimit - if p.msg.TxRunMode != core.MessageCommitMode && p.msg.GasFeeCap.BitLen() == 0 { + if !p.msg.TxRunMode.ExecutedOnChain() && p.msg.GasFeeCap.BitLen() == 0 { // In gas estimation or eth_call mode, we permit a zero gas fee cap. // This matches behavior with normal tx gas estimation and eth_call. effectiveBaseFee = common.Big0 @@ -263,7 +301,7 @@ func (p *TxProcessor) StartTxHook() (endTxNow bool, gasUsed uint64, err error, r maxGasCost := arbmath.BigMulByUint(tx.GasFeeCap, usergas) maxFeePerGasTooLow := arbmath.BigLessThan(tx.GasFeeCap, effectiveBaseFee) - if arbmath.BigLessThan(balance, maxGasCost) || usergas < params.TxGas || maxFeePerGasTooLow { + if arbmath.BigLessThan(balance.ToBig(), maxGasCost) || usergas < params.TxGas || maxFeePerGasTooLow { // User either specified too low of a gas fee cap, didn't have enough balance to pay for gas, // or the specified gas limit is below the minimum transaction gas cost. // Either way, attempt to refund the gas costs, since we're not doing the auto-redeem. @@ -405,13 +443,13 @@ func (p *TxProcessor) GasChargingHook(gasRemaining *uint64) (common.Address, err basefee := p.evm.Context.BaseFee var poster common.Address - if p.msg.TxRunMode != core.MessageCommitMode { + if !p.msg.TxRunMode.ExecutedOnChain() { poster = l1pricing.BatchPosterAddress } else { poster = p.evm.Context.Coinbase } - if p.msg.TxRunMode == core.MessageCommitMode { + if p.msg.TxRunMode.ExecutedOnChain() { p.msg.SkipL1Charging = false } if basefee.Sign() > 0 && !p.msg.SkipL1Charging { @@ -449,6 +487,10 @@ func (p *TxProcessor) GasChargingHook(gasRemaining *uint64) (common.Address, err return tipReceipient, nil } +func (p *TxProcessor) RunMode() core.MessageRunMode { + return p.msg.TxRunMode +} + func (p *TxProcessor) NonrefundableGas() uint64 { // EVM-incentivized activity like freeing storage should only refund amounts paid to the network address, // which represents the overall burden to node operators. A poster's costs, then, should not be eligible @@ -474,7 +516,7 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) { if underlyingTx != nil && underlyingTx.Type() == types.ArbitrumRetryTxType { inner, _ := underlyingTx.GetInner().(*types.ArbitrumRetryTx) effectiveBaseFee := inner.GasFeeCap - if p.msg.TxRunMode == core.MessageCommitMode && !arbmath.BigEquals(effectiveBaseFee, p.evm.Context.BaseFee) { + if p.msg.TxRunMode.ExecutedOnChain() && !arbmath.BigEquals(effectiveBaseFee, p.evm.Context.BaseFee) { log.Error( "ArbitrumRetryTx GasFeeCap doesn't match basefee in commit mode", "txHash", underlyingTx.Hash(), @@ -555,7 +597,7 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) { } } // we've already credited the network fee account, but we didn't charge the gas pool yet - p.state.Restrict(p.state.L2PricingState().AddToGasPool(-arbmath.SaturatingCast(gasUsed))) + p.state.Restrict(p.state.L2PricingState().AddToGasPool(-arbmath.SaturatingCast[int64](gasUsed))) return } @@ -614,7 +656,7 @@ func (p *TxProcessor) EndTxHook(gasLeft uint64, success bool) { log.Error("total gas used < poster gas component", "gasUsed", gasUsed, "posterGas", p.posterGas) computeGas = gasUsed } - p.state.Restrict(p.state.L2PricingState().AddToGasPool(-arbmath.SaturatingCast(computeGas))) + p.state.Restrict(p.state.L2PricingState().AddToGasPool(-arbmath.SaturatingCast[int64](computeGas))) } } @@ -624,7 +666,7 @@ func (p *TxProcessor) ScheduledTxes() types.Transactions { effectiveBaseFee := p.evm.Context.BaseFee chainID := p.evm.ChainConfig().ChainID - if p.msg.TxRunMode != core.MessageCommitMode && p.msg.GasFeeCap.BitLen() == 0 { + if !p.msg.TxRunMode.ExecutedOnChain() && p.msg.GasFeeCap.BitLen() == 0 { // In gas estimation or eth_call mode, we permit a zero gas fee cap. // This matches behavior with normal tx gas estimation and eth_call. effectiveBaseFee = common.Big0 @@ -635,8 +677,7 @@ func (p *TxProcessor) ScheduledTxes() types.Transactions { if log.Address != ArbRetryableTxAddress || log.Topics[0] != RedeemScheduledEventID { continue } - event := &precompilesgen.ArbRetryableTxRedeemScheduled{} - err := util.ParseRedeemScheduledLog(event, log) + event, err := util.ParseRedeemScheduledLog(log) if err != nil { glog.Error("Failed to parse RedeemScheduled log", "err", err) continue @@ -705,7 +746,7 @@ func (p *TxProcessor) GetPaidGasPrice() *big.Int { version := p.state.ArbOSVersion() if version != 9 { gasPrice = p.evm.Context.BaseFee - if p.msg.TxRunMode != core.MessageCommitMode && p.msg.GasFeeCap.Sign() == 0 { + if !p.msg.TxRunMode.ExecutedOnChain() && p.msg.GasFeeCap.Sign() == 0 { gasPrice = common.Big0 } } diff --git a/arbos/util/tracing.go b/arbos/util/tracing.go index f2fb9ac95e..27070baaba 100644 --- a/arbos/util/tracing.go +++ b/arbos/util/tracing.go @@ -43,7 +43,7 @@ func NewTracingInfo(evm *vm.EVM, from, to common.Address, scenario TracingScenar return &TracingInfo{ Tracer: evm.Config.Tracer, Scenario: scenario, - Contract: vm.NewContract(addressHolder{to}, addressHolder{from}, big.NewInt(0), 0), + Contract: vm.NewContract(addressHolder{to}, addressHolder{from}, uint256.NewInt(0), 0), Depth: evm.Depth(), } } @@ -86,7 +86,7 @@ func (info *TracingInfo) MockCall(input []byte, gas uint64, from, to common.Addr depth := info.Depth - contract := vm.NewContract(addressHolder{to}, addressHolder{from}, amount, gas) + contract := vm.NewContract(addressHolder{to}, addressHolder{from}, uint256.MustFromBig(amount), gas) scope := &vm.ScopeContext{ Memory: TracingMemoryFromBytes(input), diff --git a/arbos/util/transfer.go b/arbos/util/transfer.go index b292413025..894872ae4e 100644 --- a/arbos/util/transfer.go +++ b/arbos/util/transfer.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/log" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/util/arbmath" ) @@ -31,13 +32,18 @@ func TransferBalance( } if from != nil { balance := evm.StateDB.GetBalance(*from) - if arbmath.BigLessThan(balance, amount) { + if arbmath.BigLessThan(balance.ToBig(), amount) { return fmt.Errorf("%w: addr %v have %v want %v", vm.ErrInsufficientBalance, *from, balance, amount) } - evm.StateDB.SubBalance(*from, amount, state.BalanceChangeTransfer) + evm.StateDB.SubBalance(*from, uint256.MustFromBig(amount), state.BalanceChangeTransfer) + if evm.Context.ArbOSVersion >= 30 { + // ensure the from account is "touched" for EIP-161 + evm.StateDB.AddBalance(*from, &uint256.Int{}, state.BalanceChangeTouchAccount) + } + } if to != nil { - evm.StateDB.AddBalance(*to, amount, state.BalanceChangeTransfer) + evm.StateDB.AddBalance(*to, uint256.MustFromBig(amount), state.BalanceChangeTransfer) } tracer := evm.Config.Tracer @@ -69,7 +75,7 @@ func TransferBalance( info := &TracingInfo{ Tracer: evm.Config.Tracer, Scenario: scenario, - Contract: vm.NewContract(addressHolder{*to}, addressHolder{*from}, big.NewInt(0), 0), + Contract: vm.NewContract(addressHolder{*to}, addressHolder{*from}, uint256.NewInt(0), 0), Depth: evm.Depth(), } info.MockCall([]byte{}, 0, *from, *to, amount) diff --git a/arbos/util/util.go b/arbos/util/util.go index 4c0142aeb9..69d90171a0 100644 --- a/arbos/util/util.go +++ b/arbos/util/util.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package util @@ -15,14 +15,15 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" + pgen "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/util/arbmath" ) var AddressAliasOffset *big.Int var InverseAddressAliasOffset *big.Int -var ParseRedeemScheduledLog func(interface{}, *types.Log) error -var ParseL2ToL1TransactionLog func(interface{}, *types.Log) error -var ParseL2ToL1TxLog func(interface{}, *types.Log) error +var ParseRedeemScheduledLog func(*types.Log) (*pgen.ArbRetryableTxRedeemScheduled, error) +var ParseL2ToL1TransactionLog func(*types.Log) (*pgen.ArbSysL2ToL1Transaction, error) +var ParseL2ToL1TxLog func(*types.Log) (*pgen.ArbSysL2ToL1Tx, error) var PackInternalTxDataStartBlock func(...interface{}) ([]byte, error) var UnpackInternalTxDataStartBlock func([]byte) (map[string]interface{}, error) var PackInternalTxDataBatchPostingReport func(...interface{}) ([]byte, error) @@ -37,63 +38,64 @@ func init() { AddressAliasOffset = offset InverseAddressAliasOffset = arbmath.BigSub(new(big.Int).Lsh(big.NewInt(1), 160), AddressAliasOffset) - // Create a mechanism for parsing event logs - logParser := func(source string, name string) func(interface{}, *types.Log) error { - precompile, err := abi.JSON(strings.NewReader(source)) - if err != nil { - panic(fmt.Sprintf("failed to parse ABI for %s: %s", name, err)) - } - inputs := precompile.Events[name].Inputs - indexed := abi.Arguments{} - for _, input := range inputs { - if input.Indexed { - indexed = append(indexed, input) - } - } + ParseRedeemScheduledLog = NewLogParser[pgen.ArbRetryableTxRedeemScheduled](pgen.ArbRetryableTxABI, "RedeemScheduled") + ParseL2ToL1TxLog = NewLogParser[pgen.ArbSysL2ToL1Tx](pgen.ArbSysABI, "L2ToL1Tx") + ParseL2ToL1TransactionLog = NewLogParser[pgen.ArbSysL2ToL1Transaction](pgen.ArbSysABI, "L2ToL1Transaction") + + acts := precompilesgen.ArbosActsABI + PackInternalTxDataStartBlock, UnpackInternalTxDataStartBlock = NewCallParser(acts, "startBlock") + PackInternalTxDataBatchPostingReport, UnpackInternalTxDataBatchPostingReport = NewCallParser(acts, "batchPostingReport") + PackArbRetryableTxRedeem, _ = NewCallParser(precompilesgen.ArbRetryableTxABI, "redeem") +} - return func(event interface{}, log *types.Log) error { - unpacked, err := inputs.Unpack(log.Data) - if err != nil { - return err - } - if err := inputs.Copy(event, unpacked); err != nil { - return err - } - return abi.ParseTopics(event, indexed, log.Topics[1:]) +// Create a mechanism for packing and unpacking calls +func NewCallParser(source string, name string) (func(...interface{}) ([]byte, error), func([]byte) (map[string]interface{}, error)) { + contract, err := abi.JSON(strings.NewReader(source)) + if err != nil { + panic(fmt.Sprintf("failed to parse ABI for %s: %s", name, err)) + } + method, ok := contract.Methods[name] + if !ok { + panic(fmt.Sprintf("method %v does not exist", name)) + } + pack := func(args ...interface{}) ([]byte, error) { + return contract.Pack(name, args...) + } + unpack := func(data []byte) (map[string]interface{}, error) { + if len(data) < 4 { + return nil, errors.New("data not long enough") } + args := make(map[string]interface{}) + return args, method.Inputs.UnpackIntoMap(args, data[4:]) } + return pack, unpack +} - // Create a mechanism for packing and unpacking calls - callParser := func(source string, name string) (func(...interface{}) ([]byte, error), func([]byte) (map[string]interface{}, error)) { - contract, err := abi.JSON(strings.NewReader(source)) - if err != nil { - panic(fmt.Sprintf("failed to parse ABI for %s: %s", name, err)) - } - method, ok := contract.Methods[name] - if !ok { - panic(fmt.Sprintf("method %v does not exist", name)) +// Create a mechanism for parsing event logs +func NewLogParser[T any](source string, name string) func(*types.Log) (*T, error) { + precompile, err := abi.JSON(strings.NewReader(source)) + if err != nil { + panic(fmt.Sprintf("failed to parse ABI for %s: %s", name, err)) + } + inputs := precompile.Events[name].Inputs + indexed := abi.Arguments{} + for _, input := range inputs { + if input.Indexed { + indexed = append(indexed, input) } - pack := func(args ...interface{}) ([]byte, error) { - return contract.Pack(name, args...) + } + return func(log *types.Log) (*T, error) { + unpacked, err := inputs.Unpack(log.Data) + if err != nil { + return nil, err } - unpack := func(data []byte) (map[string]interface{}, error) { - if len(data) < 4 { - return nil, errors.New("data not long enough") - } - args := make(map[string]interface{}) - return args, method.Inputs.UnpackIntoMap(args, data[4:]) + var event T + if err := inputs.Copy(&event, unpacked); err != nil { + return nil, err } - return pack, unpack + err = abi.ParseTopics(&event, indexed, log.Topics[1:]) + return &event, err } - - ParseRedeemScheduledLog = logParser(precompilesgen.ArbRetryableTxABI, "RedeemScheduled") - ParseL2ToL1TxLog = logParser(precompilesgen.ArbSysABI, "L2ToL1Tx") - ParseL2ToL1TransactionLog = logParser(precompilesgen.ArbSysABI, "L2ToL1Transaction") - - acts := precompilesgen.ArbosActsABI - PackInternalTxDataStartBlock, UnpackInternalTxDataStartBlock = callParser(acts, "startBlock") - PackInternalTxDataBatchPostingReport, UnpackInternalTxDataBatchPostingReport = callParser(acts, "batchPostingReport") - PackArbRetryableTxRedeem, _ = callParser(precompilesgen.ArbRetryableTxABI, "redeem") } func AddressToHash(address common.Address) common.Hash { @@ -196,7 +198,7 @@ func UintToHash(val uint64) common.Hash { } func HashPlusInt(x common.Hash, y int64) common.Hash { - return common.BigToHash(new(big.Int).Add(x.Big(), big.NewInt(y))) //BUGBUG: BigToHash(x) converts abs(x) to a Hash + return common.BigToHash(new(big.Int).Add(x.Big(), big.NewInt(y))) // BUGBUG: BigToHash(x) converts abs(x) to a Hash } func RemapL1Address(l1Addr common.Address) common.Address { diff --git a/arbstate/daprovider/reader.go b/arbstate/daprovider/reader.go new file mode 100644 index 0000000000..560af3af1d --- /dev/null +++ b/arbstate/daprovider/reader.go @@ -0,0 +1,104 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package daprovider + +import ( + "context" + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/util/blobs" +) + +type Reader interface { + // IsValidHeaderByte returns true if the given headerByte has bits corresponding to the DA provider + IsValidHeaderByte(headerByte byte) bool + + // RecoverPayloadFromBatch fetches the underlying payload from the DA provider given the batch header information + RecoverPayloadFromBatch( + ctx context.Context, + batchNum uint64, + batchBlockHash common.Hash, + sequencerMsg []byte, + preimageRecorder PreimageRecorder, + validateSeqMsg bool, + ) ([]byte, error) +} + +// NewReaderForDAS is generally meant to be only used by nitro. +// DA Providers should implement methods in the Reader interface independently +func NewReaderForDAS(dasReader DASReader) *readerForDAS { + return &readerForDAS{dasReader: dasReader} +} + +type readerForDAS struct { + dasReader DASReader +} + +func (d *readerForDAS) IsValidHeaderByte(headerByte byte) bool { + return IsDASMessageHeaderByte(headerByte) +} + +func (d *readerForDAS) RecoverPayloadFromBatch( + ctx context.Context, + batchNum uint64, + batchBlockHash common.Hash, + sequencerMsg []byte, + preimageRecorder PreimageRecorder, + validateSeqMsg bool, +) ([]byte, error) { + return RecoverPayloadFromDasBatch(ctx, batchNum, sequencerMsg, d.dasReader, preimageRecorder, validateSeqMsg) +} + +// NewReaderForBlobReader is generally meant to be only used by nitro. +// DA Providers should implement methods in the Reader interface independently +func NewReaderForBlobReader(blobReader BlobReader) *readerForBlobReader { + return &readerForBlobReader{blobReader: blobReader} +} + +type readerForBlobReader struct { + blobReader BlobReader +} + +func (b *readerForBlobReader) IsValidHeaderByte(headerByte byte) bool { + return IsBlobHashesHeaderByte(headerByte) +} + +func (b *readerForBlobReader) RecoverPayloadFromBatch( + ctx context.Context, + batchNum uint64, + batchBlockHash common.Hash, + sequencerMsg []byte, + preimageRecorder PreimageRecorder, + validateSeqMsg bool, +) ([]byte, error) { + blobHashes := sequencerMsg[41:] + if len(blobHashes)%len(common.Hash{}) != 0 { + return nil, ErrInvalidBlobDataFormat + } + versionedHashes := make([]common.Hash, len(blobHashes)/len(common.Hash{})) + for i := 0; i*32 < len(blobHashes); i += 1 { + copy(versionedHashes[i][:], blobHashes[i*32:(i+1)*32]) + } + kzgBlobs, err := b.blobReader.GetBlobs(ctx, batchBlockHash, versionedHashes) + if err != nil { + return nil, fmt.Errorf("failed to get blobs: %w", err) + } + if preimageRecorder != nil { + for i, blob := range kzgBlobs { + // Prevent aliasing `blob` when slicing it, as for range loops overwrite the same variable + // Won't be necessary after Go 1.22 with https://go.dev/blog/loopvar-preview + b := blob + preimageRecorder(versionedHashes[i], b[:], arbutil.EthVersionedHashPreimageType) + } + } + payload, err := blobs.DecodeBlobs(kzgBlobs) + if err != nil { + log.Warn("Failed to decode blobs", "batchBlockHash", batchBlockHash, "versionedHashes", versionedHashes, "err", err) + return nil, nil + } + return payload, nil +} diff --git a/arbstate/das_reader.go b/arbstate/daprovider/util.go similarity index 62% rename from arbstate/das_reader.go rename to arbstate/daprovider/util.go index f131a53608..054bde5503 100644 --- a/arbstate/das_reader.go +++ b/arbstate/daprovider/util.go @@ -1,7 +1,7 @@ // Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -package arbstate +package daprovider import ( "bufio" @@ -13,18 +13,53 @@ import ( "io" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/crypto/kzg4844" + "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/arbos/util" + "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/blsSignatures" "github.com/offchainlabs/nitro/das/dastree" ) -type DataAvailabilityReader interface { +type DASReader interface { GetByHash(ctx context.Context, hash common.Hash) ([]byte, error) ExpirationPolicy(ctx context.Context) (ExpirationPolicy, error) } -var ErrHashMismatch = errors.New("result does not match expected hash") +type DASWriter interface { + // Store requests that the message be stored until timeout (UTC time in unix epoch seconds). + Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*DataAvailabilityCertificate, error) + fmt.Stringer +} + +type BlobReader interface { + GetBlobs( + ctx context.Context, + batchBlockHash common.Hash, + versionedHashes []common.Hash, + ) ([]kzg4844.Blob, error) + Initialize(ctx context.Context) error +} + +// PreimageRecorder is used to add (key,value) pair to the map accessed by key = ty of a bigger map, preimages. +// If ty doesn't exist as a key in the preimages map, then it is intialized to map[common.Hash][]byte and then (key,value) pair is added +type PreimageRecorder func(key common.Hash, value []byte, ty arbutil.PreimageType) + +// RecordPreimagesTo takes in preimages map and returns a function that can be used +// In recording (hash,preimage) key value pairs into preimages map, when fetching payload through RecoverPayloadFromBatch +func RecordPreimagesTo(preimages map[arbutil.PreimageType]map[common.Hash][]byte) PreimageRecorder { + if preimages == nil { + return nil + } + return func(key common.Hash, value []byte, ty arbutil.PreimageType) { + if preimages[ty] == nil { + preimages[ty] = make(map[common.Hash][]byte) + } + preimages[ty][key] = value + } +} // DASMessageHeaderFlag indicates that this data is a certificate for the data availability service, // which will retrieve the full batch data. @@ -83,6 +118,114 @@ func IsKnownHeaderByte(b uint8) bool { return b&^KnownHeaderBits == 0 } +const MinLifetimeSecondsForDataAvailabilityCert = 7 * 24 * 60 * 60 // one week +var ( + ErrHashMismatch = errors.New("result does not match expected hash") + ErrBatchToDasFailed = errors.New("unable to batch to DAS") + ErrNoBlobReader = errors.New("blob batch payload was encountered but no BlobReader was configured") + ErrInvalidBlobDataFormat = errors.New("blob batch data is not a list of hashes as expected") + ErrSeqMsgValidation = errors.New("error validating recovered payload from batch") +) + +type KeysetValidationMode uint8 + +const KeysetValidate KeysetValidationMode = 0 +const KeysetPanicIfInvalid KeysetValidationMode = 1 +const KeysetDontValidate KeysetValidationMode = 2 + +func RecoverPayloadFromDasBatch( + ctx context.Context, + batchNum uint64, + sequencerMsg []byte, + dasReader DASReader, + preimageRecorder PreimageRecorder, + validateSeqMsg bool, +) ([]byte, error) { + cert, err := DeserializeDASCertFrom(bytes.NewReader(sequencerMsg[40:])) + if err != nil { + log.Error("Failed to deserialize DAS message", "err", err) + return nil, nil + } + version := cert.Version + + if version >= 2 { + log.Error("Your node software is probably out of date", "certificateVersion", version) + return nil, nil + } + + getByHash := func(ctx context.Context, hash common.Hash) ([]byte, error) { + newHash := hash + if version == 0 { + newHash = dastree.FlatHashToTreeHash(hash) + } + + preimage, err := dasReader.GetByHash(ctx, newHash) + if err != nil && hash != newHash { + log.Debug("error fetching new style hash, trying old", "new", newHash, "old", hash, "err", err) + preimage, err = dasReader.GetByHash(ctx, hash) + } + if err != nil { + return nil, err + } + + switch { + case version == 0 && crypto.Keccak256Hash(preimage) != hash: + fallthrough + case version == 1 && dastree.Hash(preimage) != hash: + log.Error( + "preimage mismatch for hash", + "hash", hash, "err", ErrHashMismatch, "version", version, + ) + return nil, ErrHashMismatch + } + return preimage, nil + } + + keysetPreimage, err := getByHash(ctx, cert.KeysetHash) + if err != nil { + log.Error("Couldn't get keyset", "err", err) + return nil, err + } + if preimageRecorder != nil { + dastree.RecordHash(preimageRecorder, keysetPreimage) + } + + keyset, err := DeserializeKeyset(bytes.NewReader(keysetPreimage), !validateSeqMsg) + if err != nil { + return nil, fmt.Errorf("%w. Couldn't deserialize keyset, err: %w, keyset hash: %x batch num: %d", ErrSeqMsgValidation, err, cert.KeysetHash, batchNum) + } + err = keyset.VerifySignature(cert.SignersMask, cert.SerializeSignableFields(), cert.Sig) + if err != nil { + log.Error("Bad signature on DAS batch", "err", err) + return nil, nil + } + + maxTimestamp := binary.BigEndian.Uint64(sequencerMsg[8:16]) + if cert.Timeout < maxTimestamp+MinLifetimeSecondsForDataAvailabilityCert { + log.Error("Data availability cert expires too soon", "err", "") + return nil, nil + } + + dataHash := cert.DataHash + payload, err := getByHash(ctx, dataHash) + if err != nil { + log.Error("Couldn't fetch DAS batch contents", "err", err) + return nil, err + } + + if preimageRecorder != nil { + if version == 0 { + treeLeaf := dastree.FlatHashToTreeLeaf(dataHash) + preimageRecorder(dataHash, payload, arbutil.Keccak256PreimageType) + preimageRecorder(crypto.Keccak256Hash(treeLeaf), treeLeaf, arbutil.Keccak256PreimageType) + } else { + dastree.RecordHash(preimageRecorder, payload) + } + } + + return payload, nil +} + type DataAvailabilityCertificate struct { KeysetHash [32]byte DataHash [32]byte @@ -167,7 +310,7 @@ func (c *DataAvailabilityCertificate) SerializeSignableFields() []byte { func (c *DataAvailabilityCertificate) RecoverKeyset( ctx context.Context, - da DataAvailabilityReader, + da DASReader, assumeKeysetValid bool, ) (*DataAvailabilityKeyset, error) { keysetBytes, err := da.GetByHash(ctx, c.KeysetHash) @@ -316,3 +459,22 @@ func StringToExpirationPolicy(s string) (ExpirationPolicy, error) { return -1, fmt.Errorf("invalid Expiration Policy: %s", s) } } + +func Serialize(c *DataAvailabilityCertificate) []byte { + + flags := DASMessageHeaderFlag + if c.Version != 0 { + flags |= TreeDASMessageHeaderFlag + } + + buf := make([]byte, 0) + buf = append(buf, flags) + buf = append(buf, c.KeysetHash[:]...) + buf = append(buf, c.SerializeSignableFields()...) + + var intData [8]byte + binary.BigEndian.PutUint64(intData[:], c.SignersMask) + buf = append(buf, intData[:]...) + + return append(buf, blsSignatures.SignatureToBytes(c.Sig)...) +} diff --git a/arbstate/daprovider/writer.go b/arbstate/daprovider/writer.go new file mode 100644 index 0000000000..75b356c4b8 --- /dev/null +++ b/arbstate/daprovider/writer.go @@ -0,0 +1,48 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package daprovider + +import ( + "context" + "errors" + + "github.com/ethereum/go-ethereum/log" +) + +type Writer interface { + // Store posts the batch data to the invoking DA provider + // And returns sequencerMsg which is later used to retrieve the batch data + Store( + ctx context.Context, + message []byte, + timeout uint64, + sig []byte, + disableFallbackStoreDataOnChain bool, + ) ([]byte, error) +} + +// DAProviderWriterForDAS is generally meant to be only used by nitro. +// DA Providers should implement methods in the DAProviderWriter interface independently +func NewWriterForDAS(dasWriter DASWriter) *writerForDAS { + return &writerForDAS{dasWriter: dasWriter} +} + +type writerForDAS struct { + dasWriter DASWriter +} + +func (d *writerForDAS) Store(ctx context.Context, message []byte, timeout uint64, sig []byte, disableFallbackStoreDataOnChain bool) ([]byte, error) { + cert, err := d.dasWriter.Store(ctx, message, timeout, []byte{}) + if errors.Is(err, ErrBatchToDasFailed) { + if disableFallbackStoreDataOnChain { + return nil, errors.New("unable to batch to DAS and fallback storing data on chain is disabled") + } + log.Warn("Falling back to storing data on chain", "err", err) + return message, nil + } else if err != nil { + return nil, err + } else { + return Serialize(cert), nil + } +} diff --git a/arbstate/inbox.go b/arbstate/inbox.go index 3105ee92b1..753ca19cd6 100644 --- a/arbstate/inbox.go +++ b/arbstate/inbox.go @@ -13,8 +13,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rlp" @@ -22,9 +20,7 @@ import ( "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/l1pricing" - "github.com/offchainlabs/nitro/arbutil" - "github.com/offchainlabs/nitro/das/dastree" - "github.com/offchainlabs/nitro/util/blobs" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/zeroheavy" ) @@ -40,15 +36,6 @@ type InboxBackend interface { ReadDelayedInbox(seqNum uint64) (*arbostypes.L1IncomingMessage, error) } -type BlobReader interface { - GetBlobs( - ctx context.Context, - batchBlockHash common.Hash, - versionedHashes []common.Hash, - ) ([]kzg4844.Blob, error) - Initialize(ctx context.Context) error -} - type sequencerMessage struct { minTimestamp uint64 maxTimestamp uint64 @@ -61,14 +48,8 @@ type sequencerMessage struct { const MaxDecompressedLen int = 1024 * 1024 * 16 // 16 MiB const maxZeroheavyDecompressedLen = 101*MaxDecompressedLen/100 + 64 const MaxSegmentsPerSequencerMessage = 100 * 1024 -const MinLifetimeSecondsForDataAvailabilityCert = 7 * 24 * 60 * 60 // one week -var ( - ErrNoBlobReader = errors.New("blob batch payload was encountered but no BlobReader was configured") - ErrInvalidBlobDataFormat = errors.New("blob batch data is not a list of hashes as expected") -) - -func parseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash common.Hash, data []byte, daProviders []DataAvailabilityProvider, keysetValidationMode KeysetValidationMode) (*sequencerMessage, error) { +func parseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash common.Hash, data []byte, dapReaders []daprovider.Reader, keysetValidationMode daprovider.KeysetValidationMode) (*sequencerMessage, error) { if len(data) < 40 { return nil, errors.New("sequencer message missing L1 header") } @@ -86,22 +67,32 @@ func parseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash // If the parent chain sequencer inbox smart contract authenticated this batch, // an unknown header byte must mean that this node is out of date, // because the smart contract understands the header byte and this node doesn't. - if len(payload) > 0 && IsL1AuthenticatedMessageHeaderByte(payload[0]) && !IsKnownHeaderByte(payload[0]) { + if len(payload) > 0 && daprovider.IsL1AuthenticatedMessageHeaderByte(payload[0]) && !daprovider.IsKnownHeaderByte(payload[0]) { return nil, fmt.Errorf("%w: batch has unsupported authenticated header byte 0x%02x", arbosState.ErrFatalNodeOutOfDate, payload[0]) } // Stage 1: Extract the payload from any data availability header. // It's important that multiple DAS strategies can't both be invoked in the same batch, // as these headers are validated by the sequencer inbox and not other DASs. - // We try to extract payload from the first occuring valid DA provider in the daProviders list + // We try to extract payload from the first occuring valid DA reader in the dapReaders list if len(payload) > 0 { foundDA := false var err error - for _, provider := range daProviders { - if provider != nil && provider.IsValidHeaderByte(payload[0]) { - payload, err = provider.RecoverPayloadFromBatch(ctx, batchNum, batchBlockHash, data, nil, keysetValidationMode) + for _, dapReader := range dapReaders { + if dapReader != nil && dapReader.IsValidHeaderByte(payload[0]) { + payload, err = dapReader.RecoverPayloadFromBatch(ctx, batchNum, batchBlockHash, data, nil, keysetValidationMode != daprovider.KeysetDontValidate) if err != nil { - return nil, err + // Matches the way keyset validation was done inside DAS readers i.e logging the error + // But other daproviders might just want to return the error + if errors.Is(err, daprovider.ErrSeqMsgValidation) && daprovider.IsDASMessageHeaderByte(payload[0]) { + logLevel := log.Error + if keysetValidationMode == daprovider.KeysetPanicIfInvalid { + logLevel = log.Crit + } + logLevel(err.Error()) + } else { + return nil, err + } } if payload == nil { return parsedMsg, nil @@ -112,10 +103,10 @@ func parseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash } if !foundDA { - if IsDASMessageHeaderByte(payload[0]) { + if daprovider.IsDASMessageHeaderByte(payload[0]) { log.Error("No DAS Reader configured, but sequencer message found with DAS header") - } else if IsBlobHashesHeaderByte(payload[0]) { - return nil, ErrNoBlobReader + } else if daprovider.IsBlobHashesHeaderByte(payload[0]) { + return nil, daprovider.ErrNoBlobReader } } } @@ -124,7 +115,7 @@ func parseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash // It's not safe to trust any part of the payload from this point onwards. // Stage 2: If enabled, decode the zero heavy payload (saves gas based on calldata charging). - if len(payload) > 0 && IsZeroheavyEncodedHeaderByte(payload[0]) { + if len(payload) > 0 && daprovider.IsZeroheavyEncodedHeaderByte(payload[0]) { pl, err := io.ReadAll(io.LimitReader(zeroheavy.NewZeroheavyDecoder(bytes.NewReader(payload[1:])), int64(maxZeroheavyDecompressedLen))) if err != nil { log.Warn("error reading from zeroheavy decoder", err.Error()) @@ -134,7 +125,7 @@ func parseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash } // Stage 3: Decompress the brotli payload and fill the parsedMsg.segments list. - if len(payload) > 0 && IsBrotliMessageHeaderByte(payload[0]) { + if len(payload) > 0 && daprovider.IsBrotliMessageHeaderByte(payload[0]) { decompressed, err := arbcompress.Decompress(payload[1:], MaxDecompressedLen) if err == nil { reader := bytes.NewReader(decompressed) @@ -170,224 +161,24 @@ func parseSequencerMessage(ctx context.Context, batchNum uint64, batchBlockHash return parsedMsg, nil } -func RecoverPayloadFromDasBatch( - ctx context.Context, - batchNum uint64, - sequencerMsg []byte, - dasReader DataAvailabilityReader, - preimages map[arbutil.PreimageType]map[common.Hash][]byte, - keysetValidationMode KeysetValidationMode, -) ([]byte, error) { - var keccakPreimages map[common.Hash][]byte - if preimages != nil { - if preimages[arbutil.Keccak256PreimageType] == nil { - preimages[arbutil.Keccak256PreimageType] = make(map[common.Hash][]byte) - } - keccakPreimages = preimages[arbutil.Keccak256PreimageType] - } - cert, err := DeserializeDASCertFrom(bytes.NewReader(sequencerMsg[40:])) - if err != nil { - log.Error("Failed to deserialize DAS message", "err", err) - return nil, nil - } - version := cert.Version - recordPreimage := func(key common.Hash, value []byte) { - keccakPreimages[key] = value - } - - if version >= 2 { - log.Error("Your node software is probably out of date", "certificateVersion", version) - return nil, nil - } - - getByHash := func(ctx context.Context, hash common.Hash) ([]byte, error) { - newHash := hash - if version == 0 { - newHash = dastree.FlatHashToTreeHash(hash) - } - - preimage, err := dasReader.GetByHash(ctx, newHash) - if err != nil && hash != newHash { - log.Debug("error fetching new style hash, trying old", "new", newHash, "old", hash, "err", err) - preimage, err = dasReader.GetByHash(ctx, hash) - } - if err != nil { - return nil, err - } - - switch { - case version == 0 && crypto.Keccak256Hash(preimage) != hash: - fallthrough - case version == 1 && dastree.Hash(preimage) != hash: - log.Error( - "preimage mismatch for hash", - "hash", hash, "err", ErrHashMismatch, "version", version, - ) - return nil, ErrHashMismatch - } - return preimage, nil - } - - keysetPreimage, err := getByHash(ctx, cert.KeysetHash) - if err != nil { - log.Error("Couldn't get keyset", "err", err) - return nil, err - } - if keccakPreimages != nil { - dastree.RecordHash(recordPreimage, keysetPreimage) - } - - keyset, err := DeserializeKeyset(bytes.NewReader(keysetPreimage), keysetValidationMode == KeysetDontValidate) - if err != nil { - logLevel := log.Error - if keysetValidationMode == KeysetPanicIfInvalid { - logLevel = log.Crit - } - logLevel("Couldn't deserialize keyset", "err", err, "keysetHash", cert.KeysetHash, "batchNum", batchNum) - return nil, nil - } - err = keyset.VerifySignature(cert.SignersMask, cert.SerializeSignableFields(), cert.Sig) - if err != nil { - log.Error("Bad signature on DAS batch", "err", err) - return nil, nil - } - - maxTimestamp := binary.BigEndian.Uint64(sequencerMsg[8:16]) - if cert.Timeout < maxTimestamp+MinLifetimeSecondsForDataAvailabilityCert { - log.Error("Data availability cert expires too soon", "err", "") - return nil, nil - } - - dataHash := cert.DataHash - payload, err := getByHash(ctx, dataHash) - if err != nil { - log.Error("Couldn't fetch DAS batch contents", "err", err) - return nil, err - } - - if keccakPreimages != nil { - if version == 0 { - treeLeaf := dastree.FlatHashToTreeLeaf(dataHash) - keccakPreimages[dataHash] = payload - keccakPreimages[crypto.Keccak256Hash(treeLeaf)] = treeLeaf - } else { - dastree.RecordHash(recordPreimage, payload) - } - } - - return payload, nil -} - -type DataAvailabilityProvider interface { - // IsValidHeaderByte returns true if the given headerByte has bits corresponding to the DA provider - IsValidHeaderByte(headerByte byte) bool - - // RecoverPayloadFromBatch fetches the underlying payload from the DA provider given the batch header information - RecoverPayloadFromBatch( - ctx context.Context, - batchNum uint64, - batchBlockHash common.Hash, - sequencerMsg []byte, - preimages map[arbutil.PreimageType]map[common.Hash][]byte, - keysetValidationMode KeysetValidationMode, - ) ([]byte, error) -} - -// NewDAProviderDAS is generally meant to be only used by nitro. -// DA Providers should implement methods in the DataAvailabilityProvider interface independently -func NewDAProviderDAS(das DataAvailabilityReader) *dAProviderForDAS { - return &dAProviderForDAS{ - das: das, - } -} - -type dAProviderForDAS struct { - das DataAvailabilityReader -} - -func (d *dAProviderForDAS) IsValidHeaderByte(headerByte byte) bool { - return IsDASMessageHeaderByte(headerByte) -} - -func (d *dAProviderForDAS) RecoverPayloadFromBatch( - ctx context.Context, - batchNum uint64, - batchBlockHash common.Hash, - sequencerMsg []byte, - preimages map[arbutil.PreimageType]map[common.Hash][]byte, - keysetValidationMode KeysetValidationMode, -) ([]byte, error) { - return RecoverPayloadFromDasBatch(ctx, batchNum, sequencerMsg, d.das, preimages, keysetValidationMode) -} - -// NewDAProviderBlobReader is generally meant to be only used by nitro. -// DA Providers should implement methods in the DataAvailabilityProvider interface independently -func NewDAProviderBlobReader(blobReader BlobReader) *dAProviderForBlobReader { - return &dAProviderForBlobReader{ - blobReader: blobReader, - } -} - -type dAProviderForBlobReader struct { - blobReader BlobReader -} - -func (b *dAProviderForBlobReader) IsValidHeaderByte(headerByte byte) bool { - return IsBlobHashesHeaderByte(headerByte) -} - -func (b *dAProviderForBlobReader) RecoverPayloadFromBatch( - ctx context.Context, - batchNum uint64, - batchBlockHash common.Hash, - sequencerMsg []byte, - preimages map[arbutil.PreimageType]map[common.Hash][]byte, - keysetValidationMode KeysetValidationMode, -) ([]byte, error) { - blobHashes := sequencerMsg[41:] - if len(blobHashes)%len(common.Hash{}) != 0 { - return nil, ErrInvalidBlobDataFormat - } - versionedHashes := make([]common.Hash, len(blobHashes)/len(common.Hash{})) - for i := 0; i*32 < len(blobHashes); i += 1 { - copy(versionedHashes[i][:], blobHashes[i*32:(i+1)*32]) - } - kzgBlobs, err := b.blobReader.GetBlobs(ctx, batchBlockHash, versionedHashes) - if err != nil { - return nil, fmt.Errorf("failed to get blobs: %w", err) - } - payload, err := blobs.DecodeBlobs(kzgBlobs) - if err != nil { - log.Warn("Failed to decode blobs", "batchBlockHash", batchBlockHash, "versionedHashes", versionedHashes, "err", err) - return nil, nil - } - return payload, nil -} - -type KeysetValidationMode uint8 - -const KeysetValidate KeysetValidationMode = 0 -const KeysetPanicIfInvalid KeysetValidationMode = 1 -const KeysetDontValidate KeysetValidationMode = 2 - type inboxMultiplexer struct { backend InboxBackend delayedMessagesRead uint64 - daProviders []DataAvailabilityProvider + dapReaders []daprovider.Reader cachedSequencerMessage *sequencerMessage cachedSequencerMessageNum uint64 cachedSegmentNum uint64 cachedSegmentTimestamp uint64 cachedSegmentBlockNumber uint64 cachedSubMessageNumber uint64 - keysetValidationMode KeysetValidationMode + keysetValidationMode daprovider.KeysetValidationMode } -func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, daProviders []DataAvailabilityProvider, keysetValidationMode KeysetValidationMode) arbostypes.InboxMultiplexer { +func NewInboxMultiplexer(backend InboxBackend, delayedMessagesRead uint64, dapReaders []daprovider.Reader, keysetValidationMode daprovider.KeysetValidationMode) arbostypes.InboxMultiplexer { return &inboxMultiplexer{ backend: backend, delayedMessagesRead: delayedMessagesRead, - daProviders: daProviders, + dapReaders: dapReaders, keysetValidationMode: keysetValidationMode, } } @@ -409,7 +200,7 @@ func (r *inboxMultiplexer) Pop(ctx context.Context) (*arbostypes.MessageWithMeta } r.cachedSequencerMessageNum = r.backend.GetSequencerInboxPosition() var err error - r.cachedSequencerMessage, err = parseSequencerMessage(ctx, r.cachedSequencerMessageNum, batchBlockHash, bytes, r.daProviders, r.keysetValidationMode) + r.cachedSequencerMessage, err = parseSequencerMessage(ctx, r.cachedSequencerMessageNum, batchBlockHash, bytes, r.dapReaders, r.keysetValidationMode) if err != nil { return nil, err } diff --git a/arbstate/inbox_fuzz_test.go b/arbstate/inbox_fuzz_test.go index b34c02534b..5ede321810 100644 --- a/arbstate/inbox_fuzz_test.go +++ b/arbstate/inbox_fuzz_test.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/offchainlabs/nitro/arbos/arbostypes" + "github.com/offchainlabs/nitro/arbstate/daprovider" ) type multiplexerBackend struct { @@ -67,7 +68,7 @@ func FuzzInboxMultiplexer(f *testing.F) { delayedMessage: delayedMsg, positionWithinMessage: 0, } - multiplexer := NewInboxMultiplexer(backend, 0, nil, KeysetValidate) + multiplexer := NewInboxMultiplexer(backend, 0, nil, daprovider.KeysetValidate) _, err := multiplexer.Pop(context.TODO()) if err != nil { panic(err) diff --git a/arbutil/correspondingl1blocknumber.go b/arbutil/correspondingl1blocknumber.go index 136eb8e4c9..05323ed183 100644 --- a/arbutil/correspondingl1blocknumber.go +++ b/arbutil/correspondingl1blocknumber.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package arbutil diff --git a/arbutil/format.go b/arbutil/format.go new file mode 100644 index 0000000000..041866e4cb --- /dev/null +++ b/arbutil/format.go @@ -0,0 +1,19 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package arbutil + +import ( + "encoding/hex" + "unicode/utf8" +) + +func ToStringOrHex(input []byte) string { + if input == nil { + return "" + } + if utf8.Valid(input) { + return string(input) + } + return hex.EncodeToString(input) +} diff --git a/arbutil/unsafe.go b/arbutil/unsafe.go new file mode 100644 index 0000000000..341aa12c18 --- /dev/null +++ b/arbutil/unsafe.go @@ -0,0 +1,28 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package arbutil + +import "unsafe" + +func SliceToPointer[T any](slice []T) *T { + if len(slice) == 0 { + return nil + } + return &slice[0] +} + +func SliceToUnsafePointer[T any](slice []T) unsafe.Pointer { + return unsafe.Pointer(SliceToPointer(slice)) +} + +// does a defensive copy due to Go's lake of immutable types +func PointerToSlice[T any](pointer *T, length int) []T { + return CopySlice(unsafe.Slice(pointer, length)) +} + +func CopySlice[T any](slice []T) []T { + output := make([]T, len(slice)) + copy(output, slice) + return output +} diff --git a/arbutil/wait_for_l1.go b/arbutil/wait_for_l1.go index cfe24cf636..eaa5d0790d 100644 --- a/arbutil/wait_for_l1.go +++ b/arbutil/wait_for_l1.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package arbutil @@ -41,7 +41,6 @@ func SendTxAsCall(ctx context.Context, client L1Interface, tx *types.Transaction From: from, To: tx.To(), Gas: gas, - GasPrice: tx.GasPrice(), GasFeeCap: tx.GasFeeCap(), GasTipCap: tx.GasTipCap(), Value: tx.Value(), diff --git a/blocks_reexecutor/blocks_reexecutor.go b/blocks_reexecutor/blocks_reexecutor.go index bb6de00cad..1e4a06fe90 100644 --- a/blocks_reexecutor/blocks_reexecutor.go +++ b/blocks_reexecutor/blocks_reexecutor.go @@ -35,17 +35,16 @@ func (c *Config) Validate() error { if c.EndBlock < c.StartBlock { return errors.New("invalid block range for blocks re-execution") } - if c.Room == 0 { - return errors.New("room for blocks re-execution cannot be zero") + if c.Room <= 0 { + return errors.New("room for blocks re-execution should be greater than 0") } return nil } var DefaultConfig = Config{ - Enable: false, - Mode: "random", - Room: runtime.NumCPU(), - BlocksPerThread: 10000, + Enable: false, + Mode: "random", + Room: runtime.NumCPU(), } var TestConfig = Config{ @@ -66,13 +65,14 @@ func ConfigAddOptions(prefix string, f *flag.FlagSet) { type BlocksReExecutor struct { stopwaiter.StopWaiter - config *Config - blockchain *core.BlockChain - stateFor arbitrum.StateForHeaderFunction - done chan struct{} - fatalErrChan chan error - startBlock uint64 - currentBlock uint64 + config *Config + blockchain *core.BlockChain + stateFor arbitrum.StateForHeaderFunction + done chan struct{} + fatalErrChan chan error + startBlock uint64 + currentBlock uint64 + blocksPerThread uint64 } func New(c *Config, blockchain *core.BlockChain, fatalErrChan chan error) *BlocksReExecutor { @@ -84,32 +84,47 @@ func New(c *Config, blockchain *core.BlockChain, fatalErrChan chan error) *Block start = chainStart end = chainEnd } - if start < chainStart { - log.Warn("state reexecutor's start block number is lower than genesis, resetting to genesis") + if start < chainStart || start > chainEnd { + log.Warn("invalid state reexecutor's start block number, resetting to genesis", "start", start, "genesis", chainStart) start = chainStart } - if end > chainEnd { - log.Warn("state reexecutor's end block number is greater than latest, resetting to latest") + if end > chainEnd || end < chainStart { + log.Warn("invalid state reexecutor's end block number, resetting to latest", "end", end, "latest", chainEnd) end = chainEnd } + blocksPerThread := uint64(10000) + if c.BlocksPerThread != 0 { + blocksPerThread = c.BlocksPerThread + } if c.Mode == "random" && end != start { - if c.BlocksPerThread > end-start { - c.BlocksPerThread = end - start + // Reexecute a range of 10000 or (non-zero) c.BlocksPerThread number of blocks between start to end picked randomly + rng := blocksPerThread + if rng > end-start { + rng = end - start } - start += uint64(rand.Intn(int(end - start - c.BlocksPerThread + 1))) - end = start + c.BlocksPerThread + start += uint64(rand.Intn(int(end - start - rng + 1))) + end = start + rng } - // inclusive of block reexecution [start, end] - if start > 0 { + // Inclusive of block reexecution [start, end] + // Do not reexecute genesis block i,e chainStart + if start > 0 && start != chainStart { start-- } + // Divide work equally among available threads when BlocksPerThread is zero + if c.BlocksPerThread == 0 { + work := (end - start) / uint64(c.Room) + if work > 0 { + blocksPerThread = work + } + } return &BlocksReExecutor{ - config: c, - blockchain: blockchain, - currentBlock: end, - startBlock: start, - done: make(chan struct{}, c.Room), - fatalErrChan: fatalErrChan, + config: c, + blockchain: blockchain, + currentBlock: end, + startBlock: start, + blocksPerThread: blocksPerThread, + done: make(chan struct{}, c.Room), + fatalErrChan: fatalErrChan, stateFor: func(header *types.Header) (*state.StateDB, arbitrum.StateReleaseFunc, error) { state, err := blockchain.StateAt(header.Root) return state, arbitrum.NoopStateRelease, err @@ -119,17 +134,17 @@ func New(c *Config, blockchain *core.BlockChain, fatalErrChan chan error) *Block // LaunchBlocksReExecution launches the thread to apply blocks of range [currentBlock-s.config.BlocksPerThread, currentBlock] to the last available valid state func (s *BlocksReExecutor) LaunchBlocksReExecution(ctx context.Context, currentBlock uint64) uint64 { - start := arbmath.SaturatingUSub(currentBlock, s.config.BlocksPerThread) + start := arbmath.SaturatingUSub(currentBlock, s.blocksPerThread) if start < s.startBlock { start = s.startBlock } - // we don't use state release pattern here - // TODO do we want to use release pattern here? - startState, startHeader, _, err := arbitrum.FindLastAvailableState(ctx, s.blockchain, s.stateFor, s.blockchain.GetHeaderByNumber(start), nil, -1) + startState, startHeader, release, err := arbitrum.FindLastAvailableState(ctx, s.blockchain, s.stateFor, s.blockchain.GetHeaderByNumber(start), nil, -1) if err != nil { s.fatalErrChan <- fmt.Errorf("blocksReExecutor failed to get last available state while searching for state at %d, err: %w", start, err) return s.startBlock } + // NoOp + defer release() start = startHeader.Number.Uint64() s.LaunchThread(func(ctx context.Context) { _, err := arbitrum.AdvanceStateUpToBlock(ctx, s.blockchain, startState, s.blockchain.GetHeaderByNumber(currentBlock), startHeader, nil) @@ -169,9 +184,14 @@ func (s *BlocksReExecutor) Impl(ctx context.Context) { log.Info("BlocksReExecutor successfully completed re-execution of blocks against historic state", "stateAt", s.startBlock, "startBlock", s.startBlock+1, "endBlock", end) } -func (s *BlocksReExecutor) Start(ctx context.Context) { +func (s *BlocksReExecutor) Start(ctx context.Context, done chan struct{}) { s.StopWaiter.Start(ctx, s) - s.LaunchThread(s.Impl) + s.LaunchThread(func(ctx context.Context) { + s.Impl(ctx) + if done != nil { + close(done) + } + }) } func (s *BlocksReExecutor) StopAndWait() { diff --git a/broadcastclient/broadcastclient_test.go b/broadcastclient/broadcastclient_test.go index 84356d77e0..44b48192ab 100644 --- a/broadcastclient/broadcastclient_test.go +++ b/broadcastclient/broadcastclient_test.go @@ -105,7 +105,7 @@ func testReceiveMessages(t *testing.T, clientCompression bool, serverCompression go func() { for i := 0; i < messageCount; i++ { - Require(t, b.BroadcastSingle(arbostypes.TestMessageWithMetadataAndRequestId, arbutil.MessageIndex(i))) + Require(t, b.BroadcastSingle(arbostypes.TestMessageWithMetadataAndRequestId, arbutil.MessageIndex(i), nil)) } }() @@ -156,7 +156,7 @@ func TestInvalidSignature(t *testing.T) { go func() { for i := 0; i < messageCount; i++ { - Require(t, b.BroadcastSingle(arbostypes.TestMessageWithMetadataAndRequestId, arbutil.MessageIndex(i))) + Require(t, b.BroadcastSingle(arbostypes.TestMessageWithMetadataAndRequestId, arbutil.MessageIndex(i), nil)) } }() @@ -316,7 +316,7 @@ func TestServerClientDisconnect(t *testing.T) { broadcastClient.Start(ctx) t.Log("broadcasting seq 0 message") - Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 0)) + Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 0, nil)) // Wait for client to receive batch to ensure it is connected timer := time.NewTimer(5 * time.Second) @@ -387,7 +387,7 @@ func TestBroadcastClientConfirmedMessage(t *testing.T) { broadcastClient.Start(ctx) t.Log("broadcasting seq 0 message") - Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 0)) + Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 0, nil)) // Wait for client to receive batch to ensure it is connected timer := time.NewTimer(5 * time.Second) @@ -724,8 +724,8 @@ func TestBroadcasterSendsCachedMessagesOnClientConnect(t *testing.T) { Require(t, b.Start(ctx)) defer b.StopAndWait() - Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 0)) - Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 1)) + Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 0, nil)) + Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 1, nil)) var wg sync.WaitGroup for i := 0; i < 2; i++ { diff --git a/broadcaster/broadcaster.go b/broadcaster/broadcaster.go index 242b8f9eeb..ba95f2d8af 100644 --- a/broadcaster/broadcaster.go +++ b/broadcaster/broadcaster.go @@ -11,6 +11,7 @@ import ( "github.com/gobwas/ws" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/arbos/arbostypes" @@ -38,7 +39,11 @@ func NewBroadcaster(config wsbroadcastserver.BroadcasterConfigFetcher, chainId u } } -func (b *Broadcaster) NewBroadcastFeedMessage(message arbostypes.MessageWithMetadata, sequenceNumber arbutil.MessageIndex) (*m.BroadcastFeedMessage, error) { +func (b *Broadcaster) NewBroadcastFeedMessage( + message arbostypes.MessageWithMetadata, + sequenceNumber arbutil.MessageIndex, + blockHash *common.Hash, +) (*m.BroadcastFeedMessage, error) { var messageSignature []byte if b.dataSigner != nil { hash, err := message.Hash(sequenceNumber, b.chainId) @@ -54,18 +59,23 @@ func (b *Broadcaster) NewBroadcastFeedMessage(message arbostypes.MessageWithMeta return &m.BroadcastFeedMessage{ SequenceNumber: sequenceNumber, Message: message, + BlockHash: blockHash, Signature: messageSignature, }, nil } -func (b *Broadcaster) BroadcastSingle(msg arbostypes.MessageWithMetadata, seq arbutil.MessageIndex) (err error) { +func (b *Broadcaster) BroadcastSingle( + msg arbostypes.MessageWithMetadata, + seq arbutil.MessageIndex, + blockHash *common.Hash, +) (err error) { defer func() { if r := recover(); r != nil { log.Error("recovered error in BroadcastSingle", "recover", r, "backtrace", string(debug.Stack())) err = errors.New("panic in BroadcastSingle") } }() - bfm, err := b.NewBroadcastFeedMessage(msg, seq) + bfm, err := b.NewBroadcastFeedMessage(msg, seq, blockHash) if err != nil { return err } @@ -82,7 +92,10 @@ func (b *Broadcaster) BroadcastSingleFeedMessage(bfm *m.BroadcastFeedMessage) { b.BroadcastFeedMessages(broadcastFeedMessages) } -func (b *Broadcaster) BroadcastMessages(messages []arbostypes.MessageWithMetadata, seq arbutil.MessageIndex) (err error) { +func (b *Broadcaster) BroadcastMessages( + messagesWithBlockHash []arbostypes.MessageWithMetadataAndBlockHash, + seq arbutil.MessageIndex, +) (err error) { defer func() { if r := recover(); r != nil { log.Error("recovered error in BroadcastMessages", "recover", r, "backtrace", string(debug.Stack())) @@ -90,8 +103,8 @@ func (b *Broadcaster) BroadcastMessages(messages []arbostypes.MessageWithMetadat } }() var feedMessages []*m.BroadcastFeedMessage - for i, msg := range messages { - bfm, err := b.NewBroadcastFeedMessage(msg, seq+arbutil.MessageIndex(i)) + for i, msg := range messagesWithBlockHash { + bfm, err := b.NewBroadcastFeedMessage(msg.MessageWithMeta, seq+arbutil.MessageIndex(i), msg.BlockHash) if err != nil { return err } diff --git a/broadcaster/broadcaster_test.go b/broadcaster/broadcaster_test.go index 8ac06e9705..dc208f4163 100644 --- a/broadcaster/broadcaster_test.go +++ b/broadcaster/broadcaster_test.go @@ -70,17 +70,17 @@ func TestBroadcasterMessagesRemovedOnConfirmation(t *testing.T) { } // Normal broadcasting and confirming - Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 1)) + Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 1, nil)) waitUntilUpdated(t, expectMessageCount(1, "after 1 message")) - Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 2)) + Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 2, nil)) waitUntilUpdated(t, expectMessageCount(2, "after 2 messages")) - Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 3)) + Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 3, nil)) waitUntilUpdated(t, expectMessageCount(3, "after 3 messages")) - Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 4)) + Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 4, nil)) waitUntilUpdated(t, expectMessageCount(4, "after 4 messages")) - Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 5)) + Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 5, nil)) waitUntilUpdated(t, expectMessageCount(5, "after 4 messages")) - Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 6)) + Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 6, nil)) waitUntilUpdated(t, expectMessageCount(6, "after 4 messages")) b.Confirm(4) @@ -96,7 +96,7 @@ func TestBroadcasterMessagesRemovedOnConfirmation(t *testing.T) { "nothing changed because confirmed sequence number before cache")) b.Confirm(5) - Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 7)) + Require(t, b.BroadcastSingle(arbostypes.EmptyTestMessageWithMetadata, 7, nil)) waitUntilUpdated(t, expectMessageCount(2, "after 7 messages, 5 cleared by confirm")) diff --git a/broadcaster/message/message.go b/broadcaster/message/message.go index a575ae5cd0..aca9598754 100644 --- a/broadcaster/message/message.go +++ b/broadcaster/message/message.go @@ -34,6 +34,7 @@ type BroadcastMessage struct { type BroadcastFeedMessage struct { SequenceNumber arbutil.MessageIndex `json:"sequenceNumber"` Message arbostypes.MessageWithMetadata `json:"message"` + BlockHash *common.Hash `json:"blockHash,omitempty"` Signature []byte `json:"signature"` CumulativeSumMsgSize uint64 `json:"-"` diff --git a/broadcaster/message/message_serialization_test.go b/broadcaster/message/message_serialization_test.go index c3e14a86ae..1d8c10e388 100644 --- a/broadcaster/message/message_serialization_test.go +++ b/broadcaster/message/message_serialization_test.go @@ -13,7 +13,40 @@ import ( "github.com/offchainlabs/nitro/arbos/arbostypes" ) -func ExampleBroadcastMessage_broadcastfeedmessage() { +func ExampleBroadcastMessage_broadcastfeedmessageWithBlockHash() { + var requestId common.Hash + msg := BroadcastMessage{ + Version: 1, + Messages: []*BroadcastFeedMessage{ + { + SequenceNumber: 12345, + Message: arbostypes.MessageWithMetadata{ + Message: &arbostypes.L1IncomingMessage{ + Header: &arbostypes.L1IncomingMessageHeader{ + Kind: 0, + Poster: [20]byte{}, + BlockNumber: 0, + Timestamp: 0, + RequestId: &requestId, + L1BaseFee: big.NewInt(0), + }, + L2msg: []byte{0xde, 0xad, 0xbe, 0xef}, + }, + DelayedMessagesRead: 3333, + }, + BlockHash: &common.Hash{0: 0xff}, + Signature: nil, + }, + }, + } + var buf bytes.Buffer + encoder := json.NewEncoder(&buf) + _ = encoder.Encode(msg) + fmt.Println(buf.String()) + // Output: {"version":1,"messages":[{"sequenceNumber":12345,"message":{"message":{"header":{"kind":0,"sender":"0x0000000000000000000000000000000000000000","blockNumber":0,"timestamp":0,"requestId":"0x0000000000000000000000000000000000000000000000000000000000000000","baseFeeL1":0},"l2Msg":"3q2+7w=="},"delayedMessagesRead":3333},"blockHash":"0xff00000000000000000000000000000000000000000000000000000000000000","signature":null}]} +} + +func ExampleBroadcastMessage_broadcastfeedmessageWithoutBlockHash() { var requestId common.Hash msg := BroadcastMessage{ Version: 1, diff --git a/cmd/chaininfo/arbitrum_chain_info.json b/cmd/chaininfo/arbitrum_chain_info.json index 31d25cfdf5..7d47d13e84 100644 --- a/cmd/chaininfo/arbitrum_chain_info.json +++ b/cmd/chaininfo/arbitrum_chain_info.json @@ -247,5 +247,54 @@ "validator-wallet-creator": "0x894fC71fA0A666352824EC954B401573C861D664", "deployed-at": 4139226 } + }, + { + "chain-id": 23011913, + "parent-chain-id": 421614, + "chain-name": "stylus-testnet", + "sequencer-url": "https://stylus-testnet-sequencer.arbitrum.io/rpc", + "feed-url": "wss://stylus-testnet.arbitrum.io/feed", + "chain-config": + { + "chainId": 23011913, + "homesteadBlock": 0, + "daoForkBlock": null, + "daoForkSupport": true, + "eip150Block": 0, + "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "clique": + { + "period": 0, + "epoch": 0 + }, + "arbitrum": + { + "EnableArbOS": true, + "AllowDebugPrecompiles": false, + "DataAvailabilityCommittee": false, + "InitialArbOSVersion": 10, + "InitialChainOwner": "0x35c8d15334Eaf0e4b82417Fe10e28deEa0c5C32B", + "GenesisBlockNum": 0 + } + }, + "rollup": + { + "bridge": "0x35aa95ac4747D928E2Cd42FE4461F6D9d1826346", + "inbox": "0xe1e3b1CBaCC870cb6e5F4Bdf246feB6eB5cD351B", + "sequencer-inbox": "0x00A0F15b79d1D3e5991929FaAbCF2AA65623530c", + "rollup": "0x94db9E36d9336cD6F9FfcAd399dDa6Cc05299898", + "validator-utils": "0x8aB661AAC7693F60DF34464B6f964d3C3977e2D3", + "validator-wallet-creator": "0x6065949AC7D6e86Ce9EAC2089C6b68B0b7077ED6", + "deployed-at": 1847 + } } ] \ No newline at end of file diff --git a/cmd/conf/chain.go b/cmd/conf/chain.go index 531945b4d6..ab9a713287 100644 --- a/cmd/conf/chain.go +++ b/cmd/conf/chain.go @@ -20,11 +20,12 @@ type ParentChainConfig struct { } var L1ConnectionConfigDefault = rpcclient.ClientConfig{ - URL: "", - Retries: 2, - Timeout: time.Minute, - ConnectionWait: time.Minute, - ArgLogLimit: 2048, + URL: "", + Retries: 2, + Timeout: time.Minute, + ConnectionWait: time.Minute, + ArgLogLimit: 2048, + WebsocketMessageSizeLimit: 256 * 1024 * 1024, } var L1ConfigDefault = ParentChainConfig{ diff --git a/cmd/conf/database.go b/cmd/conf/database.go index b049375d66..6fde00579f 100644 --- a/cmd/conf/database.go +++ b/cmd/conf/database.go @@ -5,20 +5,25 @@ package conf import ( "fmt" + "math" "os" "path" "path/filepath" + "runtime" + "time" + "github.com/ethereum/go-ethereum/ethdb/pebble" flag "github.com/spf13/pflag" ) type PersistentConfig struct { - GlobalConfig string `koanf:"global-config"` - Chain string `koanf:"chain"` - LogDir string `koanf:"log-dir"` - Handles int `koanf:"handles"` - Ancient string `koanf:"ancient"` - DBEngine string `koanf:"db-engine"` + GlobalConfig string `koanf:"global-config"` + Chain string `koanf:"chain"` + LogDir string `koanf:"log-dir"` + Handles int `koanf:"handles"` + Ancient string `koanf:"ancient"` + DBEngine string `koanf:"db-engine"` + Pebble PebbleConfig `koanf:"pebble"` } var PersistentConfigDefault = PersistentConfig{ @@ -28,6 +33,7 @@ var PersistentConfigDefault = PersistentConfig{ Handles: 512, Ancient: "", DBEngine: "leveldb", + Pebble: PebbleConfigDefault, } func PersistentConfigAddOptions(prefix string, f *flag.FlagSet) { @@ -37,6 +43,7 @@ func PersistentConfigAddOptions(prefix string, f *flag.FlagSet) { f.Int(prefix+".handles", PersistentConfigDefault.Handles, "number of file descriptor handles to use for the database") f.String(prefix+".ancient", PersistentConfigDefault.Ancient, "directory of ancient where the chain freezer can be opened") f.String(prefix+".db-engine", PersistentConfigDefault.DBEngine, "backing database implementation to use ('leveldb' or 'pebble')") + PebbleConfigAddOptions(prefix+".pebble", f) } func (c *PersistentConfig) ResolveDirectoryNames() error { @@ -94,5 +101,174 @@ func (c *PersistentConfig) Validate() error { if c.DBEngine != "leveldb" && c.DBEngine != "pebble" { return fmt.Errorf(`invalid .db-engine choice: %q, allowed "leveldb" or "pebble"`, c.DBEngine) } + if c.DBEngine == "pebble" { + if err := c.Pebble.Validate(); err != nil { + return err + } + } return nil } + +type PebbleConfig struct { + MaxConcurrentCompactions int `koanf:"max-concurrent-compactions"` + Experimental PebbleExperimentalConfig `koanf:"experimental"` +} + +var PebbleConfigDefault = PebbleConfig{ + MaxConcurrentCompactions: runtime.NumCPU(), + Experimental: PebbleExperimentalConfigDefault, +} + +func PebbleConfigAddOptions(prefix string, f *flag.FlagSet) { + f.Int(prefix+".max-concurrent-compactions", PebbleConfigDefault.MaxConcurrentCompactions, "maximum number of concurrent compactions") + PebbleExperimentalConfigAddOptions(prefix+".experimental", f) +} + +func (c *PebbleConfig) Validate() error { + if c.MaxConcurrentCompactions < 1 { + return fmt.Errorf("invalid .max-concurrent-compactions value: %d, has to be greater then 0", c.MaxConcurrentCompactions) + } + if err := c.Experimental.Validate(); err != nil { + return err + } + return nil +} + +type PebbleExperimentalConfig struct { + BytesPerSync int `koanf:"bytes-per-sync"` + L0CompactionFileThreshold int `koanf:"l0-compaction-file-threshold"` + L0CompactionThreshold int `koanf:"l0-compaction-threshold"` + L0StopWritesThreshold int `koanf:"l0-stop-writes-threshold"` + LBaseMaxBytes int64 `koanf:"l-base-max-bytes"` + MemTableStopWritesThreshold int `koanf:"mem-table-stop-writes-threshold"` + DisableAutomaticCompactions bool `koanf:"disable-automatic-compactions"` + WALBytesPerSync int `koanf:"wal-bytes-per-sync"` + WALDir string `koanf:"wal-dir"` + WALMinSyncInterval int `koanf:"wal-min-sync-interval"` + TargetByteDeletionRate int `koanf:"target-byte-deletion-rate"` + + // level specific + BlockSize int `koanf:"block-size"` + IndexBlockSize int `koanf:"index-block-size"` + TargetFileSize int64 `koanf:"target-file-size"` + TargetFileSizeEqualLevels bool `koanf:"target-file-size-equal-levels"` + + // pebble experimental + L0CompactionConcurrency int `koanf:"l0-compaction-concurrency"` + CompactionDebtConcurrency uint64 `koanf:"compaction-debt-concurrency"` + ReadCompactionRate int64 `koanf:"read-compaction-rate"` + ReadSamplingMultiplier int64 `koanf:"read-sampling-multiplier"` + MaxWriterConcurrency int `koanf:"max-writer-concurrency"` + ForceWriterParallelism bool `koanf:"force-writer-parallelism"` +} + +var PebbleExperimentalConfigDefault = PebbleExperimentalConfig{ + BytesPerSync: 512 << 10, // 512 KB + L0CompactionFileThreshold: 500, + L0CompactionThreshold: 4, + L0StopWritesThreshold: 12, + LBaseMaxBytes: 64 << 20, // 64 MB + MemTableStopWritesThreshold: 2, + DisableAutomaticCompactions: false, + WALBytesPerSync: 0, // no background syncing + WALDir: "", // use same dir as for sstables + WALMinSyncInterval: 0, // no artificial delay + TargetByteDeletionRate: 0, // deletion pacing disabled + + BlockSize: 4 << 10, // 4 KB + IndexBlockSize: 4 << 10, // 4 KB + TargetFileSize: 2 << 20, // 2 MB + TargetFileSizeEqualLevels: true, + + L0CompactionConcurrency: 10, + CompactionDebtConcurrency: 1 << 30, // 1GB + ReadCompactionRate: 16000, // see ReadSamplingMultiplier comment + ReadSamplingMultiplier: -1, // geth default, disables read sampling and disables read triggered compaction + MaxWriterConcurrency: 0, + ForceWriterParallelism: false, +} + +func PebbleExperimentalConfigAddOptions(prefix string, f *flag.FlagSet) { + f.Int(prefix+".bytes-per-sync", PebbleExperimentalConfigDefault.BytesPerSync, "number of bytes to write to a SSTable before calling Sync on it in the background") + f.Int(prefix+".l0-compaction-file-threshold", PebbleExperimentalConfigDefault.L0CompactionFileThreshold, "count of L0 files necessary to trigger an L0 compaction") + f.Int(prefix+".l0-compaction-threshold", PebbleExperimentalConfigDefault.L0CompactionThreshold, "amount of L0 read-amplification necessary to trigger an L0 compaction") + f.Int(prefix+".l0-stop-writes-threshold", PebbleExperimentalConfigDefault.L0StopWritesThreshold, "hard limit on L0 read-amplification, computed as the number of L0 sublevels. Writes are stopped when this threshold is reached") + f.Int64(prefix+".l-base-max-bytes", PebbleExperimentalConfigDefault.LBaseMaxBytes, "The maximum number of bytes for LBase. The base level is the level which L0 is compacted into. The base level is determined dynamically based on the existing data in the LSM. The maximum number of bytes for other levels is computed dynamically based on the base level's maximum size. When the maximum number of bytes for a level is exceeded, compaction is requested.") + f.Int(prefix+".mem-table-stop-writes-threshold", PebbleExperimentalConfigDefault.MemTableStopWritesThreshold, "hard limit on the number of queued of MemTables") + f.Bool(prefix+".disable-automatic-compactions", PebbleExperimentalConfigDefault.DisableAutomaticCompactions, "disables automatic compactions") + f.Int(prefix+".wal-bytes-per-sync", PebbleExperimentalConfigDefault.WALBytesPerSync, "number of bytes to write to a write-ahead log (WAL) before calling Sync on it in the background") + f.String(prefix+".wal-dir", PebbleExperimentalConfigDefault.WALDir, "absolute path of directory to store write-ahead logs (WALs) in. If empty, WALs will be stored in the same directory as sstables") + f.Int(prefix+".wal-min-sync-interval", PebbleExperimentalConfigDefault.WALMinSyncInterval, "minimum duration in microseconds between syncs of the WAL. If WAL syncs are requested faster than this interval, they will be artificially delayed.") + f.Int(prefix+".target-byte-deletion-rate", PebbleExperimentalConfigDefault.TargetByteDeletionRate, "rate (in bytes per second) at which sstable file deletions are limited to (under normal circumstances).") + f.Int(prefix+".block-size", PebbleExperimentalConfigDefault.BlockSize, "target uncompressed size in bytes of each table block") + f.Int(prefix+".index-block-size", PebbleExperimentalConfigDefault.IndexBlockSize, fmt.Sprintf("target uncompressed size in bytes of each index block. When the index block size is larger than this target, two-level indexes are automatically enabled. Setting this option to a large value (such as %d) disables the automatic creation of two-level indexes.", math.MaxInt32)) + f.Int64(prefix+".target-file-size", PebbleExperimentalConfigDefault.TargetFileSize, "target file size for the level 0") + f.Bool(prefix+".target-file-size-equal-levels", PebbleExperimentalConfigDefault.TargetFileSizeEqualLevels, "if true same target-file-size will be uses for all levels, otherwise target size for layer n = 2 * target size for layer n - 1") + + f.Int(prefix+".l0-compaction-concurrency", PebbleExperimentalConfigDefault.L0CompactionConcurrency, "threshold of L0 read-amplification at which compaction concurrency is enabled (if compaction-debt-concurrency was not already exceeded). Every multiple of this value enables another concurrent compaction up to max-concurrent-compactions.") + f.Uint64(prefix+".compaction-debt-concurrency", PebbleExperimentalConfigDefault.CompactionDebtConcurrency, "controls the threshold of compaction debt at which additional compaction concurrency slots are added. For every multiple of this value in compaction debt bytes, an additional concurrent compaction is added. This works \"on top\" of l0-compaction-concurrency, so the higher of the count of compaction concurrency slots as determined by the two options is chosen.") + f.Int64(prefix+".read-compaction-rate", PebbleExperimentalConfigDefault.ReadCompactionRate, "controls the frequency of read triggered compactions by adjusting `AllowedSeeks` in manifest.FileMetadata: AllowedSeeks = FileSize / ReadCompactionRate") + f.Int64(prefix+".read-sampling-multiplier", PebbleExperimentalConfigDefault.ReadSamplingMultiplier, "a multiplier for the readSamplingPeriod in iterator.maybeSampleRead() to control the frequency of read sampling to trigger a read triggered compaction. A value of -1 prevents sampling and disables read triggered compactions. Geth default is -1. The pebble default is 1 << 4. which gets multiplied with a constant of 1 << 16 to yield 1 << 20 (1MB).") + f.Int(prefix+".max-writer-concurrency", PebbleExperimentalConfigDefault.MaxWriterConcurrency, "maximum number of compression workers the compression queue is allowed to use. If max-writer-concurrency > 0, then the Writer will use parallelism, to compress and write blocks to disk. Otherwise, the writer will compress and write blocks to disk synchronously.") + f.Bool(prefix+".force-writer-parallelism", PebbleExperimentalConfigDefault.ForceWriterParallelism, "force parallelism in the sstable Writer for the metamorphic tests. Even with the MaxWriterConcurrency option set, pebble only enables parallelism in the sstable Writer if there is enough CPU available, and this option bypasses that.") +} + +func (c *PebbleExperimentalConfig) Validate() error { + if c.WALDir != "" && !filepath.IsAbs(c.WALDir) { + return fmt.Errorf("invalid .wal-dir directory (%s) - has to be an absolute path", c.WALDir) + } + // TODO + return nil +} + +func (c *PebbleConfig) ExtraOptions(namespace string) *pebble.ExtraOptions { + var maxConcurrentCompactions func() int + if c.MaxConcurrentCompactions > 0 { + maxConcurrentCompactions = func() int { return c.MaxConcurrentCompactions } + } + var walMinSyncInterval func() time.Duration + if c.Experimental.WALMinSyncInterval > 0 { + walMinSyncInterval = func() time.Duration { + return time.Microsecond * time.Duration(c.Experimental.WALMinSyncInterval) + } + } + var levels []pebble.ExtraLevelOptions + for i := 0; i < 7; i++ { + targetFileSize := c.Experimental.TargetFileSize + if !c.Experimental.TargetFileSizeEqualLevels { + targetFileSize = targetFileSize << i + } + levels = append(levels, pebble.ExtraLevelOptions{ + BlockSize: c.Experimental.BlockSize, + IndexBlockSize: c.Experimental.IndexBlockSize, + TargetFileSize: targetFileSize, + }) + } + walDir := c.Experimental.WALDir + if walDir != "" { + walDir = path.Join(walDir, namespace) + } + return &pebble.ExtraOptions{ + BytesPerSync: c.Experimental.BytesPerSync, + L0CompactionFileThreshold: c.Experimental.L0CompactionFileThreshold, + L0CompactionThreshold: c.Experimental.L0CompactionThreshold, + L0StopWritesThreshold: c.Experimental.L0StopWritesThreshold, + LBaseMaxBytes: c.Experimental.LBaseMaxBytes, + MemTableStopWritesThreshold: c.Experimental.MemTableStopWritesThreshold, + MaxConcurrentCompactions: maxConcurrentCompactions, + DisableAutomaticCompactions: c.Experimental.DisableAutomaticCompactions, + WALBytesPerSync: c.Experimental.WALBytesPerSync, + WALDir: walDir, + WALMinSyncInterval: walMinSyncInterval, + TargetByteDeletionRate: c.Experimental.TargetByteDeletionRate, + Experimental: pebble.ExtraOptionsExperimental{ + L0CompactionConcurrency: c.Experimental.L0CompactionConcurrency, + CompactionDebtConcurrency: c.Experimental.CompactionDebtConcurrency, + ReadCompactionRate: c.Experimental.ReadCompactionRate, + ReadSamplingMultiplier: c.Experimental.ReadSamplingMultiplier, + MaxWriterConcurrency: c.Experimental.MaxWriterConcurrency, + ForceWriterParallelism: c.Experimental.ForceWriterParallelism, + }, + Levels: levels, + } +} diff --git a/cmd/conf/init.go b/cmd/conf/init.go index 8a6c5096fb..2697a11111 100644 --- a/cmd/conf/init.go +++ b/cmd/conf/init.go @@ -45,7 +45,7 @@ var InitConfigDefault = InitConfig{ func InitConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".force", InitConfigDefault.Force, "if true: in case database exists init code will be reexecuted and genesis block compared to database") - f.String(prefix+".url", InitConfigDefault.Url, "url to download initializtion data - will poll if download fails") + f.String(prefix+".url", InitConfigDefault.Url, "url to download initialization data - will poll if download fails") f.String(prefix+".download-path", InitConfigDefault.DownloadPath, "path to save temp downloaded file") f.Duration(prefix+".download-poll", InitConfigDefault.DownloadPoll, "how long to wait between polling attempts") f.Bool(prefix+".dev-init", InitConfigDefault.DevInit, "init with dev data (1 account with balance) instead of file import") diff --git a/cmd/daserver/daserver.go b/cmd/daserver/daserver.go index 07481651b2..1a3fd435b8 100644 --- a/cmd/daserver/daserver.go +++ b/cmd/daserver/daserver.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "io" "net/http" "os" "os/signal" @@ -30,10 +31,11 @@ import ( ) type DAServerConfig struct { - EnableRPC bool `koanf:"enable-rpc"` - RPCAddr string `koanf:"rpc-addr"` - RPCPort uint64 `koanf:"rpc-port"` - RPCServerTimeouts genericconf.HTTPServerTimeoutConfig `koanf:"rpc-server-timeouts"` + EnableRPC bool `koanf:"enable-rpc"` + RPCAddr string `koanf:"rpc-addr"` + RPCPort uint64 `koanf:"rpc-port"` + RPCServerTimeouts genericconf.HTTPServerTimeoutConfig `koanf:"rpc-server-timeouts"` + RPCServerBodyLimit int `koanf:"rpc-server-body-limit"` EnableREST bool `koanf:"enable-rest"` RESTAddr string `koanf:"rest-addr"` @@ -43,7 +45,7 @@ type DAServerConfig struct { DataAvailability das.DataAvailabilityConfig `koanf:"data-availability"` Conf genericconf.ConfConfig `koanf:"conf"` - LogLevel int `koanf:"log-level"` + LogLevel string `koanf:"log-level"` LogType string `koanf:"log-type"` Metrics bool `koanf:"metrics"` @@ -57,13 +59,14 @@ var DefaultDAServerConfig = DAServerConfig{ RPCAddr: "localhost", RPCPort: 9876, RPCServerTimeouts: genericconf.HTTPServerTimeoutConfigDefault, + RPCServerBodyLimit: genericconf.HTTPServerBodyLimitDefault, EnableREST: false, RESTAddr: "localhost", RESTPort: 9877, RESTServerTimeouts: genericconf.HTTPServerTimeoutConfigDefault, DataAvailability: das.DefaultDataAvailabilityConfig, Conf: genericconf.ConfConfigDefault, - LogLevel: int(log.LvlInfo), + LogLevel: "INFO", LogType: "plaintext", Metrics: false, MetricsServer: genericconf.MetricsServerConfigDefault, @@ -87,6 +90,7 @@ func parseDAServer(args []string) (*DAServerConfig, error) { f.Bool("enable-rpc", DefaultDAServerConfig.EnableRPC, "enable the HTTP-RPC server listening on rpc-addr and rpc-port") f.String("rpc-addr", DefaultDAServerConfig.RPCAddr, "HTTP-RPC server listening interface") f.Uint64("rpc-port", DefaultDAServerConfig.RPCPort, "HTTP-RPC server listening port") + f.Int("rpc-server-body-limit", DefaultDAServerConfig.RPCServerBodyLimit, "HTTP-RPC server maximum request body size in bytes; the default (0) uses geth's 5MB limit") genericconf.HTTPServerTimeoutConfigAddOptions("rpc-server-timeouts", f) f.Bool("enable-rest", DefaultDAServerConfig.EnableREST, "enable the REST server listening on rest-addr and rest-port") @@ -100,7 +104,7 @@ func parseDAServer(args []string) (*DAServerConfig, error) { f.Bool("pprof", DefaultDAServerConfig.PProf, "enable pprof") genericconf.PProfAddOptions("pprof-cfg", f) - f.Int("log-level", int(log.LvlInfo), "log level; 1: ERROR, 2: WARN, 3: INFO, 4: DEBUG, 5: TRACE") + f.String("log-level", DefaultDAServerConfig.LogLevel, "log level, valid values are CRIT, ERROR, WARN, INFO, DEBUG, TRACE") f.String("log-type", DefaultDAServerConfig.LogType, "log type (plaintext or json)") das.DataAvailabilityConfigAddDaserverOptions("data-availability", f) @@ -182,14 +186,19 @@ func startup() error { confighelpers.PrintErrorAndExit(errors.New("please specify at least one of --enable-rest or --enable-rpc"), printSampleUsage) } - logFormat, err := genericconf.ParseLogType(serverConfig.LogType) + logLevel, err := genericconf.ToSlogLevel(serverConfig.LogLevel) + if err != nil { + confighelpers.PrintErrorAndExit(err, printSampleUsage) + } + + handler, err := genericconf.HandlerFromLogType(serverConfig.LogType, io.Writer(os.Stderr)) if err != nil { flag.Usage() - panic(fmt.Sprintf("Error parsing log type: %v", err)) + return fmt.Errorf("error parsing log type when creating handler: %w", err) } - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, logFormat)) - glogger.Verbosity(log.Lvl(serverConfig.LogLevel)) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler(handler) + glogger.Verbosity(logLevel) + log.SetDefault(log.NewLogger(glogger)) if err := startMetrics(serverConfig); err != nil { return err @@ -244,7 +253,7 @@ func startup() error { if serverConfig.EnableRPC { log.Info("Starting HTTP-RPC server", "addr", serverConfig.RPCAddr, "port", serverConfig.RPCPort, "revision", vcsRevision, "vcs.time", vcsTime) - rpcServer, err = das.StartDASRPCServer(ctx, serverConfig.RPCAddr, serverConfig.RPCPort, serverConfig.RPCServerTimeouts, daReader, daWriter, daHealthChecker) + rpcServer, err = das.StartDASRPCServer(ctx, serverConfig.RPCAddr, serverConfig.RPCPort, serverConfig.RPCServerTimeouts, serverConfig.RPCServerBodyLimit, daReader, daWriter, daHealthChecker) if err != nil { return err } diff --git a/cmd/dataavailability/data_availability_check.go b/cmd/dataavailability/data_availability_check.go index 72a311a7be..d80c0475bf 100644 --- a/cmd/dataavailability/data_availability_check.go +++ b/cmd/dataavailability/data_availability_check.go @@ -21,7 +21,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rpc" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/cmd/util/confighelpers" "github.com/offchainlabs/nitro/das" "github.com/offchainlabs/nitro/solgen/go/bridgegen" @@ -65,7 +65,7 @@ type DataAvailabilityCheck struct { config *DataAvailabilityCheckConfig inboxAddr *common.Address inboxContract *bridgegen.SequencerInbox - urlToReaderMap map[string]arbstate.DataAvailabilityReader + urlToReaderMap map[string]daprovider.DASReader checkInterval time.Duration } @@ -86,7 +86,7 @@ func newDataAvailabilityCheck(ctx context.Context, dataAvailabilityCheckConfig * if err != nil { return nil, err } - urlToReaderMap := make(map[string]arbstate.DataAvailabilityReader, len(onlineUrls)) + urlToReaderMap := make(map[string]daprovider.DASReader, len(onlineUrls)) for _, url := range onlineUrls { reader, err := das.NewRestfulDasClientFromURL(url) if err != nil { @@ -238,7 +238,7 @@ func (d *DataAvailabilityCheck) checkDataAvailability(ctx context.Context, deliv if data == nil { return false, nil } - cert, err := arbstate.DeserializeDASCertFrom(bytes.NewReader(data)) + cert, err := daprovider.DeserializeDASCertFrom(bytes.NewReader(data)) if err != nil { return true, err } diff --git a/cmd/datool/datool.go b/cmd/datool/datool.go index d78d975fd5..3f64a990ca 100644 --- a/cmd/datool/datool.go +++ b/cmd/datool/datool.go @@ -22,7 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/cmd/util" @@ -165,7 +165,7 @@ func startClientStore(args []string) error { } ctx := context.Background() - var cert *arbstate.DataAvailabilityCertificate + var cert *daprovider.DataAvailabilityCertificate if config.RandomMessageSize > 0 { message := make([]byte, config.RandomMessageSize) @@ -184,7 +184,7 @@ func startClientStore(args []string) error { return err } - serializedCert := das.Serialize(cert) + serializedCert := daprovider.Serialize(cert) fmt.Printf("Hex Encoded Cert: %s\n", hexutil.Encode(serializedCert)) fmt.Printf("Hex Encoded Data Hash: %s\n", hexutil.Encode(cert.DataHash[:])) diff --git a/cmd/genericconf/config.go b/cmd/genericconf/config.go index 50aafbe223..06e1fcd12d 100644 --- a/cmd/genericconf/config.go +++ b/cmd/genericconf/config.go @@ -5,11 +5,13 @@ package genericconf import ( "errors" + "io" "time" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" flag "github.com/spf13/pflag" + "golang.org/x/exp/slog" ) type ConfConfig struct { @@ -63,11 +65,11 @@ var DefaultS3Config = S3Config{ SecretKey: "", } -func ParseLogType(logType string) (log.Format, error) { +func HandlerFromLogType(logType string, output io.Writer) (slog.Handler, error) { if logType == "plaintext" { - return log.TerminalFormat(false), nil + return log.NewTerminalHandler(output, false), nil } else if logType == "json" { - return log.JSONFormat(), nil + return log.JSONHandler(output), nil } return nil, errors.New("invalid log type") } diff --git a/cmd/genericconf/filehandler_test.go b/cmd/genericconf/filehandler_test.go index 7ea0668229..daa9ed397c 100644 --- a/cmd/genericconf/filehandler_test.go +++ b/cmd/genericconf/filehandler_test.go @@ -72,9 +72,10 @@ func testFileHandler(t *testing.T, testCompressed bool) { config.MaxSize = 1 config.Compress = testCompressed config.File = testFile - fileHandler := globalFileHandlerFactory.newHandler(log.JSONFormat(), &config, testFile) - defer func() { testhelpers.RequireImpl(t, globalFileHandlerFactory.close()) }() - log.Root().SetHandler(fileHandler) + handler, err := HandlerFromLogType("json", globalFileLoggerFactory.newFileWriter(&config, testFile)) + defer func() { testhelpers.RequireImpl(t, globalFileLoggerFactory.close()) }() + testhelpers.RequireImpl(t, err) + log.SetDefault(log.NewLogger(handler)) expected := []string{"dead", "beef", "ate", "bad", "beef"} for _, e := range expected { log.Warn(e) diff --git a/cmd/genericconf/logging.go b/cmd/genericconf/logging.go index a50dfa3190..fa45953278 100644 --- a/cmd/genericconf/logging.go +++ b/cmd/genericconf/logging.go @@ -4,22 +4,46 @@ import ( "context" "flag" "fmt" + "io" "os" + "sync" "github.com/ethereum/go-ethereum/log" "gopkg.in/natefinch/lumberjack.v2" ) -var globalFileHandlerFactory = fileHandlerFactory{} +var globalFileLoggerFactory = fileLoggerFactory{} -type fileHandlerFactory struct { - writer *lumberjack.Logger - records chan *log.Record - cancel context.CancelFunc +type fileLoggerFactory struct { + // writerMutex is to avoid parallel writes to the file-logger + writerMutex sync.Mutex + writer *lumberjack.Logger + + cancel context.CancelFunc + + // writeStartPing and writeDonePing are used to simulate sending of data via a buffered channel + // when Write is called and receiving it on another go-routine to write it to the io.Writer. + writeStartPing chan struct{} + writeDonePing chan struct{} +} + +// Write is essentially a wrapper for filewriter or lumberjack.Logger's Write method to implement +// config.BufSize functionality, data is dropped when l.writeStartPing channel (of size config.BuffSize) is full +func (l *fileLoggerFactory) Write(p []byte) (n int, err error) { + select { + case l.writeStartPing <- struct{}{}: + // Write data to the filelogger + l.writerMutex.Lock() + _, _ = l.writer.Write(p) + l.writerMutex.Unlock() + l.writeDonePing <- struct{}{} + default: + } + return len(p), nil } -// newHandler is not threadsafe -func (l *fileHandlerFactory) newHandler(logFormat log.Format, config *FileLoggingConfig, filename string) log.Handler { +// newFileWriter is not threadsafe +func (l *fileLoggerFactory) newFileWriter(config *FileLoggingConfig, filename string) io.Writer { l.close() l.writer = &lumberjack.Logger{ Filename: filename, @@ -28,40 +52,29 @@ func (l *fileHandlerFactory) newHandler(logFormat log.Format, config *FileLoggin MaxAge: config.MaxAge, Compress: config.Compress, } - // capture copy of the pointer - writer := l.writer - // lumberjack.Logger already locks on Write, no need for SyncHandler proxy which is used in StreamHandler - unsafeStreamHandler := log.LazyHandler(log.FuncHandler(func(r *log.Record) error { - _, err := writer.Write(logFormat.Format(r)) - return err - })) - l.records = make(chan *log.Record, config.BufSize) + l.writeStartPing = make(chan struct{}, config.BufSize) + l.writeDonePing = make(chan struct{}, config.BufSize) // capture copy - records := l.records + writeStartPing := l.writeStartPing + writeDonePing := l.writeDonePing var consumerCtx context.Context consumerCtx, l.cancel = context.WithCancel(context.Background()) go func() { + // writeStartPing channel signals Write operations to correctly implement config.BufSize functionality for { select { - case r := <-records: - _ = unsafeStreamHandler.Log(r) + case <-writeStartPing: + <-writeDonePing case <-consumerCtx.Done(): return } } }() - return log.FuncHandler(func(r *log.Record) error { - select { - case records <- r: - return nil - default: - return fmt.Errorf("Buffer overflow, dropping record") - } - }) + return l } // close is not threadsafe -func (l *fileHandlerFactory) close() error { +func (l *fileLoggerFactory) close() error { if l.cancel != nil { l.cancel() l.cancel = nil @@ -76,28 +89,35 @@ func (l *fileHandlerFactory) close() error { } // initLog is not threadsafe -func InitLog(logType string, logLevel log.Lvl, fileLoggingConfig *FileLoggingConfig, pathResolver func(string) string) error { - logFormat, err := ParseLogType(logType) - if err != nil { - flag.Usage() - return fmt.Errorf("error parsing log type: %w", err) - } +func InitLog(logType string, logLevel string, fileLoggingConfig *FileLoggingConfig, pathResolver func(string) string) error { var glogger *log.GlogHandler // always close previous instance of file logger - if err := globalFileHandlerFactory.close(); err != nil { + if err := globalFileLoggerFactory.close(); err != nil { return fmt.Errorf("failed to close file writer: %w", err) } + var output io.Writer if fileLoggingConfig.Enable { - glogger = log.NewGlogHandler( - log.MultiHandler( - log.StreamHandler(os.Stderr, logFormat), - // on overflow records are dropped silently as MultiHandler ignores errors - globalFileHandlerFactory.newHandler(logFormat, fileLoggingConfig, pathResolver(fileLoggingConfig.File)), - )) + output = io.MultiWriter( + io.Writer(os.Stderr), + // on overflow writeStartPing are dropped silently + globalFileLoggerFactory.newFileWriter(fileLoggingConfig, pathResolver(fileLoggingConfig.File)), + ) } else { - glogger = log.NewGlogHandler(log.StreamHandler(os.Stderr, logFormat)) + output = io.Writer(os.Stderr) + } + handler, err := HandlerFromLogType(logType, output) + if err != nil { + flag.Usage() + return fmt.Errorf("error parsing log type when creating handler: %w", err) } - glogger.Verbosity(logLevel) - log.Root().SetHandler(glogger) + slogLevel, err := ToSlogLevel(logLevel) + if err != nil { + flag.Usage() + return fmt.Errorf("error parsing log level: %w", err) + } + + glogger = log.NewGlogHandler(handler) + glogger.Verbosity(slogLevel) + log.SetDefault(log.NewLogger(glogger)) return nil } diff --git a/cmd/genericconf/loglevel.go b/cmd/genericconf/loglevel.go new file mode 100644 index 0000000000..f7ad05a2cc --- /dev/null +++ b/cmd/genericconf/loglevel.go @@ -0,0 +1,38 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package genericconf + +import ( + "errors" + "strconv" + "strings" + + "github.com/ethereum/go-ethereum/log" + "golang.org/x/exp/slog" +) + +func ToSlogLevel(str string) (slog.Level, error) { + switch strings.ToLower(str) { + case "trace": + return log.LevelTrace, nil + case "debug": + return log.LevelDebug, nil + case "info": + return log.LevelInfo, nil + case "warn": + return log.LevelWarn, nil + case "error": + return log.LevelError, nil + case "crit": + return log.LevelCrit, nil + default: + legacyLevel, err := strconv.Atoi(str) + if err != nil { + // Leave legacy geth numeric log levels undocumented, but if anyone happens + // to be using them, it will work. + return log.LevelTrace, errors.New("invalid log-level") + } + return log.FromLegacyLevel(legacyLevel), nil + } +} diff --git a/cmd/genericconf/server.go b/cmd/genericconf/server.go index 7550791d6d..9b8acd5f71 100644 --- a/cmd/genericconf/server.go +++ b/cmd/genericconf/server.go @@ -48,6 +48,8 @@ var HTTPServerTimeoutConfigDefault = HTTPServerTimeoutConfig{ IdleTimeout: 120 * time.Second, } +const HTTPServerBodyLimitDefault = 0 // Use default from go-ethereum + func (c HTTPConfig) Apply(stackConf *node.Config) { stackConf.HTTPHost = c.Addr stackConf.HTTPPort = c.Port diff --git a/cmd/ipfshelper/ipfshelper.go b/cmd/ipfshelper/ipfshelper.bkup_go similarity index 99% rename from cmd/ipfshelper/ipfshelper.go rename to cmd/ipfshelper/ipfshelper.bkup_go index 82e726dbf3..ccde492ca6 100644 --- a/cmd/ipfshelper/ipfshelper.go +++ b/cmd/ipfshelper/ipfshelper.bkup_go @@ -1,3 +1,6 @@ +//go:build ipfs +// +build ipfs + package ipfshelper import ( diff --git a/cmd/ipfshelper/ipfshelper_stub.go b/cmd/ipfshelper/ipfshelper_stub.go new file mode 100644 index 0000000000..fa6a451927 --- /dev/null +++ b/cmd/ipfshelper/ipfshelper_stub.go @@ -0,0 +1,31 @@ +//go:build !ipfs +// +build !ipfs + +package ipfshelper + +import ( + "context" + "errors" +) + +type IpfsHelper struct{} + +var ErrIpfsNotSupported = errors.New("ipfs not supported") + +var DefaultIpfsProfiles = "default ipfs profiles stub" + +func CanBeIpfsPath(pathString string) bool { + return false +} + +func CreateIpfsHelper(ctx context.Context, downloadPath string, clientOnly bool, peerList []string, profiles string) (*IpfsHelper, error) { + return nil, ErrIpfsNotSupported +} + +func (h *IpfsHelper) DownloadFile(ctx context.Context, cidString string, destinationDir string) (string, error) { + return "", ErrIpfsNotSupported +} + +func (h *IpfsHelper) Close() error { + return ErrIpfsNotSupported +} diff --git a/cmd/ipfshelper/ipfshelper_test.go b/cmd/ipfshelper/ipfshelper_test.go index 052d6bab01..80f10c21f6 100644 --- a/cmd/ipfshelper/ipfshelper_test.go +++ b/cmd/ipfshelper/ipfshelper_test.go @@ -1,3 +1,6 @@ +//go:build ipfs +// +build ipfs + package ipfshelper import ( diff --git a/cmd/nitro-val/config.go b/cmd/nitro-val/config.go index 51d3978836..b52a1c6b5e 100644 --- a/cmd/nitro-val/config.go +++ b/cmd/nitro-val/config.go @@ -2,10 +2,10 @@ package main import ( "fmt" + "reflect" "time" - "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p/nat" @@ -20,7 +20,7 @@ import ( type ValidationNodeConfig struct { Conf genericconf.ConfConfig `koanf:"conf" reload:"hot"` Validation valnode.Config `koanf:"validation" reload:"hot"` - LogLevel int `koanf:"log-level" reload:"hot"` + LogLevel string `koanf:"log-level" reload:"hot"` LogType string `koanf:"log-type" reload:"hot"` FileLogging genericconf.FileLoggingConfig `koanf:"file-logging" reload:"hot"` Persistent conf.PersistentConfig `koanf:"persistent"` @@ -61,7 +61,7 @@ var IPCConfigDefault = genericconf.IPCConfig{ var ValidationNodeConfigDefault = ValidationNodeConfig{ Conf: genericconf.ConfConfigDefault, - LogLevel: int(log.LvlInfo), + LogLevel: "INFO", LogType: "plaintext", Persistent: conf.PersistentConfigDefault, HTTP: HTTPConfigDefault, @@ -79,7 +79,7 @@ var ValidationNodeConfigDefault = ValidationNodeConfig{ func ValidationNodeConfigAddOptions(f *flag.FlagSet) { genericconf.ConfConfigAddOptions("conf", f) valnode.ValidationConfigAddOptions("validation", f) - f.Int("log-level", ValidationNodeConfigDefault.LogLevel, "log level") + f.String("log-level", ValidationNodeConfigDefault.LogLevel, "log level, valid values are CRIT, ERROR, WARN, INFO, DEBUG, TRACE") f.String("log-type", ValidationNodeConfigDefault.LogType, "log type (plaintext or json)") genericconf.FileLoggingConfigAddOptions("file-logging", f) conf.PersistentConfigAddOptions("persistent", f) diff --git a/cmd/nitro-val/nitro_val.go b/cmd/nitro-val/nitro_val.go index fea95cbb15..1e894336ea 100644 --- a/cmd/nitro-val/nitro_val.go +++ b/cmd/nitro-val/nitro_val.go @@ -20,7 +20,7 @@ import ( "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/cmd/util/confighelpers" - _ "github.com/offchainlabs/nitro/nodeInterface" + _ "github.com/offchainlabs/nitro/execution/nodeInterface" "github.com/offchainlabs/nitro/validator/valnode" ) @@ -89,7 +89,7 @@ func mainImpl() int { } } - err = genericconf.InitLog(nodeConfig.LogType, log.Lvl(nodeConfig.LogLevel), &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) + err = genericconf.InitLog(nodeConfig.LogType, nodeConfig.LogLevel, &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) if err != nil { fmt.Fprintf(os.Stderr, "Error initializing logging: %v\n", err) return 1 @@ -108,7 +108,7 @@ func mainImpl() int { liveNodeConfig := genericconf.NewLiveConfig[*ValidationNodeConfig](args, nodeConfig, ParseNode) liveNodeConfig.SetOnReloadHook(func(oldCfg *ValidationNodeConfig, newCfg *ValidationNodeConfig) error { - return genericconf.InitLog(newCfg.LogType, log.Lvl(newCfg.LogLevel), &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) + return genericconf.InitLog(newCfg.LogType, newCfg.LogLevel, &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) }) valnode.EnsureValidationExposedViaAuthRPC(&stackConf) diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 6c12e67c92..14cfb0d76a 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -5,10 +5,14 @@ package main import ( "context" + "crypto/sha256" + "encoding/hex" "encoding/json" "errors" "fmt" + "io" "math/big" + "net/http" "os" "runtime" "strings" @@ -40,6 +44,8 @@ import ( "github.com/offchainlabs/nitro/util/arbmath" ) +var notFoundError = errors.New("file not found") + func downloadInit(ctx context.Context, initConfig *conf.InitConfig) (string, error) { if initConfig.Url == "" { return "", nil @@ -66,18 +72,30 @@ func downloadInit(ctx context.Context, initConfig *conf.InitConfig) (string, err } return initFile, nil } - grabclient := grab.NewClient() log.Info("Downloading initial database", "url", initConfig.Url) - fmt.Println() + path, err := downloadFile(ctx, initConfig, initConfig.Url) + if errors.Is(err, notFoundError) { + return downloadInitInParts(ctx, initConfig) + } + return path, err +} + +func downloadFile(ctx context.Context, initConfig *conf.InitConfig, url string) (string, error) { + checksum, err := fetchChecksum(ctx, url+".sha256") + if err != nil { + return "", fmt.Errorf("error fetching checksum: %w", err) + } + grabclient := grab.NewClient() printTicker := time.NewTicker(time.Second) defer printTicker.Stop() attempt := 0 for { attempt++ - req, err := grab.NewRequest(initConfig.DownloadPath, initConfig.Url) + req, err := grab.NewRequest(initConfig.DownloadPath, url) if err != nil { panic(err) } + req.SetChecksum(sha256.New(), checksum, false) resp := grabclient.Do(req.WithContext(ctx)) firstPrintTime := time.Now().Add(time.Second * 2) updateLoop: @@ -102,6 +120,9 @@ func downloadInit(ctx context.Context, initConfig *conf.InitConfig) (string, err } case <-resp.Done: if err := resp.Err(); err != nil { + if resp.HTTPResponse.StatusCode == http.StatusNotFound { + return "", fmt.Errorf("file not found but checksum exists") + } fmt.Printf("\n attempt %d failed: %v\n", attempt, err) break updateLoop } @@ -121,6 +142,99 @@ func downloadInit(ctx context.Context, initConfig *conf.InitConfig) (string, err } } +// fetchChecksum performs a GET request to the specified URL using the provided context +// and returns the checksum as a []byte +func fetchChecksum(ctx context.Context, url string) ([]byte, error) { + req, err := http.NewRequestWithContext(ctx, "GET", url, nil) + if err != nil { + return nil, fmt.Errorf("error creating request: %w", err) + } + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("error making GET request: %w", err) + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusNotFound { + return nil, notFoundError + } else if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code: %v", resp.Status) + } + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("error reading response body: %w", err) + } + checksumStr := strings.TrimSpace(string(body)) + checksum, err := hex.DecodeString(checksumStr) + if err != nil { + return nil, fmt.Errorf("error decoding checksum: %w", err) + } + if len(checksum) != sha256.Size { + return nil, fmt.Errorf("invalid checksum length") + } + return checksum, nil +} + +func downloadInitInParts(ctx context.Context, initConfig *conf.InitConfig) (string, error) { + log.Info("File not found; trying to download database in parts") + fileInfo, err := os.Stat(initConfig.DownloadPath) + if err != nil || !fileInfo.IsDir() { + return "", fmt.Errorf("download path must be a directory: %v", initConfig.DownloadPath) + } + part := 0 + parts := []string{} + defer func() { + // remove all temporary files. + for _, part := range parts { + err := os.Remove(part) + if err != nil { + log.Warn("Failed to remove temporary file", "file", part) + } + } + }() + for { + url := fmt.Sprintf("%s.part%d", initConfig.Url, part) + log.Info("Downloading database part", "url", url) + partFile, err := downloadFile(ctx, initConfig, url) + if errors.Is(err, notFoundError) { + log.Info("Part not found; concatenating archive into single file", "numParts", len(parts)) + break + } else if err != nil { + return "", err + } + parts = append(parts, partFile) + part++ + } + return joinArchive(parts) +} + +// joinArchive joins the archive parts into a single file and return its path. +func joinArchive(parts []string) (string, error) { + if len(parts) == 0 { + return "", fmt.Errorf("no database parts found") + } + archivePath := strings.TrimSuffix(parts[0], ".part0") + archive, err := os.Create(archivePath) + if err != nil { + return "", fmt.Errorf("failed to create archive: %w", err) + } + defer archive.Close() + for _, part := range parts { + partFile, err := os.Open(part) + if err != nil { + return "", fmt.Errorf("failed to open part file %s: %w", part, err) + } + defer partFile.Close() + _, err = io.Copy(archive, partFile) + if err != nil { + return "", fmt.Errorf("failed to copy part file %s: %w", part, err) + } + log.Info("Joined database part into archive", "part", part) + } + log.Info("Successfully joined parts into archive", "archive", archivePath) + return archivePath, nil +} + func validateBlockChain(blockChain *core.BlockChain, chainConfig *params.ChainConfig) error { statedb, err := blockChain.State() if err != nil { @@ -155,23 +269,39 @@ func validateBlockChain(blockChain *core.BlockChain, chainConfig *params.ChainCo return fmt.Errorf("invalid chain config, not compatible with previous: %w", err) } } + // Make sure we don't allow accidentally downgrading ArbOS + if chainConfig.DebugMode() { + if currentArbosState.ArbOSVersion() > currentArbosState.MaxDebugArbosVersionSupported() { + return fmt.Errorf("attempted to launch node in debug mode with ArbOS version %v on ArbOS state with version %v", currentArbosState.MaxDebugArbosVersionSupported(), currentArbosState.ArbOSVersion()) + } + } else { + if currentArbosState.ArbOSVersion() > currentArbosState.MaxArbosVersionSupported() { + return fmt.Errorf("attempted to launch node with ArbOS version %v on ArbOS state with version %v", currentArbosState.MaxArbosVersionSupported(), currentArbosState.ArbOSVersion()) + } + + } return nil } -func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeConfig, chainId *big.Int, cacheConfig *core.CacheConfig, l1Client arbutil.L1Interface, rollupAddrs chaininfo.RollupAddresses, tracer core.BlockchainLogger) (ethdb.Database, *core.BlockChain, error) { +func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeConfig, chainId *big.Int, cacheConfig *core.CacheConfig, persistentConfig *conf.PersistentConfig, l1Client arbutil.L1Interface, rollupAddrs chaininfo.RollupAddresses, tracer core.BlockchainLogger) (ethdb.Database, *core.BlockChain, error) { if !config.Init.Force { - if readOnlyDb, err := stack.OpenDatabaseWithFreezer("l2chaindata", 0, 0, "", "", true); err == nil { + if readOnlyDb, err := stack.OpenDatabaseWithFreezerWithExtraOptions("l2chaindata", 0, 0, "", "l2chaindata/", true, persistentConfig.Pebble.ExtraOptions("l2chaindata")); err == nil { if chainConfig := gethexec.TryReadStoredChainConfig(readOnlyDb); chainConfig != nil { readOnlyDb.Close() if !arbmath.BigEquals(chainConfig.ChainID, chainId) { return nil, nil, fmt.Errorf("database has chain ID %v but config has chain ID %v (are you sure this database is for the right chain?)", chainConfig.ChainID, chainId) } - chainDb, err := stack.OpenDatabaseWithFreezer("l2chaindata", config.Execution.Caching.DatabaseCache, config.Persistent.Handles, config.Persistent.Ancient, "", false) + chainData, err := stack.OpenDatabaseWithFreezerWithExtraOptions("l2chaindata", config.Execution.Caching.DatabaseCache, config.Persistent.Handles, config.Persistent.Ancient, "l2chaindata/", false, persistentConfig.Pebble.ExtraOptions("l2chaindata")) if err != nil { - return chainDb, nil, err + return nil, nil, err } - err = pruning.PruneChainDb(ctx, chainDb, stack, &config.Init, cacheConfig, l1Client, rollupAddrs, config.Node.ValidatorRequired()) + wasmDb, err := stack.OpenDatabaseWithExtraOptions("wasm", config.Execution.Caching.DatabaseCache, config.Persistent.Handles, "wasm/", false, persistentConfig.Pebble.ExtraOptions("wasm")) + if err != nil { + return nil, nil, err + } + chainDb := rawdb.WrapDatabaseWithWasm(chainData, wasmDb, 1) + err = pruning.PruneChainDb(ctx, chainDb, stack, &config.Init, cacheConfig, persistentConfig, l1Client, rollupAddrs, config.Node.ValidatorRequired()) if err != nil { return chainDb, nil, fmt.Errorf("error pruning: %w", err) } @@ -219,10 +349,15 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo var initDataReader statetransfer.InitDataReader = nil - chainDb, err := stack.OpenDatabaseWithFreezer("l2chaindata", config.Execution.Caching.DatabaseCache, config.Persistent.Handles, config.Persistent.Ancient, "", false) + chainData, err := stack.OpenDatabaseWithFreezerWithExtraOptions("l2chaindata", config.Execution.Caching.DatabaseCache, config.Persistent.Handles, config.Persistent.Ancient, "l2chaindata/", false, persistentConfig.Pebble.ExtraOptions("l2chaindata")) + if err != nil { + return nil, nil, err + } + wasmDb, err := stack.OpenDatabaseWithExtraOptions("wasm", config.Execution.Caching.DatabaseCache, config.Persistent.Handles, "wasm/", false, persistentConfig.Pebble.ExtraOptions("wasm")) if err != nil { - return chainDb, nil, err + return nil, nil, err } + chainDb := rawdb.WrapDatabaseWithWasm(chainData, wasmDb, 1) if config.Init.ImportFile != "" { initDataReader, err = statetransfer.NewJsonInitDataReader(config.Init.ImportFile) @@ -367,7 +502,7 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo return chainDb, l2BlockChain, err } - err = pruning.PruneChainDb(ctx, chainDb, stack, &config.Init, cacheConfig, l1Client, rollupAddrs, config.Node.ValidatorRequired()) + err = pruning.PruneChainDb(ctx, chainDb, stack, &config.Init, cacheConfig, persistentConfig, l1Client, rollupAddrs, config.Node.ValidatorRequired()) if err != nil { return chainDb, nil, fmt.Errorf("error pruning: %w", err) } diff --git a/cmd/nitro/init_test.go b/cmd/nitro/init_test.go new file mode 100644 index 0000000000..a621a6669d --- /dev/null +++ b/cmd/nitro/init_test.go @@ -0,0 +1,142 @@ +// Copyright 2021-2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package main + +import ( + "bytes" + "context" + "crypto/sha256" + "encoding/hex" + "errors" + "fmt" + "net" + "net/http" + "os" + "testing" + "time" + + "github.com/offchainlabs/nitro/cmd/conf" + "github.com/offchainlabs/nitro/util/testhelpers" +) + +func TestDownloadInit(t *testing.T) { + const ( + archiveName = "random_data.tar.gz" + dataSize = 1024 * 1024 + filePerm = 0600 + ) + + // Create archive with random data + serverDir := t.TempDir() + data := testhelpers.RandomSlice(dataSize) + checksumBytes := sha256.Sum256(data) + checksum := hex.EncodeToString(checksumBytes[:]) + + // Write archive file + archiveFile := fmt.Sprintf("%s/%s", serverDir, archiveName) + err := os.WriteFile(archiveFile, data, filePerm) + Require(t, err, "failed to write archive") + + // Write checksum file + checksumFile := archiveFile + ".sha256" + err = os.WriteFile(checksumFile, []byte(checksum), filePerm) + Require(t, err, "failed to write checksum") + + // Start HTTP server + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + addr := startFileServer(t, ctx, serverDir) + + // Download file + initConfig := conf.InitConfigDefault + initConfig.Url = fmt.Sprintf("http://%s/%s", addr, archiveName) + initConfig.DownloadPath = t.TempDir() + receivedArchive, err := downloadInit(ctx, &initConfig) + Require(t, err, "failed to download") + + // Check archive contents + receivedData, err := os.ReadFile(receivedArchive) + Require(t, err, "failed to read received archive") + if !bytes.Equal(receivedData, data) { + t.Error("downloaded archive is different from generated one") + } +} + +func TestDownloadInitInParts(t *testing.T) { + const ( + archiveName = "random_data.tar.gz" + numParts = 3 + partSize = 1024 * 1024 + dataSize = numParts * partSize + filePerm = 0600 + ) + + // Create parts with random data + serverDir := t.TempDir() + data := testhelpers.RandomSlice(dataSize) + for i := 0; i < numParts; i++ { + // Create part and checksum + partData := data[partSize*i : partSize*(i+1)] + checksumBytes := sha256.Sum256(partData) + checksum := hex.EncodeToString(checksumBytes[:]) + // Write part file + partFile := fmt.Sprintf("%s/%s.part%d", serverDir, archiveName, i) + err := os.WriteFile(partFile, partData, filePerm) + Require(t, err, "failed to write part") + // Write checksum file + checksumFile := partFile + ".sha256" + err = os.WriteFile(checksumFile, []byte(checksum), filePerm) + Require(t, err, "failed to write checksum") + } + + // Start HTTP server + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + addr := startFileServer(t, ctx, serverDir) + + // Download file + initConfig := conf.InitConfigDefault + initConfig.Url = fmt.Sprintf("http://%s/%s", addr, archiveName) + initConfig.DownloadPath = t.TempDir() + receivedArchive, err := downloadInit(ctx, &initConfig) + Require(t, err, "failed to download") + + // check database contents + receivedData, err := os.ReadFile(receivedArchive) + Require(t, err, "failed to read received archive") + if !bytes.Equal(receivedData, data) { + t.Error("downloaded archive is different from generated one") + } + + // Check if the function deleted the temporary files + entries, err := os.ReadDir(initConfig.DownloadPath) + Require(t, err, "failed to read temp dir") + if len(entries) != 1 { + t.Error("download function did not delete temp files") + } +} + +func startFileServer(t *testing.T, ctx context.Context, dir string) string { + t.Helper() + ln, err := net.Listen("tcp", "127.0.0.1:0") + Require(t, err, "failed to listen") + addr := ln.Addr().String() + server := &http.Server{ + Addr: addr, + Handler: http.FileServer(http.Dir(dir)), + ReadHeaderTimeout: time.Second, + } + go func() { + err := server.Serve(ln) + if err != nil && !errors.Is(err, http.ErrServerClosed) { + t.Error("failed to shutdown server") + } + }() + go func() { + <-ctx.Done() + err := server.Shutdown(ctx) + Require(t, err, "failed to shutdown server") + }() + return addr +} diff --git a/cmd/nitro/nitro.go b/cmd/nitro/nitro.go index cbd1a0eb93..cdedf67a2e 100644 --- a/cmd/nitro/nitro.go +++ b/cmd/nitro/nitro.go @@ -6,6 +6,7 @@ package main import ( "context" "crypto/ecdsa" + "encoding/hex" "errors" "fmt" "io" @@ -44,7 +45,7 @@ import ( "github.com/offchainlabs/nitro/arbnode" "github.com/offchainlabs/nitro/arbnode/resourcemanager" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/arbutil" blocksreexecutor "github.com/offchainlabs/nitro/blocks_reexecutor" "github.com/offchainlabs/nitro/cmd/chaininfo" @@ -53,7 +54,7 @@ import ( "github.com/offchainlabs/nitro/cmd/util" "github.com/offchainlabs/nitro/cmd/util/confighelpers" "github.com/offchainlabs/nitro/execution/gethexec" - _ "github.com/offchainlabs/nitro/nodeInterface" + _ "github.com/offchainlabs/nitro/execution/nodeInterface" "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/solgen/go/rollupgen" @@ -209,7 +210,7 @@ func mainImpl() int { } stackConf.JWTSecret = filename } - err = genericconf.InitLog(nodeConfig.LogType, log.Lvl(nodeConfig.LogLevel), &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) + err = genericconf.InitLog(nodeConfig.LogType, nodeConfig.LogLevel, &nodeConfig.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)) if err != nil { fmt.Fprintf(os.Stderr, "Error initializing logging: %v\n", err) return 1 @@ -326,7 +327,7 @@ func mainImpl() int { var rollupAddrs chaininfo.RollupAddresses var l1Client *ethclient.Client var l1Reader *headerreader.HeaderReader - var blobReader arbstate.BlobReader + var blobReader daprovider.BlobReader if nodeConfig.Node.ParentChainReader.Enable { confFetcher := func() *rpcclient.ClientConfig { return &liveNodeConfig.Get().ParentChain.Connection } rpcClient := rpcclient.NewRpcClient(confFetcher, nil) @@ -454,7 +455,21 @@ func mainImpl() int { if len(allowedWasmModuleRoots) > 0 { moduleRootMatched := false for _, root := range allowedWasmModuleRoots { - if common.HexToHash(root) == moduleRoot { + bytes, err := hex.DecodeString(strings.TrimPrefix(root, "0x")) + if err == nil { + if common.HexToHash(root) == common.BytesToHash(bytes) { + moduleRootMatched = true + break + } + continue + } + locator, locatorErr := server_common.NewMachineLocator(root) + if locatorErr != nil { + log.Warn("allowed-wasm-module-roots: value not a hex nor valid path:", "value", root, "locatorErr", locatorErr, "decodeErr", err) + continue + } + path := locator.GetMachinePath(moduleRoot) + if _, err := os.Stat(path); err == nil { moduleRootMatched = true break } @@ -482,7 +497,7 @@ func mainImpl() int { tracer = tracers.NewFirehoseLogger() } - chainDb, l2BlockChain, err := openInitializeChainDb(ctx, stack, nodeConfig, new(big.Int).SetUint64(nodeConfig.Chain.ID), gethexec.DefaultCacheConfigFor(stack, &nodeConfig.Execution.Caching), l1Client, rollupAddrs, tracer) + chainDb, l2BlockChain, err := openInitializeChainDb(ctx, stack, nodeConfig, new(big.Int).SetUint64(nodeConfig.Chain.ID), gethexec.DefaultCacheConfigFor(stack, &nodeConfig.Execution.Caching), &nodeConfig.Persistent, l1Client, rollupAddrs, tracer) if l2BlockChain != nil { deferFuncs = append(deferFuncs, func() { l2BlockChain.Stop() }) } @@ -493,13 +508,32 @@ func mainImpl() int { return 1 } - arbDb, err := stack.OpenDatabase("arbitrumdata", 0, 0, "", false) + arbDb, err := stack.OpenDatabaseWithExtraOptions("arbitrumdata", 0, 0, "arbitrumdata/", false, nodeConfig.Persistent.Pebble.ExtraOptions("arbitrumdata")) deferFuncs = append(deferFuncs, func() { closeDb(arbDb, "arbDb") }) if err != nil { log.Error("failed to open database", "err", err) return 1 } + fatalErrChan := make(chan error, 10) + + var blocksReExecutor *blocksreexecutor.BlocksReExecutor + if nodeConfig.BlocksReExecutor.Enable && l2BlockChain != nil { + blocksReExecutor = blocksreexecutor.New(&nodeConfig.BlocksReExecutor, l2BlockChain, fatalErrChan) + if nodeConfig.Init.ThenQuit { + success := make(chan struct{}) + blocksReExecutor.Start(ctx, success) + deferFuncs = append(deferFuncs, func() { blocksReExecutor.StopAndWait() }) + select { + case err := <-fatalErrChan: + log.Error("shutting down due to fatal error", "err", err) + defer log.Error("shut down due to fatal error", "err", err) + return 1 + case <-success: + } + } + } + if nodeConfig.Init.ThenQuit && nodeConfig.Init.ResetToMessage < 0 { return 0 } @@ -520,8 +554,6 @@ func mainImpl() int { return 1 } - fatalErrChan := make(chan error, 10) - var valNode *valnode.ValidationNode if sameProcessValidationNodeEnabled { valNode, err = valnode.CreateValidationNode( @@ -606,7 +638,7 @@ func mainImpl() int { } liveNodeConfig.SetOnReloadHook(func(oldCfg *NodeConfig, newCfg *NodeConfig) error { - if err := genericconf.InitLog(newCfg.LogType, log.Lvl(newCfg.LogLevel), &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)); err != nil { + if err := genericconf.InitLog(newCfg.LogType, newCfg.LogLevel, &newCfg.FileLogging, pathResolver(nodeConfig.Persistent.LogDir)); err != nil { return fmt.Errorf("failed to re-init logging: %w", err) } return currentNode.OnConfigReload(&oldCfg.Node, &newCfg.Node) @@ -651,9 +683,8 @@ func mainImpl() int { // remove previous deferFuncs, StopAndWait closes database and blockchain. deferFuncs = []func(){func() { currentNode.StopAndWait() }} } - if nodeConfig.BlocksReExecutor.Enable && l2BlockChain != nil { - blocksReExecutor := blocksreexecutor.New(&nodeConfig.BlocksReExecutor, l2BlockChain, fatalErrChan) - blocksReExecutor.Start(ctx) + if blocksReExecutor != nil && !nodeConfig.Init.ThenQuit { + blocksReExecutor.Start(ctx, nil) deferFuncs = append(deferFuncs, func() { blocksReExecutor.StopAndWait() }) } @@ -697,7 +728,7 @@ type NodeConfig struct { Validation valnode.Config `koanf:"validation" reload:"hot"` ParentChain conf.ParentChainConfig `koanf:"parent-chain" reload:"hot"` Chain conf.L2Config `koanf:"chain"` - LogLevel int `koanf:"log-level" reload:"hot"` + LogLevel string `koanf:"log-level" reload:"hot"` LogType string `koanf:"log-type" reload:"hot"` FileLogging genericconf.FileLoggingConfig `koanf:"file-logging" reload:"hot"` Persistent conf.PersistentConfig `koanf:"persistent"` @@ -723,7 +754,7 @@ var NodeConfigDefault = NodeConfig{ Validation: valnode.DefaultValidationConfig, ParentChain: conf.L1ConfigDefault, Chain: conf.L2ConfigDefault, - LogLevel: int(log.LvlInfo), + LogLevel: "INFO", LogType: "plaintext", FileLogging: genericconf.DefaultFileLoggingConfig, Persistent: conf.PersistentConfigDefault, @@ -749,7 +780,7 @@ func NodeConfigAddOptions(f *flag.FlagSet) { valnode.ValidationConfigAddOptions("validation", f) conf.L1ConfigAddOptions("parent-chain", f) conf.L2ConfigAddOptions("chain", f) - f.Int("log-level", NodeConfigDefault.LogLevel, "log level") + f.String("log-level", NodeConfigDefault.LogLevel, "log level, valid values are CRIT, ERROR, WARN, INFO, DEBUG, TRACE") f.String("log-type", NodeConfigDefault.LogType, "log type (plaintext or json)") genericconf.FileLoggingConfigAddOptions("file-logging", f) conf.PersistentConfigAddOptions("persistent", f) diff --git a/cmd/pruning/pruning.go b/cmd/pruning/pruning.go index da015ac52c..3af728e5e2 100644 --- a/cmd/pruning/pruning.go +++ b/cmd/pruning/pruning.go @@ -80,12 +80,12 @@ func (r *importantRoots) addHeader(header *types.Header, overwrite bool) error { var hashListRegex = regexp.MustCompile("^(0x)?[0-9a-fA-F]{64}(,(0x)?[0-9a-fA-F]{64})*$") // Finds important roots to retain while proving -func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node.Node, initConfig *conf.InitConfig, cacheConfig *core.CacheConfig, l1Client arbutil.L1Interface, rollupAddrs chaininfo.RollupAddresses, validatorRequired bool) ([]common.Hash, error) { +func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node.Node, initConfig *conf.InitConfig, cacheConfig *core.CacheConfig, persistentConfig *conf.PersistentConfig, l1Client arbutil.L1Interface, rollupAddrs chaininfo.RollupAddresses, validatorRequired bool) ([]common.Hash, error) { chainConfig := gethexec.TryReadStoredChainConfig(chainDb) if chainConfig == nil { return nil, errors.New("database doesn't have a chain config (was this node initialized?)") } - arbDb, err := stack.OpenDatabase("arbitrumdata", 0, 0, "", true) + arbDb, err := stack.OpenDatabaseWithExtraOptions("arbitrumdata", 0, 0, "arbitrumdata/", true, persistentConfig.Pebble.ExtraOptions("arbitrumdata")) if err != nil { return nil, err } @@ -189,7 +189,7 @@ func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node return nil, fmt.Errorf("failed to get finalized block: %w", err) } l1BlockNum := l1Block.NumberU64() - tracker, err := arbnode.NewInboxTracker(arbDb, nil, nil, nil) + tracker, err := arbnode.NewInboxTracker(arbDb, nil, nil, arbnode.DefaultSnapSyncConfig) if err != nil { return nil, err } @@ -232,11 +232,11 @@ func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node return roots.roots, nil } -func PruneChainDb(ctx context.Context, chainDb ethdb.Database, stack *node.Node, initConfig *conf.InitConfig, cacheConfig *core.CacheConfig, l1Client arbutil.L1Interface, rollupAddrs chaininfo.RollupAddresses, validatorRequired bool) error { +func PruneChainDb(ctx context.Context, chainDb ethdb.Database, stack *node.Node, initConfig *conf.InitConfig, cacheConfig *core.CacheConfig, persistentConfig *conf.PersistentConfig, l1Client arbutil.L1Interface, rollupAddrs chaininfo.RollupAddresses, validatorRequired bool) error { if initConfig.Prune == "" { return pruner.RecoverPruning(stack.InstanceDir(), chainDb) } - root, err := findImportantRoots(ctx, chainDb, stack, initConfig, cacheConfig, l1Client, rollupAddrs, validatorRequired) + root, err := findImportantRoots(ctx, chainDb, stack, initConfig, cacheConfig, persistentConfig, l1Client, rollupAddrs, validatorRequired) if err != nil { return fmt.Errorf("failed to find root to retain for pruning: %w", err) } diff --git a/cmd/relay/relay.go b/cmd/relay/relay.go index 40f4f26eec..6f786f976a 100644 --- a/cmd/relay/relay.go +++ b/cmd/relay/relay.go @@ -6,6 +6,7 @@ package main import ( "context" "fmt" + "io" "os" "os/signal" "syscall" @@ -62,14 +63,18 @@ func startup() error { confighelpers.PrintErrorAndExit(err, printSampleUsage) } - logFormat, err := genericconf.ParseLogType(relayConfig.LogType) + handler, err := genericconf.HandlerFromLogType(relayConfig.LogType, io.Writer(os.Stderr)) if err != nil { flag.Usage() - panic(fmt.Sprintf("Error parsing log type: %v", err)) + return fmt.Errorf("error parsing log type when creating handler: %w", err) } - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, logFormat)) - glogger.Verbosity(log.Lvl(relayConfig.LogLevel)) - log.Root().SetHandler(glogger) + logLevel, err := genericconf.ToSlogLevel(relayConfig.LogLevel) + if err != nil { + confighelpers.PrintErrorAndExit(err, printSampleUsage) + } + glogger := log.NewGlogHandler(handler) + glogger.Verbosity(logLevel) + log.SetDefault(log.NewLogger(glogger)) vcsRevision, _, vcsTime := confighelpers.GetVersion() log.Info("Running Arbitrum nitro relay", "revision", vcsRevision, "vcs.time", vcsTime) diff --git a/cmd/replay/main.go b/cmd/replay/main.go index 89805ab0f4..2bcd136de4 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -9,6 +9,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "io" "os" "github.com/ethereum/go-ethereum/common" @@ -27,6 +28,7 @@ import ( "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/das/dastree" @@ -115,8 +117,8 @@ func (dasReader *PreimageDASReader) HealthCheck(ctx context.Context) error { return nil } -func (dasReader *PreimageDASReader) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { - return arbstate.DiscardImmediately, nil +func (dasReader *PreimageDASReader) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { + return daprovider.DiscardImmediately, nil } type BlobPreimageReader struct { @@ -172,9 +174,10 @@ func main() { wavmio.StubInit() gethhook.RequireHookedGeth() - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlError) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelError) + log.SetDefault(log.NewLogger(glogger)) populateEcdsaCaches() @@ -201,21 +204,21 @@ func main() { if lastBlockHeader != nil { delayedMessagesRead = lastBlockHeader.Nonce.Uint64() } - var dasReader arbstate.DataAvailabilityReader + var dasReader daprovider.DASReader if dasEnabled { dasReader = &PreimageDASReader{} } backend := WavmInbox{} - var keysetValidationMode = arbstate.KeysetPanicIfInvalid + var keysetValidationMode = daprovider.KeysetPanicIfInvalid if backend.GetPositionWithinMessage() > 0 { - keysetValidationMode = arbstate.KeysetDontValidate + keysetValidationMode = daprovider.KeysetDontValidate } - var daProviders []arbstate.DataAvailabilityProvider + var dapReaders []daprovider.Reader if dasReader != nil { - daProviders = append(daProviders, arbstate.NewDAProviderDAS(dasReader)) + dapReaders = append(dapReaders, daprovider.NewReaderForDAS(dasReader)) } - daProviders = append(daProviders, arbstate.NewDAProviderBlobReader(&BlobPreimageReader{})) - inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, daProviders, keysetValidationMode) + dapReaders = append(dapReaders, daprovider.NewReaderForBlobReader(&BlobPreimageReader{})) + inboxMultiplexer := arbstate.NewInboxMultiplexer(backend, delayedMessagesRead, dapReaders, keysetValidationMode) ctx := context.Background() message, err := inboxMultiplexer.Pop(ctx) if err != nil { @@ -273,7 +276,7 @@ func main() { batchFetcher := func(batchNum uint64) ([]byte, error) { return wavmio.ReadInboxMessage(batchNum), nil } - newBlock, _, err = arbos.ProduceBlock(message.Message, message.DelayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, batchFetcher, nil) + newBlock, _, err = arbos.ProduceBlock(message.Message, message.DelayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, batchFetcher, false, nil) if err != nil { panic(err) } diff --git a/cmd/staterecovery/staterecovery.go b/cmd/staterecovery/staterecovery.go index 6390826a91..58ad06ad14 100644 --- a/cmd/staterecovery/staterecovery.go +++ b/cmd/staterecovery/staterecovery.go @@ -31,7 +31,7 @@ func RecreateMissingStates(chainDb ethdb.Database, bc *core.BlockChain, cacheCon return fmt.Errorf("start block parent is missing, parent block number: %d", current-1) } hashConfig := *hashdb.Defaults - hashConfig.CleanCacheSize = cacheConfig.TrieCleanLimit + hashConfig.CleanCacheSize = cacheConfig.TrieCleanLimit * 1024 * 1024 trieConfig := &trie.Config{ Preimages: false, HashDB: &hashConfig, diff --git a/contracts b/contracts index 1cab72ff3d..7a41cd59cd 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 1cab72ff3dfcfe06ceed371a9db7a54a527e3bfb +Subproject commit 7a41cd59cdf2eb01cf31c2351b8d1ff6fbf52178 diff --git a/das/aggregator.go b/das/aggregator.go index 4b4571eb43..d3edd58437 100644 --- a/das/aggregator.go +++ b/das/aggregator.go @@ -17,7 +17,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/blsSignatures" "github.com/offchainlabs/nitro/das/dastree" @@ -37,8 +37,6 @@ var DefaultAggregatorConfig = AggregatorConfig{ Backends: "", } -var BatchToDasFailed = errors.New("unable to batch to DAS") - func AggregatorConfigAddOptions(prefix string, f *flag.FlagSet) { f.Bool(prefix+".enable", DefaultAggregatorConfig.Enable, "enable storage/retrieval of sequencer batch data from a list of RPC endpoints; this should only be used by the batch poster and not in combination with other DAS storage types") f.Int(prefix+".assumed-honest", DefaultAggregatorConfig.AssumedHonest, "Number of assumed honest backends (H). If there are N backends, K=N+1-H valid responses are required to consider an Store request to be successful.") @@ -164,7 +162,7 @@ type storeResponse struct { // constructed, calls to Store(...) will try to verify the passed-in data's signature // is from the batch poster. If the contract details are not provided, then the // signature is not checked, which is useful for testing. -func (a *Aggregator) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*arbstate.DataAvailabilityCertificate, error) { +func (a *Aggregator) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*daprovider.DataAvailabilityCertificate, error) { log.Trace("das.Aggregator.Store", "message", pretty.FirstFewBytes(message), "timeout", time.Unix(int64(timeout), 0), "sig", pretty.FirstFewBytes(sig)) if a.addrVerifier != nil { actualSigner, err := DasRecoverSigner(message, timeout, sig) @@ -243,7 +241,7 @@ func (a *Aggregator) Store(ctx context.Context, message []byte, timeout uint64, }(ctx, d) } - var aggCert arbstate.DataAvailabilityCertificate + var aggCert daprovider.DataAvailabilityCertificate type certDetails struct { pubKeys []blsSignatures.PublicKey @@ -296,7 +294,7 @@ func (a *Aggregator) Store(ctx context.Context, message []byte, timeout uint64, } } else if storeFailures > a.maxAllowedServiceStoreFailures { cd := certDetails{} - cd.err = fmt.Errorf("aggregator failed to store message to at least %d out of %d DASes (assuming %d are honest). %w", a.requiredServicesForStore, len(a.services), a.config.AssumedHonest, BatchToDasFailed) + cd.err = fmt.Errorf("aggregator failed to store message to at least %d out of %d DASes (assuming %d are honest). %w", a.requiredServicesForStore, len(a.services), a.config.AssumedHonest, daprovider.ErrBatchToDasFailed) certDetailsChan <- cd returned = true } @@ -323,10 +321,10 @@ func (a *Aggregator) Store(ctx context.Context, message []byte, timeout uint64, verified, err := blsSignatures.VerifySignature(aggCert.Sig, aggCert.SerializeSignableFields(), aggPubKey) if err != nil { //nolint:errorlint - return nil, fmt.Errorf("%s. %w", err.Error(), BatchToDasFailed) + return nil, fmt.Errorf("%s. %w", err.Error(), daprovider.ErrBatchToDasFailed) } if !verified { - return nil, fmt.Errorf("failed aggregate signature check. %w", BatchToDasFailed) + return nil, fmt.Errorf("failed aggregate signature check. %w", daprovider.ErrBatchToDasFailed) } return &aggCert, nil } diff --git a/das/aggregator_test.go b/das/aggregator_test.go index 776af3975b..728db6cf50 100644 --- a/das/aggregator_test.go +++ b/das/aggregator_test.go @@ -8,6 +8,7 @@ import ( "context" "errors" "fmt" + "io" "math/rand" "os" "strconv" @@ -15,10 +16,10 @@ import ( "testing" "time" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/blsSignatures" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" ) func TestDAS_BasicAggregationLocal(t *testing.T) { @@ -122,7 +123,7 @@ type WrapStore struct { DataAvailabilityServiceWriter } -func (w *WrapStore) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*arbstate.DataAvailabilityCertificate, error) { +func (w *WrapStore) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*daprovider.DataAvailabilityCertificate, error) { switch w.injector.shouldFail() { case success: return w.DataAvailabilityServiceWriter.Store(ctx, message, timeout, sig) @@ -158,9 +159,10 @@ func min(a, b int) int { } func enableLogging() { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlTrace) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelTrace) + log.SetDefault(log.NewLogger(glogger)) } func testConfigurableStorageFailures(t *testing.T, shouldFailAggregation bool) { diff --git a/das/cache_storage_service.go b/das/cache_storage_service.go index 13bdb189d3..439ccda086 100644 --- a/das/cache_storage_service.go +++ b/das/cache_storage_service.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/das/dastree" "github.com/offchainlabs/nitro/util/pretty" flag "github.com/spf13/pflag" @@ -82,7 +82,7 @@ func (c *CacheStorageService) Close(ctx context.Context) error { return c.baseStorageService.Close(ctx) } -func (c *CacheStorageService) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { +func (c *CacheStorageService) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { return c.baseStorageService.ExpirationPolicy(ctx) } diff --git a/das/chain_fetch_das.go b/das/chain_fetch_das.go index bc8ab5bc19..99311decaa 100644 --- a/das/chain_fetch_das.go +++ b/das/chain_fetch_das.go @@ -8,7 +8,7 @@ import ( "errors" "sync" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/util/pretty" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -38,13 +38,13 @@ func (c *syncedKeysetCache) put(key [32]byte, value []byte) { } type ChainFetchReader struct { - arbstate.DataAvailabilityReader + daprovider.DASReader seqInboxCaller *bridgegen.SequencerInboxCaller seqInboxFilterer *bridgegen.SequencerInboxFilterer keysetCache syncedKeysetCache } -func NewChainFetchReader(inner arbstate.DataAvailabilityReader, l1client arbutil.L1Interface, seqInboxAddr common.Address) (*ChainFetchReader, error) { +func NewChainFetchReader(inner daprovider.DASReader, l1client arbutil.L1Interface, seqInboxAddr common.Address) (*ChainFetchReader, error) { seqInbox, err := bridgegen.NewSequencerInbox(seqInboxAddr, l1client) if err != nil { return nil, err @@ -53,18 +53,18 @@ func NewChainFetchReader(inner arbstate.DataAvailabilityReader, l1client arbutil return NewChainFetchReaderWithSeqInbox(inner, seqInbox) } -func NewChainFetchReaderWithSeqInbox(inner arbstate.DataAvailabilityReader, seqInbox *bridgegen.SequencerInbox) (*ChainFetchReader, error) { +func NewChainFetchReaderWithSeqInbox(inner daprovider.DASReader, seqInbox *bridgegen.SequencerInbox) (*ChainFetchReader, error) { return &ChainFetchReader{ - DataAvailabilityReader: inner, - seqInboxCaller: &seqInbox.SequencerInboxCaller, - seqInboxFilterer: &seqInbox.SequencerInboxFilterer, - keysetCache: syncedKeysetCache{cache: make(map[[32]byte][]byte)}, + DASReader: inner, + seqInboxCaller: &seqInbox.SequencerInboxCaller, + seqInboxFilterer: &seqInbox.SequencerInboxFilterer, + keysetCache: syncedKeysetCache{cache: make(map[[32]byte][]byte)}, }, nil } func (c *ChainFetchReader) GetByHash(ctx context.Context, hash common.Hash) ([]byte, error) { log.Trace("das.ChainFetchReader.GetByHash", "hash", pretty.PrettyHash(hash)) - return chainFetchGetByHash(ctx, c.DataAvailabilityReader, &c.keysetCache, c.seqInboxCaller, c.seqInboxFilterer, hash) + return chainFetchGetByHash(ctx, c.DASReader, &c.keysetCache, c.seqInboxCaller, c.seqInboxFilterer, hash) } func (c *ChainFetchReader) String() string { return "ChainFetchReader" @@ -72,7 +72,7 @@ func (c *ChainFetchReader) String() string { func chainFetchGetByHash( ctx context.Context, - daReader arbstate.DataAvailabilityReader, + daReader daprovider.DASReader, cache *syncedKeysetCache, seqInboxCaller *bridgegen.SequencerInboxCaller, seqInboxFilterer *bridgegen.SequencerInboxFilterer, diff --git a/das/das.go b/das/das.go index dd8e43a34d..b0708e3b33 100644 --- a/das/das.go +++ b/das/das.go @@ -5,7 +5,6 @@ package das import ( "context" - "encoding/binary" "errors" "fmt" "math" @@ -16,18 +15,17 @@ import ( "github.com/ethereum/go-ethereum/log" flag "github.com/spf13/pflag" - "github.com/offchainlabs/nitro/arbstate" - "github.com/offchainlabs/nitro/blsSignatures" + "github.com/offchainlabs/nitro/arbstate/daprovider" ) type DataAvailabilityServiceWriter interface { // Store requests that the message be stored until timeout (UTC time in unix epoch seconds). - Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*arbstate.DataAvailabilityCertificate, error) + Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*daprovider.DataAvailabilityCertificate, error) fmt.Stringer } type DataAvailabilityServiceReader interface { - arbstate.DataAvailabilityReader + daprovider.DASReader fmt.Stringer } @@ -138,25 +136,6 @@ func dataAvailabilityConfigAddOptions(prefix string, f *flag.FlagSet, r role) { f.String(prefix+".sequencer-inbox-address", DefaultDataAvailabilityConfig.SequencerInboxAddress, "parent chain address of SequencerInbox contract") } -func Serialize(c *arbstate.DataAvailabilityCertificate) []byte { - - flags := arbstate.DASMessageHeaderFlag - if c.Version != 0 { - flags |= arbstate.TreeDASMessageHeaderFlag - } - - buf := make([]byte, 0) - buf = append(buf, flags) - buf = append(buf, c.KeysetHash[:]...) - buf = append(buf, c.SerializeSignableFields()...) - - var intData [8]byte - binary.BigEndian.PutUint64(intData[:], c.SignersMask) - buf = append(buf, intData[:]...) - - return append(buf, blsSignatures.SignatureToBytes(c.Sig)...) -} - func GetL1Client(ctx context.Context, maxConnectionAttempts int, l1URL string) (*ethclient.Client, error) { if maxConnectionAttempts <= 0 { maxConnectionAttempts = math.MaxInt diff --git a/das/dasRpcClient.go b/das/dasRpcClient.go index 54d8eba94c..5fca1e449f 100644 --- a/das/dasRpcClient.go +++ b/das/dasRpcClient.go @@ -13,7 +13,7 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/blsSignatures" "github.com/offchainlabs/nitro/util/pretty" ) @@ -34,7 +34,7 @@ func NewDASRPCClient(target string) (*DASRPCClient, error) { }, nil } -func (c *DASRPCClient) Store(ctx context.Context, message []byte, timeout uint64, reqSig []byte) (*arbstate.DataAvailabilityCertificate, error) { +func (c *DASRPCClient) Store(ctx context.Context, message []byte, timeout uint64, reqSig []byte) (*daprovider.DataAvailabilityCertificate, error) { log.Trace("das.DASRPCClient.Store(...)", "message", pretty.FirstFewBytes(message), "timeout", time.Unix(int64(timeout), 0), "sig", pretty.FirstFewBytes(reqSig), "this", *c) var ret StoreResult if err := c.clnt.CallContext(ctx, &ret, "das_store", hexutil.Bytes(message), hexutil.Uint64(timeout), hexutil.Bytes(reqSig)); err != nil { @@ -44,7 +44,7 @@ func (c *DASRPCClient) Store(ctx context.Context, message []byte, timeout uint64 if err != nil { return nil, err } - return &arbstate.DataAvailabilityCertificate{ + return &daprovider.DataAvailabilityCertificate{ DataHash: common.BytesToHash(ret.DataHash), Timeout: uint64(ret.Timeout), SignersMask: uint64(ret.SignersMask), @@ -62,11 +62,11 @@ func (c *DASRPCClient) HealthCheck(ctx context.Context) error { return c.clnt.CallContext(ctx, nil, "das_healthCheck") } -func (c *DASRPCClient) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { +func (c *DASRPCClient) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { var res string err := c.clnt.CallContext(ctx, &res, "das_expirationPolicy") if err != nil { return -1, err } - return arbstate.StringToExpirationPolicy(res) + return daprovider.StringToExpirationPolicy(res) } diff --git a/das/dasRpcServer.go b/das/dasRpcServer.go index 2f1fc1fd42..03f755b90e 100644 --- a/das/dasRpcServer.go +++ b/das/dasRpcServer.go @@ -36,19 +36,22 @@ type DASRPCServer struct { daHealthChecker DataAvailabilityServiceHealthChecker } -func StartDASRPCServer(ctx context.Context, addr string, portNum uint64, rpcServerTimeouts genericconf.HTTPServerTimeoutConfig, daReader DataAvailabilityServiceReader, daWriter DataAvailabilityServiceWriter, daHealthChecker DataAvailabilityServiceHealthChecker) (*http.Server, error) { +func StartDASRPCServer(ctx context.Context, addr string, portNum uint64, rpcServerTimeouts genericconf.HTTPServerTimeoutConfig, rpcServerBodyLimit int, daReader DataAvailabilityServiceReader, daWriter DataAvailabilityServiceWriter, daHealthChecker DataAvailabilityServiceHealthChecker) (*http.Server, error) { listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, portNum)) if err != nil { return nil, err } - return StartDASRPCServerOnListener(ctx, listener, rpcServerTimeouts, daReader, daWriter, daHealthChecker) + return StartDASRPCServerOnListener(ctx, listener, rpcServerTimeouts, rpcServerBodyLimit, daReader, daWriter, daHealthChecker) } -func StartDASRPCServerOnListener(ctx context.Context, listener net.Listener, rpcServerTimeouts genericconf.HTTPServerTimeoutConfig, daReader DataAvailabilityServiceReader, daWriter DataAvailabilityServiceWriter, daHealthChecker DataAvailabilityServiceHealthChecker) (*http.Server, error) { +func StartDASRPCServerOnListener(ctx context.Context, listener net.Listener, rpcServerTimeouts genericconf.HTTPServerTimeoutConfig, rpcServerBodyLimit int, daReader DataAvailabilityServiceReader, daWriter DataAvailabilityServiceWriter, daHealthChecker DataAvailabilityServiceHealthChecker) (*http.Server, error) { if daWriter == nil { return nil, errors.New("No writer backend was configured for DAS RPC server. Has the BLS signing key been set up (--data-availability.key.key-dir or --data-availability.key.priv-key options)?") } rpcServer := rpc.NewServer() + if rpcServerBodyLimit > 0 { + rpcServer.SetHTTPBodyLimit(rpcServerBodyLimit) + } err := rpcServer.RegisterName("das", &DASRPCServer{ daReader: daReader, daWriter: daWriter, diff --git a/das/dastree/dastree.go b/das/dastree/dastree.go index bc325a3200..d873f0568d 100644 --- a/das/dastree/dastree.go +++ b/das/dastree/dastree.go @@ -9,6 +9,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/arbmath" ) @@ -26,7 +27,7 @@ type node struct { // RecordHash chunks the preimage into 64kB bins and generates a recursive hash tree, // calling the caller-supplied record function for each hash/preimage pair created in // building the tree structure. -func RecordHash(record func(bytes32, []byte), preimage ...[]byte) bytes32 { +func RecordHash(record func(bytes32, []byte, arbutil.PreimageType), preimage ...[]byte) bytes32 { // Algorithm // 1. split the preimage into 64kB bins and double hash them to produce the tree's leaves // 2. repeatedly hash pairs and their combined length, bubbling up any odd-one's out, to form the root @@ -48,7 +49,7 @@ func RecordHash(record func(bytes32, []byte), preimage ...[]byte) bytes32 { keccord := func(value []byte) bytes32 { hash := crypto.Keccak256Hash(value) - record(hash, value) + record(hash, value, arbutil.Keccak256PreimageType) return hash } prepend := func(before byte, slice []byte) []byte { @@ -94,7 +95,7 @@ func RecordHash(record func(bytes32, []byte), preimage ...[]byte) bytes32 { func Hash(preimage ...[]byte) bytes32 { // Merkelizes without recording anything. All but the validator's DAS will call this - return RecordHash(func(bytes32, []byte) {}, preimage...) + return RecordHash(func(bytes32, []byte, arbutil.PreimageType) {}, preimage...) } func HashBytes(preimage ...[]byte) []byte { diff --git a/das/dastree/dastree_test.go b/das/dastree/dastree_test.go index 33f729f4f3..4d24c9ae98 100644 --- a/das/dastree/dastree_test.go +++ b/das/dastree/dastree_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/crypto" + "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/colors" "github.com/offchainlabs/nitro/util/pretty" "github.com/offchainlabs/nitro/util/testhelpers" @@ -25,7 +26,7 @@ func TestDASTree(t *testing.T) { tests = append(tests, large) } - record := func(key bytes32, value []byte) { + record := func(key bytes32, value []byte, ty arbutil.PreimageType) { colors.PrintGrey("storing ", key, " ", pretty.PrettyBytes(value)) store[key] = value if crypto.Keccak256Hash(value) != key { diff --git a/das/db_storage_service.go b/das/db_storage_service.go index 33d21942b2..5596ff378e 100644 --- a/das/db_storage_service.go +++ b/das/db_storage_service.go @@ -12,7 +12,7 @@ import ( badger "github.com/dgraph-io/badger/v4" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/das/dastree" "github.com/offchainlabs/nitro/util/pretty" "github.com/offchainlabs/nitro/util/stopwaiter" @@ -173,11 +173,11 @@ func (dbs *DBStorageService) Close(ctx context.Context) error { return dbs.stopWaiter.StopAndWait() } -func (dbs *DBStorageService) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { +func (dbs *DBStorageService) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { if dbs.discardAfterTimeout { - return arbstate.DiscardAfterDataTimeout, nil + return daprovider.DiscardAfterDataTimeout, nil } - return arbstate.KeepForever, nil + return daprovider.KeepForever, nil } func (dbs *DBStorageService) String() string { diff --git a/das/extra_signature_checker_test.go b/das/extra_signature_checker_test.go index 88a0969229..2fcfac167d 100644 --- a/das/extra_signature_checker_test.go +++ b/das/extra_signature_checker_test.go @@ -14,7 +14,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/util/signature" ) @@ -22,7 +22,7 @@ type StubSignatureCheckDAS struct { keyDir string } -func (s *StubSignatureCheckDAS) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*arbstate.DataAvailabilityCertificate, error) { +func (s *StubSignatureCheckDAS) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*daprovider.DataAvailabilityCertificate, error) { pubkeyEncoded, err := ioutil.ReadFile(s.keyDir + "/ecdsa.pub") if err != nil { return nil, err @@ -39,8 +39,8 @@ func (s *StubSignatureCheckDAS) Store(ctx context.Context, message []byte, timeo return nil, nil } -func (s *StubSignatureCheckDAS) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { - return arbstate.KeepForever, nil +func (s *StubSignatureCheckDAS) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { + return daprovider.KeepForever, nil } func (s *StubSignatureCheckDAS) GetByHash(ctx context.Context, hash common.Hash) ([]byte, error) { diff --git a/das/fallback_storage_service.go b/das/fallback_storage_service.go index a78b4104e8..49f961da60 100644 --- a/das/fallback_storage_service.go +++ b/das/fallback_storage_service.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/das/dastree" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/pretty" @@ -18,7 +18,7 @@ import ( type FallbackStorageService struct { StorageService - backup arbstate.DataAvailabilityReader + backup daprovider.DASReader backupHealthChecker DataAvailabilityServiceHealthChecker backupRetentionSeconds uint64 ignoreRetentionWriteErrors bool @@ -32,7 +32,7 @@ type FallbackStorageService struct { // a successful GetByHash result from the backup is Put into the primary. func NewFallbackStorageService( primary StorageService, - backup arbstate.DataAvailabilityReader, + backup daprovider.DASReader, backupHealthChecker DataAvailabilityServiceHealthChecker, backupRetentionSeconds uint64, // how long to retain data that we copy in from the backup (MaxUint64 means forever) ignoreRetentionWriteErrors bool, // if true, don't return error if write of retention data to primary fails diff --git a/das/ipfs_storage_service.go b/das/ipfs_storage_service.bkup_go similarity index 96% rename from das/ipfs_storage_service.go rename to das/ipfs_storage_service.bkup_go index 4f73242c22..43b06fd4b6 100644 --- a/das/ipfs_storage_service.go +++ b/das/ipfs_storage_service.bkup_go @@ -5,6 +5,9 @@ // It takes advantage of IPFS' content addressing scheme to be able to directly retrieve // the batches from IPFS using their root hash from the L1 sequencer inbox contract. +//go:build ipfs +// +build ipfs + package das import ( @@ -22,7 +25,8 @@ import ( "github.com/ipfs/interface-go-ipfs-core/options" "github.com/ipfs/interface-go-ipfs-core/path" "github.com/multiformats/go-multihash" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" + "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/cmd/ipfshelper" "github.com/offchainlabs/nitro/das/dastree" "github.com/offchainlabs/nitro/util/pretty" @@ -180,7 +184,7 @@ func (s *IpfsStorageService) Put(ctx context.Context, data []byte, timeout uint6 var chunks [][]byte - record := func(_ common.Hash, value []byte) { + record := func(_ common.Hash, value []byte, ty arbutil.PreimageType) { chunks = append(chunks, value) } @@ -219,8 +223,8 @@ func (s *IpfsStorageService) Put(ctx context.Context, data []byte, timeout uint6 panic("unreachable") } -func (s *IpfsStorageService) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { - return arbstate.KeepForever, nil +func (s *IpfsStorageService) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { + return daprovider.KeepForever, nil } func (s *IpfsStorageService) Sync(ctx context.Context) error { diff --git a/das/ipfs_storage_service_stub.go b/das/ipfs_storage_service_stub.go new file mode 100644 index 0000000000..5814f2c7e4 --- /dev/null +++ b/das/ipfs_storage_service_stub.go @@ -0,0 +1,68 @@ +// Copyright 2022, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +// IPFS DAS backend stub +// a stub. we don't currently support ipfs + +//go:build !ipfs +// +build !ipfs + +package das + +import ( + "context" + "errors" + + "github.com/ethereum/go-ethereum/common" + "github.com/offchainlabs/nitro/arbstate/daprovider" + flag "github.com/spf13/pflag" +) + +var ErrIpfsNotSupported = errors.New("ipfs not supported") + +type IpfsStorageServiceConfig struct { + Enable bool +} + +var DefaultIpfsStorageServiceConfig = IpfsStorageServiceConfig{ + Enable: false, +} + +func IpfsStorageServiceConfigAddOptions(prefix string, f *flag.FlagSet) { + f.Bool(prefix+".enable", DefaultIpfsStorageServiceConfig.Enable, "legacy option - not supported") +} + +type IpfsStorageService struct { +} + +func NewIpfsStorageService(ctx context.Context, config IpfsStorageServiceConfig) (*IpfsStorageService, error) { + return nil, ErrIpfsNotSupported +} + +func (s *IpfsStorageService) GetByHash(ctx context.Context, hash common.Hash) ([]byte, error) { + return nil, ErrIpfsNotSupported +} + +func (s *IpfsStorageService) Put(ctx context.Context, data []byte, timeout uint64) error { + return ErrIpfsNotSupported +} + +func (s *IpfsStorageService) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { + return daprovider.KeepForever, ErrIpfsNotSupported +} + +func (s *IpfsStorageService) Sync(ctx context.Context) error { + return ErrIpfsNotSupported +} + +func (s *IpfsStorageService) Close(ctx context.Context) error { + return ErrIpfsNotSupported +} + +func (s *IpfsStorageService) String() string { + return "IpfsStorageService-not supported" +} + +func (s *IpfsStorageService) HealthCheck(ctx context.Context) error { + return ErrIpfsNotSupported +} diff --git a/das/ipfs_storage_service_test.go b/das/ipfs_storage_service_test.go index 54d6705c83..6e1a86b234 100644 --- a/das/ipfs_storage_service_test.go +++ b/das/ipfs_storage_service_test.go @@ -1,6 +1,9 @@ // Copyright 2022, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE +//go:build ipfs +// +build ipfs + package das import ( diff --git a/das/local_file_storage_service.go b/das/local_file_storage_service.go index 5fa5306e39..4ebb1d56d9 100644 --- a/das/local_file_storage_service.go +++ b/das/local_file_storage_service.go @@ -14,7 +14,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/das/dastree" "github.com/offchainlabs/nitro/util/pretty" flag "github.com/spf13/pflag" @@ -130,8 +130,8 @@ func (s *LocalFileStorageService) Close(ctx context.Context) error { return nil } -func (s *LocalFileStorageService) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { - return arbstate.KeepForever, nil +func (s *LocalFileStorageService) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { + return daprovider.KeepForever, nil } func (s *LocalFileStorageService) String() string { diff --git a/das/memory_backed_storage_service.go b/das/memory_backed_storage_service.go index 6484231479..91f7d9a2f5 100644 --- a/das/memory_backed_storage_service.go +++ b/das/memory_backed_storage_service.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/das/dastree" ) @@ -79,8 +79,8 @@ func (m *MemoryBackedStorageService) Close(ctx context.Context) error { return nil } -func (m *MemoryBackedStorageService) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { - return arbstate.KeepForever, nil +func (m *MemoryBackedStorageService) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { + return daprovider.KeepForever, nil } func (m *MemoryBackedStorageService) String() string { diff --git a/das/panic_wrapper.go b/das/panic_wrapper.go index 7a15f6bec0..dbb61cba96 100644 --- a/das/panic_wrapper.go +++ b/das/panic_wrapper.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" ) type WriterPanicWrapper struct { @@ -26,7 +26,7 @@ func (w *WriterPanicWrapper) String() string { return fmt.Sprintf("WriterPanicWrapper{%v}", w.DataAvailabilityServiceWriter) } -func (w *WriterPanicWrapper) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*arbstate.DataAvailabilityCertificate, error) { +func (w *WriterPanicWrapper) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*daprovider.DataAvailabilityCertificate, error) { cert, err := w.DataAvailabilityServiceWriter.Store(ctx, message, timeout, sig) if err != nil { panic(fmt.Sprintf("panic wrapper Store: %v", err)) diff --git a/das/read_limited.go b/das/read_limited.go index 74d6d5358d..5ef0335d5f 100644 --- a/das/read_limited.go +++ b/das/read_limited.go @@ -7,7 +7,7 @@ import ( "context" "fmt" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" ) // These classes are wrappers implementing das.StorageService and das.DataAvailabilityService. @@ -16,12 +16,12 @@ import ( // it is a programming error in the code setting up the node or daserver if a non-writeable object // is used in a writeable context. -func NewReadLimitedStorageService(reader arbstate.DataAvailabilityReader) *readLimitedStorageService { +func NewReadLimitedStorageService(reader daprovider.DASReader) *readLimitedStorageService { return &readLimitedStorageService{reader} } type readLimitedStorageService struct { - arbstate.DataAvailabilityReader + daprovider.DASReader } func (s *readLimitedStorageService) Put(ctx context.Context, data []byte, expiration uint64) error { @@ -37,22 +37,22 @@ func (s *readLimitedStorageService) Close(ctx context.Context) error { } func (s *readLimitedStorageService) String() string { - return fmt.Sprintf("readLimitedStorageService(%v)", s.DataAvailabilityReader) + return fmt.Sprintf("readLimitedStorageService(%v)", s.DASReader) } type readLimitedDataAvailabilityService struct { - arbstate.DataAvailabilityReader + daprovider.DASReader } -func NewReadLimitedDataAvailabilityService(da arbstate.DataAvailabilityReader) *readLimitedDataAvailabilityService { +func NewReadLimitedDataAvailabilityService(da daprovider.DASReader) *readLimitedDataAvailabilityService { return &readLimitedDataAvailabilityService{da} } -func (*readLimitedDataAvailabilityService) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*arbstate.DataAvailabilityCertificate, error) { +func (*readLimitedDataAvailabilityService) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*daprovider.DataAvailabilityCertificate, error) { panic("Logic error: readLimitedDataAvailabilityService.Store shouldn't be called.") } func (s *readLimitedDataAvailabilityService) String() string { - return fmt.Sprintf("ReadLimitedDataAvailabilityService(%v)", s.DataAvailabilityReader) + return fmt.Sprintf("ReadLimitedDataAvailabilityService(%v)", s.DASReader) } diff --git a/das/reader_aggregator_strategies.go b/das/reader_aggregator_strategies.go index 855be5e318..d20760bd5b 100644 --- a/das/reader_aggregator_strategies.go +++ b/das/reader_aggregator_strategies.go @@ -10,30 +10,30 @@ import ( "sync" "sync/atomic" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" ) var ErrNoReadersResponded = errors.New("no DAS readers responded successfully") type aggregatorStrategy interface { newInstance() aggregatorStrategyInstance - update([]arbstate.DataAvailabilityReader, map[arbstate.DataAvailabilityReader]readerStats) + update([]daprovider.DASReader, map[daprovider.DASReader]readerStats) } type abstractAggregatorStrategy struct { sync.RWMutex - readers []arbstate.DataAvailabilityReader - stats map[arbstate.DataAvailabilityReader]readerStats + readers []daprovider.DASReader + stats map[daprovider.DASReader]readerStats } -func (s *abstractAggregatorStrategy) update(readers []arbstate.DataAvailabilityReader, stats map[arbstate.DataAvailabilityReader]readerStats) { +func (s *abstractAggregatorStrategy) update(readers []daprovider.DASReader, stats map[daprovider.DASReader]readerStats) { s.Lock() defer s.Unlock() - s.readers = make([]arbstate.DataAvailabilityReader, len(readers)) + s.readers = make([]daprovider.DASReader, len(readers)) copy(s.readers, readers) - s.stats = make(map[arbstate.DataAvailabilityReader]readerStats) + s.stats = make(map[daprovider.DASReader]readerStats) for k, v := range stats { s.stats[k] = v } @@ -51,11 +51,11 @@ type simpleExploreExploitStrategy struct { func (s *simpleExploreExploitStrategy) newInstance() aggregatorStrategyInstance { iterations := atomic.AddUint32(&s.iterations, 1) - readerSets := make([][]arbstate.DataAvailabilityReader, 0) + readerSets := make([][]daprovider.DASReader, 0) s.RLock() defer s.RUnlock() - readers := make([]arbstate.DataAvailabilityReader, len(s.readers)) + readers := make([]daprovider.DASReader, len(s.readers)) copy(readers, s.readers) if iterations%(s.exploreIterations+s.exploitIterations) < s.exploreIterations { @@ -70,7 +70,7 @@ func (s *simpleExploreExploitStrategy) newInstance() aggregatorStrategyInstance } for i, maxTake := 0, 1; i < len(readers); maxTake = maxTake * 2 { - readerSet := make([]arbstate.DataAvailabilityReader, 0, maxTake) + readerSet := make([]daprovider.DASReader, 0, maxTake) for taken := 0; taken < maxTake && i < len(readers); i, taken = i+1, taken+1 { readerSet = append(readerSet, readers[i]) } @@ -91,7 +91,7 @@ func (s *testingSequentialStrategy) newInstance() aggregatorStrategyInstance { si := basicStrategyInstance{} for _, reader := range s.readers { - si.readerSets = append(si.readerSets, []arbstate.DataAvailabilityReader{reader}) + si.readerSets = append(si.readerSets, []daprovider.DASReader{reader}) } return &si @@ -99,14 +99,14 @@ func (s *testingSequentialStrategy) newInstance() aggregatorStrategyInstance { // Instance of a strategy that returns readers in an order according to the strategy type aggregatorStrategyInstance interface { - nextReaders() []arbstate.DataAvailabilityReader + nextReaders() []daprovider.DASReader } type basicStrategyInstance struct { - readerSets [][]arbstate.DataAvailabilityReader + readerSets [][]daprovider.DASReader } -func (si *basicStrategyInstance) nextReaders() []arbstate.DataAvailabilityReader { +func (si *basicStrategyInstance) nextReaders() []daprovider.DASReader { if len(si.readerSets) == 0 { return nil } diff --git a/das/reader_aggregator_strategies_test.go b/das/reader_aggregator_strategies_test.go index 987bc08938..cdb85b25e9 100644 --- a/das/reader_aggregator_strategies_test.go +++ b/das/reader_aggregator_strategies_test.go @@ -11,7 +11,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" ) type dummyReader struct { @@ -26,13 +26,13 @@ func (*dummyReader) HealthCheck(context.Context) error { return errors.New("not implemented") } -func (*dummyReader) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { +func (*dummyReader) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { return -1, errors.New("not implemented") } func TestDAS_SimpleExploreExploit(t *testing.T) { - readers := []arbstate.DataAvailabilityReader{&dummyReader{0}, &dummyReader{1}, &dummyReader{2}, &dummyReader{3}, &dummyReader{4}, &dummyReader{5}} - stats := make(map[arbstate.DataAvailabilityReader]readerStats) + readers := []daprovider.DASReader{&dummyReader{0}, &dummyReader{1}, &dummyReader{2}, &dummyReader{3}, &dummyReader{4}, &dummyReader{5}} + stats := make(map[daprovider.DASReader]readerStats) stats[readers[0]] = []readerStat{ // weighted avg 10s {10 * time.Second, true}, } @@ -57,7 +57,7 @@ func TestDAS_SimpleExploreExploit(t *testing.T) { {8 * time.Second, true}, } - expectedOrdering := []arbstate.DataAvailabilityReader{readers[1], readers[2], readers[5], readers[4], readers[0], readers[3]} + expectedOrdering := []daprovider.DASReader{readers[1], readers[2], readers[5], readers[4], readers[0], readers[3]} expectedExploreIterations, expectedExploitIterations := uint32(5), uint32(5) strategy := simpleExploreExploitStrategy{ @@ -66,7 +66,7 @@ func TestDAS_SimpleExploreExploit(t *testing.T) { } strategy.update(readers, stats) - checkMatch := func(expected, was []arbstate.DataAvailabilityReader, doMatch bool) { + checkMatch := func(expected, was []daprovider.DASReader, doMatch bool) { if len(expected) != len(was) { Fail(t, fmt.Sprintf("Incorrect number of nextReaders %d, expected %d", len(was), len(expected))) } diff --git a/das/redis_storage_service.go b/das/redis_storage_service.go index 3449a8e78c..dbd85921ed 100644 --- a/das/redis_storage_service.go +++ b/das/redis_storage_service.go @@ -13,7 +13,7 @@ import ( "golang.org/x/crypto/sha3" "github.com/go-redis/redis/v8" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/das/dastree" "github.com/offchainlabs/nitro/util/pretty" "github.com/offchainlabs/nitro/util/redisutil" @@ -162,7 +162,7 @@ func (rs *RedisStorageService) Close(ctx context.Context) error { return rs.baseStorageService.Close(ctx) } -func (rs *RedisStorageService) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { +func (rs *RedisStorageService) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { return rs.baseStorageService.ExpirationPolicy(ctx) } diff --git a/das/redundant_storage_service.go b/das/redundant_storage_service.go index 74d32bd819..3158d28076 100644 --- a/das/redundant_storage_service.go +++ b/das/redundant_storage_service.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/util/pretty" ) @@ -121,7 +121,7 @@ func (r *RedundantStorageService) Close(ctx context.Context) error { return anyError } -func (r *RedundantStorageService) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { +func (r *RedundantStorageService) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { // If at least one inner service has KeepForever, // then whole redundant service can serve after timeout. @@ -132,20 +132,20 @@ func (r *RedundantStorageService) ExpirationPolicy(ctx context.Context) (arbstat // If no inner service has KeepForever, DiscardAfterArchiveTimeout, // but at least one inner service has DiscardAfterDataTimeout, // then whole redundant service can serve till data timeout. - var res arbstate.ExpirationPolicy = -1 + var res daprovider.ExpirationPolicy = -1 for _, serv := range r.innerServices { expirationPolicy, err := serv.ExpirationPolicy(ctx) if err != nil { return -1, err } switch expirationPolicy { - case arbstate.KeepForever: - return arbstate.KeepForever, nil - case arbstate.DiscardAfterArchiveTimeout: - res = arbstate.DiscardAfterArchiveTimeout - case arbstate.DiscardAfterDataTimeout: - if res != arbstate.DiscardAfterArchiveTimeout { - res = arbstate.DiscardAfterDataTimeout + case daprovider.KeepForever: + return daprovider.KeepForever, nil + case daprovider.DiscardAfterArchiveTimeout: + res = daprovider.DiscardAfterArchiveTimeout + case daprovider.DiscardAfterDataTimeout: + if res != daprovider.DiscardAfterArchiveTimeout { + res = daprovider.DiscardAfterDataTimeout } } } diff --git a/das/restful_client.go b/das/restful_client.go index 7d757c6bb8..b65426e7cd 100644 --- a/das/restful_client.go +++ b/das/restful_client.go @@ -14,11 +14,11 @@ import ( "strings" "github.com/ethereum/go-ethereum/common" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/das/dastree" ) -// RestfulDasClient implements DataAvailabilityReader +// RestfulDasClient implements daprovider.DASReader type RestfulDasClient struct { url string } @@ -65,7 +65,7 @@ func (c *RestfulDasClient) GetByHash(ctx context.Context, hash common.Hash) ([]b return nil, err } if !dastree.ValidHash(hash, decodedBytes) { - return nil, arbstate.ErrHashMismatch + return nil, daprovider.ErrHashMismatch } return decodedBytes, nil @@ -82,7 +82,7 @@ func (c *RestfulDasClient) HealthCheck(ctx context.Context) error { return nil } -func (c *RestfulDasClient) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { +func (c *RestfulDasClient) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { res, err := http.Get(c.url + expirationPolicyRequestPath) if err != nil { return -1, err @@ -101,5 +101,5 @@ func (c *RestfulDasClient) ExpirationPolicy(ctx context.Context) (arbstate.Expir return -1, err } - return arbstate.StringToExpirationPolicy(response.ExpirationPolicy) + return daprovider.StringToExpirationPolicy(response.ExpirationPolicy) } diff --git a/das/restful_server.go b/das/restful_server.go index 5c5e82e820..b1607729e2 100644 --- a/das/restful_server.go +++ b/das/restful_server.go @@ -17,7 +17,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/cmd/genericconf" "github.com/offchainlabs/nitro/util/pretty" ) @@ -32,13 +32,13 @@ var ( type RestfulDasServer struct { server *http.Server - daReader arbstate.DataAvailabilityReader + daReader daprovider.DASReader daHealthChecker DataAvailabilityServiceHealthChecker httpServerExitedChan chan interface{} httpServerError error } -func NewRestfulDasServer(address string, port uint64, restServerTimeouts genericconf.HTTPServerTimeoutConfig, daReader arbstate.DataAvailabilityReader, daHealthChecker DataAvailabilityServiceHealthChecker) (*RestfulDasServer, error) { +func NewRestfulDasServer(address string, port uint64, restServerTimeouts genericconf.HTTPServerTimeoutConfig, daReader daprovider.DASReader, daHealthChecker DataAvailabilityServiceHealthChecker) (*RestfulDasServer, error) { listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", address, port)) if err != nil { return nil, err @@ -46,7 +46,7 @@ func NewRestfulDasServer(address string, port uint64, restServerTimeouts generic return NewRestfulDasServerOnListener(listener, restServerTimeouts, daReader, daHealthChecker) } -func NewRestfulDasServerOnListener(listener net.Listener, restServerTimeouts genericconf.HTTPServerTimeoutConfig, daReader arbstate.DataAvailabilityReader, daHealthChecker DataAvailabilityServiceHealthChecker) (*RestfulDasServer, error) { +func NewRestfulDasServerOnListener(listener net.Listener, restServerTimeouts genericconf.HTTPServerTimeoutConfig, daReader daprovider.DASReader, daHealthChecker DataAvailabilityServiceHealthChecker) (*RestfulDasServer, error) { ret := &RestfulDasServer{ daReader: daReader, diff --git a/das/rpc_aggregator.go b/das/rpc_aggregator.go index 134c4229c8..490116a89a 100644 --- a/das/rpc_aggregator.go +++ b/das/rpc_aggregator.go @@ -12,7 +12,7 @@ import ( "math/bits" "net/url" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/blsSignatures" "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/util/metricsutil" @@ -102,7 +102,7 @@ func KeysetHashFromServices(services []ServiceDetails, assumedHonest uint64) ([3 return [32]byte{}, nil, errors.New("at least two signers share a mask") } - keyset := &arbstate.DataAvailabilityKeyset{ + keyset := &daprovider.DataAvailabilityKeyset{ AssumedHonest: uint64(assumedHonest), PubKeys: pubKeys, } diff --git a/das/rpc_test.go b/das/rpc_test.go index 044ba597be..658592cc0b 100644 --- a/das/rpc_test.go +++ b/das/rpc_test.go @@ -55,7 +55,7 @@ func TestRPC(t *testing.T) { testhelpers.RequireImpl(t, err) localDas, err := NewSignAfterStoreDASWriterWithSeqInboxCaller(privKey, nil, storageService, "") testhelpers.RequireImpl(t, err) - dasServer, err := StartDASRPCServerOnListener(ctx, lis, genericconf.HTTPServerTimeoutConfigDefault, storageService, localDas, storageService) + dasServer, err := StartDASRPCServerOnListener(ctx, lis, genericconf.HTTPServerTimeoutConfigDefault, genericconf.HTTPServerBodyLimitDefault, storageService, localDas, storageService) defer func() { if err := dasServer.Shutdown(ctx); err != nil { panic(err) diff --git a/das/s3_storage_service.go b/das/s3_storage_service.go index 1a3ae94114..b5150fb8ed 100644 --- a/das/s3_storage_service.go +++ b/das/s3_storage_service.go @@ -15,7 +15,7 @@ import ( "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/das/dastree" "github.com/offchainlabs/nitro/util/pretty" @@ -145,11 +145,11 @@ func (s3s *S3StorageService) Close(ctx context.Context) error { return nil } -func (s3s *S3StorageService) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { +func (s3s *S3StorageService) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { if s3s.discardAfterTimeout { - return arbstate.DiscardAfterDataTimeout, nil + return daprovider.DiscardAfterDataTimeout, nil } - return arbstate.KeepForever, nil + return daprovider.KeepForever, nil } func (s3s *S3StorageService) String() string { diff --git a/das/sign_after_store_das_writer.go b/das/sign_after_store_das_writer.go index 50c4ee9aee..36c51c022e 100644 --- a/das/sign_after_store_das_writer.go +++ b/das/sign_after_store_das_writer.go @@ -18,7 +18,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/blsSignatures" "github.com/offchainlabs/nitro/das/dastree" "github.com/offchainlabs/nitro/solgen/go/bridgegen" @@ -123,7 +123,7 @@ func NewSignAfterStoreDASWriterWithSeqInboxCaller( return nil, err } - keyset := &arbstate.DataAvailabilityKeyset{ + keyset := &daprovider.DataAvailabilityKeyset{ AssumedHonest: 1, PubKeys: []blsSignatures.PublicKey{publicKey}, } @@ -180,7 +180,7 @@ func NewSignAfterStoreDASWriterWithSeqInboxCaller( func (d *SignAfterStoreDASWriter) Store( ctx context.Context, message []byte, timeout uint64, sig []byte, -) (c *arbstate.DataAvailabilityCertificate, err error) { +) (c *daprovider.DataAvailabilityCertificate, err error) { log.Trace("das.SignAfterStoreDASWriter.Store", "message", pretty.FirstFewBytes(message), "timeout", time.Unix(int64(timeout), 0), "sig", pretty.FirstFewBytes(sig), "this", d) var verified bool if d.extraBpVerifier != nil { @@ -201,7 +201,7 @@ func (d *SignAfterStoreDASWriter) Store( } } - c = &arbstate.DataAvailabilityCertificate{ + c = &daprovider.DataAvailabilityCertificate{ Timeout: timeout, DataHash: dastree.Hash(message), Version: 1, diff --git a/das/simple_das_reader_aggregator.go b/das/simple_das_reader_aggregator.go index eb82a33837..dc6147a7e4 100644 --- a/das/simple_das_reader_aggregator.go +++ b/das/simple_das_reader_aggregator.go @@ -14,7 +14,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/das/dastree" "github.com/offchainlabs/nitro/util/pretty" "github.com/offchainlabs/nitro/util/stopwaiter" @@ -80,7 +80,7 @@ func SimpleExploreExploitStrategyConfigAddOptions(prefix string, f *flag.FlagSet func NewRestfulClientAggregator(ctx context.Context, config *RestfulClientAggregatorConfig) (*SimpleDASReaderAggregator, error) { a := SimpleDASReaderAggregator{ config: config, - stats: make(map[arbstate.DataAvailabilityReader]readerStats), + stats: make(map[daprovider.DASReader]readerStats), } combinedUrls := make(map[string]bool) @@ -160,7 +160,7 @@ type readerStat struct { type readerStatMessage struct { readerStat - reader arbstate.DataAvailabilityReader + reader daprovider.DASReader } type SimpleDASReaderAggregator struct { @@ -170,8 +170,8 @@ type SimpleDASReaderAggregator struct { readersMutex sync.RWMutex // readers and stats are only to be updated by the stats goroutine - readers []arbstate.DataAvailabilityReader - stats map[arbstate.DataAvailabilityReader]readerStats + readers []daprovider.DASReader + stats map[daprovider.DASReader]readerStats strategy aggregatorStrategy @@ -199,7 +199,7 @@ func (a *SimpleDASReaderAggregator) GetByHash(ctx context.Context, hash common.H waitChan := make(chan interface{}) for _, reader := range readers { wg.Add(1) - go func(reader arbstate.DataAvailabilityReader) { + go func(reader daprovider.DASReader) { defer wg.Done() data, err := a.tryGetByHash(subCtx, hash, reader) if err != nil && errors.Is(ctx.Err(), context.Canceled) { @@ -243,7 +243,7 @@ func (a *SimpleDASReaderAggregator) GetByHash(ctx context.Context, hash common.H } func (a *SimpleDASReaderAggregator) tryGetByHash( - ctx context.Context, hash common.Hash, reader arbstate.DataAvailabilityReader, + ctx context.Context, hash common.Hash, reader daprovider.DASReader, ) ([]byte, error) { stat := readerStatMessage{reader: reader} stat.success = false @@ -278,7 +278,7 @@ func (a *SimpleDASReaderAggregator) Start(ctx context.Context) { defer a.readersMutex.Unlock() combinedUrls := a.config.Urls combinedUrls = append(combinedUrls, urls...) - combinedReaders := make(map[arbstate.DataAvailabilityReader]bool) + combinedReaders := make(map[daprovider.DASReader]bool) for _, url := range combinedUrls { reader, err := NewRestfulDasClientFromURL(url) if err != nil { @@ -286,7 +286,7 @@ func (a *SimpleDASReaderAggregator) Start(ctx context.Context) { } combinedReaders[reader] = true } - a.readers = make([]arbstate.DataAvailabilityReader, 0, len(combinedUrls)) + a.readers = make([]daprovider.DASReader, 0, len(combinedUrls)) // Update reader and add newly added stats for reader := range combinedReaders { a.readers = append(a.readers, reader) @@ -350,7 +350,7 @@ func (a *SimpleDASReaderAggregator) HealthCheck(ctx context.Context) error { return nil } -func (a *SimpleDASReaderAggregator) ExpirationPolicy(ctx context.Context) (arbstate.ExpirationPolicy, error) { +func (a *SimpleDASReaderAggregator) ExpirationPolicy(ctx context.Context) (daprovider.ExpirationPolicy, error) { a.readersMutex.RLock() defer a.readersMutex.RUnlock() if len(a.readers) == 0 { @@ -368,7 +368,7 @@ func (a *SimpleDASReaderAggregator) ExpirationPolicy(ctx context.Context) (arbst return -1, err } if ep != expectedExpirationPolicy { - return arbstate.MixedTimeout, nil + return daprovider.MixedTimeout, nil } } return expectedExpirationPolicy, nil diff --git a/das/storage_service.go b/das/storage_service.go index 881d6fc8b1..806e80dba5 100644 --- a/das/storage_service.go +++ b/das/storage_service.go @@ -11,13 +11,13 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" ) var ErrNotFound = errors.New("not found") type StorageService interface { - arbstate.DataAvailabilityReader + daprovider.DASReader Put(ctx context.Context, data []byte, expirationTime uint64) error Sync(ctx context.Context) error Closer diff --git a/das/store_signing.go b/das/store_signing.go index 8039774b65..8ebc1a9805 100644 --- a/das/store_signing.go +++ b/das/store_signing.go @@ -12,7 +12,7 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/das/dastree" "github.com/offchainlabs/nitro/util/pretty" "github.com/offchainlabs/nitro/util/signature" @@ -56,7 +56,7 @@ func NewStoreSigningDAS(inner DataAvailabilityServiceWriter, signer signature.Da return &StoreSigningDAS{inner, signer, addr}, nil } -func (s *StoreSigningDAS) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*arbstate.DataAvailabilityCertificate, error) { +func (s *StoreSigningDAS) Store(ctx context.Context, message []byte, timeout uint64, sig []byte) (*daprovider.DataAvailabilityCertificate, error) { log.Trace("das.StoreSigningDAS.Store(...)", "message", pretty.FirstFewBytes(message), "timeout", time.Unix(int64(timeout), 0), "sig", pretty.FirstFewBytes(sig), "this", s) mySig, err := applyDasSigner(s.signer, message, timeout) if err != nil { diff --git a/das/syncing_fallback_storage.go b/das/syncing_fallback_storage.go index c79cd80400..411e7a1977 100644 --- a/das/syncing_fallback_storage.go +++ b/das/syncing_fallback_storage.go @@ -20,7 +20,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/solgen/go/bridgegen" "github.com/offchainlabs/nitro/util/arbmath" @@ -94,7 +94,7 @@ type l1SyncService struct { config SyncToStorageConfig syncTo StorageService - dataSource arbstate.DataAvailabilityReader + dataSource daprovider.DASReader l1Reader *headerreader.HeaderReader inboxContract *bridgegen.SequencerInbox @@ -161,7 +161,7 @@ func writeSyncState(syncDir string, blockNr uint64) error { return os.Rename(f.Name(), path) } -func newl1SyncService(config *SyncToStorageConfig, syncTo StorageService, dataSource arbstate.DataAvailabilityReader, l1Reader *headerreader.HeaderReader, inboxAddr common.Address) (*l1SyncService, error) { +func newl1SyncService(config *SyncToStorageConfig, syncTo StorageService, dataSource daprovider.DASReader, l1Reader *headerreader.HeaderReader, inboxAddr common.Address) (*l1SyncService, error) { l1Client := l1Reader.Client() inboxContract, err := bridgegen.NewSequencerInbox(inboxAddr, l1Client) if err != nil { @@ -213,9 +213,14 @@ func (s *l1SyncService) processBatchDelivered(ctx context.Context, batchDelivere data = append(header, data...) preimages := make(map[arbutil.PreimageType]map[common.Hash][]byte) - if _, err = arbstate.RecoverPayloadFromDasBatch(ctx, deliveredEvent.BatchSequenceNumber.Uint64(), data, s.dataSource, preimages, arbstate.KeysetValidate); err != nil { - log.Error("recover payload failed", "txhash", batchDeliveredLog.TxHash, "data", data) - return err + preimageRecorder := daprovider.RecordPreimagesTo(preimages) + if _, err = daprovider.RecoverPayloadFromDasBatch(ctx, deliveredEvent.BatchSequenceNumber.Uint64(), data, s.dataSource, preimageRecorder, true); err != nil { + if errors.Is(err, daprovider.ErrSeqMsgValidation) { + log.Error(err.Error()) + } else { + log.Error("recover payload failed", "txhash", batchDeliveredLog.TxHash, "data", data) + return err + } } for _, preimages := range preimages { for hash, contents := range preimages { @@ -291,7 +296,7 @@ func FindDASDataFromLog( log.Warn("BatchDelivered - no data found", "data", data) return nil, nil } - if !arbstate.IsDASMessageHeaderByte(data[0]) { + if !daprovider.IsDASMessageHeaderByte(data[0]) { log.Warn("BatchDelivered - data not DAS") return nil, nil } @@ -417,7 +422,7 @@ type SyncingFallbackStorageService struct { func NewSyncingFallbackStorageService(ctx context.Context, primary StorageService, - backup arbstate.DataAvailabilityReader, + backup daprovider.DASReader, backupHealthChecker DataAvailabilityServiceHealthChecker, l1Reader *headerreader.HeaderReader, inboxAddr common.Address, diff --git a/das/util.go b/das/util.go index d98a2687fe..de266c433f 100644 --- a/das/util.go +++ b/das/util.go @@ -7,11 +7,11 @@ import ( "time" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/offchainlabs/nitro/util/pretty" ) -func logPut(store string, data []byte, timeout uint64, reader arbstate.DataAvailabilityReader, more ...interface{}) { +func logPut(store string, data []byte, timeout uint64, reader daprovider.DASReader, more ...interface{}) { if len(more) == 0 { log.Trace( store, "message", pretty.FirstFewBytes(data), "timeout", time.Unix(int64(timeout), 0), diff --git a/execution/gethexec/arb_interface.go b/execution/gethexec/arb_interface.go index 50d7dfb891..dbf9c24015 100644 --- a/execution/gethexec/arb_interface.go +++ b/execution/gethexec/arb_interface.go @@ -21,30 +21,31 @@ type TransactionPublisher interface { } type ArbInterface struct { - exec *ExecutionEngine + blockchain *core.BlockChain + node *ExecutionNode txPublisher TransactionPublisher - arbNode interface{} } -func NewArbInterface(exec *ExecutionEngine, txPublisher TransactionPublisher) (*ArbInterface, error) { +func NewArbInterface(blockchain *core.BlockChain, txPublisher TransactionPublisher) (*ArbInterface, error) { return &ArbInterface{ - exec: exec, + blockchain: blockchain, txPublisher: txPublisher, }, nil } -func (a *ArbInterface) Initialize(arbnode interface{}) { - a.arbNode = arbnode +func (a *ArbInterface) Initialize(node *ExecutionNode) { + a.node = node } func (a *ArbInterface) PublishTransaction(ctx context.Context, tx *types.Transaction, options *arbitrum_types.ConditionalOptions) error { return a.txPublisher.PublishTransaction(ctx, tx, options) } +// might be used before Initialize func (a *ArbInterface) BlockChain() *core.BlockChain { - return a.exec.bc + return a.blockchain } func (a *ArbInterface) ArbNode() interface{} { - return a.arbNode + return a.node } diff --git a/execution/gethexec/block_recorder.go b/execution/gethexec/block_recorder.go index 147bc49188..e55bd8d9b2 100644 --- a/execution/gethexec/block_recorder.go +++ b/execution/gethexec/block_recorder.go @@ -123,7 +123,7 @@ func (r *BlockRecorder) RecordBlockCreation( var readBatchInfo []validator.BatchInfo if msg != nil { batchFetcher := func(batchNum uint64) ([]byte, error) { - data, blockHash, err := r.execEngine.streamer.FetchBatch(batchNum) + data, blockHash, err := r.execEngine.consensus.FetchBatch(ctx, batchNum) if err != nil { return nil, err } @@ -145,6 +145,7 @@ func (r *BlockRecorder) RecordBlockCreation( chaincontext, chainConfig, batchFetcher, + false, r.execEngine.logger, ) if err != nil { @@ -173,6 +174,7 @@ func (r *BlockRecorder) RecordBlockCreation( BlockHash: blockHash, Preimages: preimages, BatchInfo: readBatchInfo, + UserWasms: recordingdb.UserWasms(), }, err } diff --git a/execution/gethexec/blockchain.go b/execution/gethexec/blockchain.go index b58090ae5f..ea45f8689e 100644 --- a/execution/gethexec/blockchain.go +++ b/execution/gethexec/blockchain.go @@ -37,6 +37,7 @@ type CachingConfig struct { SnapshotRestoreGasLimit uint64 `koanf:"snapshot-restore-gas-limit"` MaxNumberOfBlocksToSkipStateSaving uint32 `koanf:"max-number-of-blocks-to-skip-state-saving"` MaxAmountOfGasToSkipStateSaving uint64 `koanf:"max-amount-of-gas-to-skip-state-saving"` + StylusLRUCache uint32 `koanf:"stylus-lru-cache"` } func CachingConfigAddOptions(prefix string, f *flag.FlagSet) { @@ -51,6 +52,7 @@ func CachingConfigAddOptions(prefix string, f *flag.FlagSet) { f.Uint64(prefix+".snapshot-restore-gas-limit", DefaultCachingConfig.SnapshotRestoreGasLimit, "maximum gas rolled back to recover snapshot") f.Uint32(prefix+".max-number-of-blocks-to-skip-state-saving", DefaultCachingConfig.MaxNumberOfBlocksToSkipStateSaving, "maximum number of blocks to skip state saving to persistent storage (archive node only) -- warning: this option seems to cause issues") f.Uint64(prefix+".max-amount-of-gas-to-skip-state-saving", DefaultCachingConfig.MaxAmountOfGasToSkipStateSaving, "maximum amount of gas in blocks to skip saving state to Persistent storage (archive node only) -- warning: this option seems to cause issues") + f.Uint32(prefix+".stylus-lru-cache", DefaultCachingConfig.StylusLRUCache, "initialized stylus programs to keep in LRU cache") } var DefaultCachingConfig = CachingConfig{ @@ -65,6 +67,22 @@ var DefaultCachingConfig = CachingConfig{ SnapshotRestoreGasLimit: 300_000_000_000, MaxNumberOfBlocksToSkipStateSaving: 0, MaxAmountOfGasToSkipStateSaving: 0, + StylusLRUCache: 256, +} + +var TestCachingConfig = CachingConfig{ + Archive: false, + BlockCount: 128, + BlockAge: 30 * time.Minute, + TrieTimeLimit: time.Hour, + TrieDirtyCache: 1024, + TrieCleanCache: 600, + SnapshotCache: 400, + DatabaseCache: 2048, + SnapshotRestoreGasLimit: 300_000_000_000, + MaxNumberOfBlocksToSkipStateSaving: 0, + MaxAmountOfGasToSkipStateSaving: 0, + StylusLRUCache: 0, } // TODO remove stack from parameters as it is no longer needed here diff --git a/arbnode/classicMessage.go b/execution/gethexec/classicMessage.go similarity index 99% rename from arbnode/classicMessage.go rename to execution/gethexec/classicMessage.go index f03ef5bd45..df749b98b4 100644 --- a/arbnode/classicMessage.go +++ b/execution/gethexec/classicMessage.go @@ -1,7 +1,7 @@ // Copyright 2022, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -package arbnode +package gethexec import ( "encoding/binary" diff --git a/execution/gethexec/executionengine.go b/execution/gethexec/executionengine.go index 31c0b3675b..e02ca750bd 100644 --- a/execution/gethexec/executionengine.go +++ b/execution/gethexec/executionengine.go @@ -1,10 +1,27 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +//go:build !wasm +// +build !wasm + package gethexec +/* +#cgo CFLAGS: -g -Wall -I../../target/include/ +#cgo LDFLAGS: ${SRCDIR}/../../target/lib/libstylus.a -ldl -lm +#include "arbitrator.h" +*/ +import "C" import ( + "bytes" "context" "encoding/binary" "errors" "fmt" + "os" + "path" + "runtime/pprof" + "runtime/trace" "sync" "testing" "time" @@ -15,10 +32,12 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" + "github.com/google/uuid" "github.com/offchainlabs/nitro/arbos" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbos/l1pricing" + "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/util/arbmath" @@ -27,19 +46,20 @@ import ( ) var ( - baseFeeGauge = metrics.NewRegisteredGauge("arb/block/basefee", nil) - blockGasUsedHistogram = metrics.NewRegisteredHistogram("arb/block/gasused", nil, metrics.NewBoundedHistogramSample()) - txCountHistogram = metrics.NewRegisteredHistogram("arb/block/transactions/count", nil, metrics.NewBoundedHistogramSample()) - txGasUsedHistogram = metrics.NewRegisteredHistogram("arb/block/transactions/gasused", nil, metrics.NewBoundedHistogramSample()) + baseFeeGauge = metrics.NewRegisteredGauge("arb/block/basefee", nil) + blockGasUsedHistogram = metrics.NewRegisteredHistogram("arb/block/gasused", nil, metrics.NewBoundedHistogramSample()) + txCountHistogram = metrics.NewRegisteredHistogram("arb/block/transactions/count", nil, metrics.NewBoundedHistogramSample()) + txGasUsedHistogram = metrics.NewRegisteredHistogram("arb/block/transactions/gasused", nil, metrics.NewBoundedHistogramSample()) + gasUsedSinceStartupCounter = metrics.NewRegisteredCounter("arb/gas_used", nil) ) type ExecutionEngine struct { stopwaiter.StopWaiter - logger core.BlockchainLogger - bc *core.BlockChain - streamer execution.TransactionStreamer - recorder *BlockRecorder + logger core.BlockchainLogger + bc *core.BlockChain + consensus execution.FullConsensusClient + recorder *BlockRecorder resequenceChan chan []*arbostypes.MessageWithMetadata createBlocksMutex sync.Mutex @@ -67,6 +87,12 @@ func (s *ExecutionEngine) SetLogger(logger core.BlockchainLogger) { s.logger = logger } +func (n *ExecutionEngine) Initialize(rustCacheSize uint32) { + if rustCacheSize != 0 { + programs.ResizeWasmLruCache(rustCacheSize) + } +} + func (s *ExecutionEngine) SetRecorder(recorder *BlockRecorder) { if s.Started() { panic("trying to set recorder after start") @@ -97,19 +123,23 @@ func (s *ExecutionEngine) EnablePrefetchBlock() { s.prefetchBlock = true } -func (s *ExecutionEngine) SetTransactionStreamer(streamer execution.TransactionStreamer) { +func (s *ExecutionEngine) SetConsensus(consensus execution.FullConsensusClient) { if s.Started() { - panic("trying to set transaction streamer after start") + panic("trying to set transaction consensus after start") } - if s.streamer != nil { - panic("trying to set transaction streamer when already set") + if s.consensus != nil { + panic("trying to set transaction consensus when already set") } - s.streamer = streamer + s.consensus = consensus } -func (s *ExecutionEngine) Reorg(count arbutil.MessageIndex, newMessages []arbostypes.MessageWithMetadata, oldMessages []*arbostypes.MessageWithMetadata) error { +func (s *ExecutionEngine) GetBatchFetcher() execution.BatchFetcher { + return s.consensus +} + +func (s *ExecutionEngine) Reorg(count arbutil.MessageIndex, newMessages []arbostypes.MessageWithMetadataAndBlockHash, oldMessages []*arbostypes.MessageWithMetadata) ([]*execution.MessageResult, error) { if count == 0 { - return errors.New("cannot reorg out genesis") + return nil, errors.New("cannot reorg out genesis") } s.createBlocksMutex.Lock() resequencing := false @@ -125,22 +155,29 @@ func (s *ExecutionEngine) Reorg(count arbutil.MessageIndex, newMessages []arbost targetBlock := s.bc.GetBlockByNumber(uint64(blockNum)) if targetBlock == nil { log.Warn("reorg target block not found", "block", blockNum) - return nil + return nil, nil } + tag := s.bc.StateCache().WasmCacheTag() + // reorg Rust-side VM state + C.stylus_reorg_vm(C.uint64_t(blockNum), C.uint32_t(tag)) + err := s.bc.ReorgToOldBlock(targetBlock) if err != nil { - return err + return nil, err } + + newMessagesResults := make([]*execution.MessageResult, 0, len(oldMessages)) for i := range newMessages { var msgForPrefetch *arbostypes.MessageWithMetadata if i < len(newMessages)-1 { - msgForPrefetch = &newMessages[i] + msgForPrefetch = &newMessages[i].MessageWithMeta } - err := s.digestMessageWithBlockMutex(count+arbutil.MessageIndex(i), &newMessages[i], msgForPrefetch) + msgResult, err := s.digestMessageWithBlockMutex(count+arbutil.MessageIndex(i), &newMessages[i].MessageWithMeta, msgForPrefetch) if err != nil { - return err + return nil, err } + newMessagesResults = append(newMessagesResults, msgResult) } if s.recorder != nil { s.recorder.ReorgTo(targetBlock.Header()) @@ -149,7 +186,7 @@ func (s *ExecutionEngine) Reorg(count arbutil.MessageIndex, newMessages []arbost s.resequenceChan <- oldMessages resequencing = true } - return nil + return newMessagesResults, nil } func (s *ExecutionEngine) getCurrentHeader() (*types.Header, error) { @@ -182,7 +219,7 @@ func (s *ExecutionEngine) NextDelayedMessageNumber() (uint64, error) { return currentHeader.Nonce.Uint64(), nil } -func messageFromTxes(header *arbostypes.L1IncomingMessageHeader, txes types.Transactions, txErrors []error) (*arbostypes.L1IncomingMessage, error) { +func MessageFromTxes(header *arbostypes.L1IncomingMessageHeader, txes types.Transactions, txErrors []error) (*arbostypes.L1IncomingMessage, error) { var l2Message []byte if len(txes) == 1 && txErrors[0] == nil { txBytes, err := txes[0].MarshalBinary() @@ -208,6 +245,9 @@ func messageFromTxes(header *arbostypes.L1IncomingMessageHeader, txes types.Tran l2Message = append(l2Message, txBytes...) } } + if len(l2Message) > arbostypes.MaxL2MessageSize { + return nil, errors.New("l2message too long") + } return &arbostypes.L1IncomingMessage{ Header: header, L2msg: l2Message, @@ -280,7 +320,7 @@ func (s *ExecutionEngine) sequencerWrapper(sequencerFunc func() (*types.Block, e } // We got SequencerInsertLockTaken // option 1: there was a race, we are no longer main sequencer - chosenErr := s.streamer.ExpectChosenSequencer() + chosenErr := s.consensus.ExpectChosenSequencer() if chosenErr != nil { return nil, chosenErr } @@ -305,6 +345,44 @@ func (s *ExecutionEngine) SequenceTransactions(header *arbostypes.L1IncomingMess }) } +// SequenceTransactionsWithProfiling runs SequenceTransactions with tracing and +// CPU profiling enabled. If the block creation takes longer than 2 seconds, it +// keeps both and prints out filenames in an error log line. +func (s *ExecutionEngine) SequenceTransactionsWithProfiling(header *arbostypes.L1IncomingMessageHeader, txes types.Transactions, hooks *arbos.SequencingHooks) (*types.Block, error) { + pprofBuf, traceBuf := bytes.NewBuffer(nil), bytes.NewBuffer(nil) + if err := pprof.StartCPUProfile(pprofBuf); err != nil { + log.Error("Starting CPU profiling", "error", err) + } + if err := trace.Start(traceBuf); err != nil { + log.Error("Starting tracing", "error", err) + } + start := time.Now() + res, err := s.SequenceTransactions(header, txes, hooks) + elapsed := time.Since(start) + pprof.StopCPUProfile() + trace.Stop() + if elapsed > 2*time.Second { + writeAndLog(pprofBuf, traceBuf) + return res, err + } + return res, err +} + +func writeAndLog(pprof, trace *bytes.Buffer) { + id := uuid.NewString() + pprofFile := path.Join(os.TempDir(), id+".pprof") + if err := os.WriteFile(pprofFile, pprof.Bytes(), 0o600); err != nil { + log.Error("Creating temporary file for pprof", "fileName", pprofFile, "error", err) + return + } + traceFile := path.Join(os.TempDir(), id+".trace") + if err := os.WriteFile(traceFile, trace.Bytes(), 0o600); err != nil { + log.Error("Creating temporary file for trace", "fileName", traceFile, "error", err) + return + } + log.Info("Transactions sequencing took longer than 2 seconds, created pprof and trace files", "pprof", pprofFile, "traceFile", traceFile) +} + func (s *ExecutionEngine) sequenceTransactionsWithBlockMutex(header *arbostypes.L1IncomingMessageHeader, txes types.Transactions, hooks *arbos.SequencingHooks) (*types.Block, error) { lastBlockHeader, err := s.getCurrentHeader() if err != nil { @@ -328,6 +406,7 @@ func (s *ExecutionEngine) sequenceTransactionsWithBlockMutex(header *arbostypes. s.bc, s.bc.Config(), hooks, + false, s.logger, ) if err != nil { @@ -353,7 +432,12 @@ func (s *ExecutionEngine) sequenceTransactionsWithBlockMutex(header *arbostypes. return nil, nil } - msg, err := messageFromTxes(header, txes, hooks.TxErrors) + msg, err := MessageFromTxes(header, txes, hooks.TxErrors) + if err != nil { + return nil, err + } + + pos, err := s.BlockNumberToMessageIndex(lastBlockHeader.Number.Uint64() + 1) if err != nil { return nil, err } @@ -362,13 +446,12 @@ func (s *ExecutionEngine) sequenceTransactionsWithBlockMutex(header *arbostypes. Message: msg, DelayedMessagesRead: delayedMessagesRead, } - - pos, err := s.BlockNumberToMessageIndex(lastBlockHeader.Number.Uint64() + 1) + msgResult, err := s.resultFromHeader(block.Header()) if err != nil { return nil, err } - err = s.streamer.WriteMessageFromSequencer(pos, msgWithMeta) + err = s.consensus.WriteMessageFromSequencer(pos, msgWithMeta, *msgResult) if err != nil { return nil, err } @@ -380,6 +463,8 @@ func (s *ExecutionEngine) sequenceTransactionsWithBlockMutex(header *arbostypes. return nil, err } + s.cacheL1PriceDataOfMsg(pos, receipts, block) + return block, nil } @@ -412,18 +497,24 @@ func (s *ExecutionEngine) sequenceDelayedMessageWithBlockMutex(message *arbostyp DelayedMessagesRead: delayedSeqNum + 1, } - err = s.streamer.WriteMessageFromSequencer(lastMsg+1, messageWithMeta) + startTime := time.Now() + block, statedb, receipts, err := s.createBlockFromNextMessage(&messageWithMeta, false, s.logger) if err != nil { return nil, err } + blockCalcTime := time.Since(startTime) - startTime := time.Now() - block, statedb, receipts, err := s.createBlockFromNextMessage(&messageWithMeta, s.logger) + msgResult, err := s.resultFromHeader(block.Header()) if err != nil { return nil, err } - err = s.appendBlock(block, statedb, receipts, time.Since(startTime)) + err = s.consensus.WriteMessageFromSequencer(lastMsg+1, messageWithMeta, *msgResult) + if err != nil { + return nil, err + } + + err = s.appendBlock(block, statedb, receipts, blockCalcTime) if err != nil { return nil, err } @@ -450,7 +541,7 @@ func (s *ExecutionEngine) MessageIndexToBlockNumber(messageNum arbutil.MessageIn } // must hold createBlockMutex -func (s *ExecutionEngine) createBlockFromNextMessage(msg *arbostypes.MessageWithMetadata, logger core.BlockchainLogger) (*types.Block, *state.StateDB, types.Receipts, error) { +func (s *ExecutionEngine) createBlockFromNextMessage(msg *arbostypes.MessageWithMetadata, isMsgForPrefetch bool, logger core.BlockchainLogger) (*types.Block, *state.StateDB, types.Receipts, error) { currentHeader := s.bc.CurrentBlock() if currentHeader == nil { return nil, nil, nil, errors.New("failed to get current block header") @@ -473,6 +564,11 @@ func (s *ExecutionEngine) createBlockFromNextMessage(msg *arbostypes.MessageWith statedb.StartPrefetcher("TransactionStreamer") defer statedb.StopPrefetcher() + batchFetcher := func(num uint64) ([]byte, error) { + data, _, err := s.consensus.FetchBatch(s.GetContext(), num) + return data, err + } + block, receipts, err := arbos.ProduceBlock( msg.Message, msg.DelayedMessagesRead, @@ -480,10 +576,8 @@ func (s *ExecutionEngine) createBlockFromNextMessage(msg *arbostypes.MessageWith statedb, s.bc, s.bc.Config(), - func(batchNum uint64) ([]byte, error) { - data, _, err := s.streamer.FetchBatch(batchNum) - return data, err - }, + batchFetcher, + isMsgForPrefetch, logger, ) @@ -512,6 +606,7 @@ func (s *ExecutionEngine) appendBlock(block *types.Block, statedb *state.StateDB blockGasused += val } blockGasUsedHistogram.Update(int64(blockGasused)) + gasUsedSinceStartupCounter.Inc(int64(blockGasused)) return nil } @@ -530,64 +625,110 @@ func (s *ExecutionEngine) ResultAtPos(pos arbutil.MessageIndex) (*execution.Mess return s.resultFromHeader(s.bc.GetHeaderByNumber(s.MessageIndexToBlockNumber(pos))) } +func (s *ExecutionEngine) GetL1GasPriceEstimate() (uint64, error) { + bc := s.bc + latestHeader := bc.CurrentBlock() + latestState, err := bc.StateAt(latestHeader.Root) + if err != nil { + return 0, errors.New("error getting latest statedb while fetching l2 Estimate of L1 GasPrice") + } + arbState, err := arbosState.OpenSystemArbosState(latestState, nil, true) + if err != nil { + return 0, errors.New("error opening system arbos state while fetching l2 Estimate of L1 GasPrice") + } + l2EstimateL1GasPrice, err := arbState.L1PricingState().PricePerUnit() + if err != nil { + return 0, errors.New("error fetching l2 Estimate of L1 GasPrice") + } + return l2EstimateL1GasPrice.Uint64(), nil +} + +func (s *ExecutionEngine) getL1PricingSurplus() (int64, error) { + bc := s.bc + latestHeader := bc.CurrentBlock() + latestState, err := bc.StateAt(latestHeader.Root) + if err != nil { + return 0, errors.New("error getting latest statedb while fetching current L1 pricing surplus") + } + arbState, err := arbosState.OpenSystemArbosState(latestState, nil, true) + if err != nil { + return 0, errors.New("error opening system arbos state while fetching current L1 pricing surplus") + } + surplus, err := arbState.L1PricingState().GetL1PricingSurplus() + if err != nil { + return 0, errors.New("error fetching current L1 pricing surplus") + } + return surplus.Int64(), nil +} + +func (s *ExecutionEngine) cacheL1PriceDataOfMsg(num arbutil.MessageIndex, receipts types.Receipts, block *types.Block) { + var gasUsedForL1 uint64 + for i := 1; i < len(receipts); i++ { + gasUsedForL1 += receipts[i].GasUsedForL1 + } + gasChargedForL1 := gasUsedForL1 * block.BaseFee().Uint64() + var callDataUnits uint64 + for _, tx := range block.Transactions() { + callDataUnits += tx.CalldataUnits + } + s.consensus.CacheL1PriceDataOfMsg(num, callDataUnits, gasChargedForL1) +} + // DigestMessage is used to create a block by executing msg against the latest state and storing it. // Also, while creating a block by executing msg against the latest state, // in parallel, creates a block by executing msgForPrefetch (msg+1) against the latest state // but does not store the block. // This helps in filling the cache, so that the next block creation is faster. -func (s *ExecutionEngine) DigestMessage(num arbutil.MessageIndex, msg *arbostypes.MessageWithMetadata, msgForPrefetch *arbostypes.MessageWithMetadata) error { +func (s *ExecutionEngine) DigestMessage(num arbutil.MessageIndex, msg *arbostypes.MessageWithMetadata, msgForPrefetch *arbostypes.MessageWithMetadata) (*execution.MessageResult, error) { if !s.createBlocksMutex.TryLock() { - return errors.New("createBlock mutex held") + return nil, errors.New("createBlock mutex held") } defer s.createBlocksMutex.Unlock() return s.digestMessageWithBlockMutex(num, msg, msgForPrefetch) } -func (s *ExecutionEngine) digestMessageWithBlockMutex(num arbutil.MessageIndex, msg *arbostypes.MessageWithMetadata, msgForPrefetch *arbostypes.MessageWithMetadata) error { +func (s *ExecutionEngine) digestMessageWithBlockMutex(num arbutil.MessageIndex, msg *arbostypes.MessageWithMetadata, msgForPrefetch *arbostypes.MessageWithMetadata) (*execution.MessageResult, error) { currentHeader, err := s.getCurrentHeader() if err != nil { - return err + return nil, err } curMsg, err := s.BlockNumberToMessageIndex(currentHeader.Number.Uint64()) if err != nil { - return err + return nil, err } if curMsg+1 != num { - return fmt.Errorf("wrong message number in digest got %d expected %d", num, curMsg+1) + return nil, fmt.Errorf("wrong message number in digest got %d expected %d", num, curMsg+1) } startTime := time.Now() - var wg sync.WaitGroup if s.prefetchBlock && msgForPrefetch != nil { - wg.Add(1) go func() { - defer wg.Done() - _, _, _, err := s.createBlockFromNextMessage(msgForPrefetch, nil) + _, _, _, err := s.createBlockFromNextMessage(msgForPrefetch, true, nil) if err != nil { return } }() } - block, statedb, receipts, err := s.createBlockFromNextMessage(msg, s.logger) + block, statedb, receipts, err := s.createBlockFromNextMessage(msg, false, s.logger) if err != nil { - return err + return nil, err } - wg.Wait() + err = s.appendBlock(block, statedb, receipts, time.Since(startTime)) if err != nil { - return err + return nil, err } if time.Now().After(s.nextScheduledVersionCheck) { s.nextScheduledVersionCheck = time.Now().Add(time.Minute) arbState, err := arbosState.OpenSystemArbosState(statedb, nil, true) if err != nil { - return err + return nil, err } version, timestampInt, err := arbState.GetScheduledUpgrade() if err != nil { - return err + return nil, err } var timeUntilUpgrade time.Duration var timestamp time.Time @@ -623,7 +764,12 @@ func (s *ExecutionEngine) digestMessageWithBlockMutex(num arbutil.MessageIndex, case s.newBlockNotifier <- struct{}{}: default: } - return nil + + msgResult, err := s.resultFromHeader(block.Header()) + if err != nil { + return nil, err + } + return msgResult, nil } func (s *ExecutionEngine) ArbOSVersionForMessageNumber(messageNum arbutil.MessageIndex) (uint64, error) { diff --git a/execution/gethexec/node.go b/execution/gethexec/node.go index 3c835ff674..932fea58f7 100644 --- a/execution/gethexec/node.go +++ b/execution/gethexec/node.go @@ -51,6 +51,7 @@ type Config struct { TxLookupLimit uint64 `koanf:"tx-lookup-limit"` Dangerous DangerousConfig `koanf:"dangerous"` EnablePrefetchBlock bool `koanf:"enable-prefetch-block"` + SyncMonitor SyncMonitorConfig `koanf:"sync-monitor"` forwardingTarget string } @@ -83,6 +84,7 @@ func ConfigAddOptions(prefix string, f *flag.FlagSet) { AddOptionsForNodeForwarderConfig(prefix+".forwarder", f) TxPreCheckerConfigAddOptions(prefix+".tx-pre-checker", f) CachingConfigAddOptions(prefix+".caching", f) + SyncMonitorConfigAddOptions(prefix+".sync-monitor", f) f.Uint64(prefix+".tx-lookup-limit", ConfigDefault.TxLookupLimit, "retain the ability to lookup transactions by hash for the past N blocks (0 = all blocks)") DangerousConfigAddOptions(prefix+".dangerous", f) f.Bool(prefix+".enable-prefetch-block", ConfigDefault.EnablePrefetchBlock, "enable prefetching of blocks") @@ -105,6 +107,7 @@ var ConfigDefault = Config{ func ConfigDefaultNonSequencerTest() *Config { config := ConfigDefault + config.Caching = TestCachingConfig config.ParentChainReader = headerreader.TestConfig config.Sequencer.Enable = false config.Forwarder = DefaultTestForwarderConfig @@ -117,9 +120,10 @@ func ConfigDefaultNonSequencerTest() *Config { func ConfigDefaultTest() *Config { config := ConfigDefault + config.Caching = TestCachingConfig config.Sequencer = TestSequencerConfig - config.ForwardingTarget = "null" config.ParentChainReader = headerreader.TestConfig + config.ForwardingTarget = "null" _ = config.Validate() @@ -138,7 +142,9 @@ type ExecutionNode struct { Sequencer *Sequencer // either nil or same as TxPublisher TxPublisher TransactionPublisher ConfigFetcher ConfigFetcher + SyncMonitor *SyncMonitor ParentChainReader *headerreader.HeaderReader + ClassicOutbox *ClassicOutboxRetriever started atomic.Bool } @@ -173,6 +179,8 @@ func CreateExecutionNode( if err != nil { return nil, err } + } else if config.Sequencer.Enable { + log.Warn("sequencer enabled without l1 client") } if config.Sequencer.Enable { @@ -196,7 +204,7 @@ func CreateExecutionNode( txprecheckConfigFetcher := func() *TxPreCheckerConfig { return &configFetcher().TxPreChecker } txPublisher = NewTxPreChecker(txPublisher, l2BlockChain, txprecheckConfigFetcher) - arbInterface, err := NewArbInterface(execEngine, txPublisher) + arbInterface, err := NewArbInterface(l2BlockChain, txPublisher) if err != nil { return nil, err } @@ -209,6 +217,20 @@ func CreateExecutionNode( return nil, err } + syncMon := NewSyncMonitor(&config.SyncMonitor, execEngine) + + var classicOutbox *ClassicOutboxRetriever + + if l2BlockChain.Config().ArbitrumChainParams.GenesisBlockNum > 0 { + classicMsgDb, err := stack.OpenDatabase("classic-msg", 0, 0, "classicmsg/", true) // TODO can we skip using ExtraOptions here? + if err != nil { + log.Warn("Classic Msg Database not found", "err", err) + classicOutbox = nil + } else { + classicOutbox = NewClassicOutboxRetriever(classicMsgDb) + } + } + apis := []rpc.API{{ Namespace: "arb", Version: "1.0", @@ -252,13 +274,20 @@ func CreateExecutionNode( Sequencer: sequencer, TxPublisher: txPublisher, ConfigFetcher: configFetcher, + SyncMonitor: syncMon, ParentChainReader: parentChainReader, + ClassicOutbox: classicOutbox, }, nil } -func (n *ExecutionNode) Initialize(ctx context.Context, arbnode interface{}, sync arbitrum.SyncProgressBackend) error { - n.ArbInterface.Initialize(arbnode) +func (n *ExecutionNode) GetL1GasPriceEstimate() (uint64, error) { + return n.ExecEngine.GetL1GasPriceEstimate() +} + +func (n *ExecutionNode) Initialize(ctx context.Context) error { + n.ExecEngine.Initialize(n.ConfigFetcher().Caching.StylusLRUCache) + n.ArbInterface.Initialize(n) err := n.Backend.Start() if err != nil { return fmt.Errorf("error starting geth backend: %w", err) @@ -267,7 +296,7 @@ func (n *ExecutionNode) Initialize(ctx context.Context, arbnode interface{}, syn if err != nil { return fmt.Errorf("error initializing transaction publisher: %w", err) } - err = n.Backend.APIBackend().SetSyncBackend(sync) + err = n.Backend.APIBackend().SetSyncBackend(n.SyncMonitor) if err != nil { return fmt.Errorf("error setting sync backend: %w", err) } @@ -321,10 +350,10 @@ func (n *ExecutionNode) StopAndWait() { // } } -func (n *ExecutionNode) DigestMessage(num arbutil.MessageIndex, msg *arbostypes.MessageWithMetadata, msgForPrefetch *arbostypes.MessageWithMetadata) error { +func (n *ExecutionNode) DigestMessage(num arbutil.MessageIndex, msg *arbostypes.MessageWithMetadata, msgForPrefetch *arbostypes.MessageWithMetadata) (*execution.MessageResult, error) { return n.ExecEngine.DigestMessage(num, msg, msgForPrefetch) } -func (n *ExecutionNode) Reorg(count arbutil.MessageIndex, newMessages []arbostypes.MessageWithMetadata, oldMessages []*arbostypes.MessageWithMetadata) error { +func (n *ExecutionNode) Reorg(count arbutil.MessageIndex, newMessages []arbostypes.MessageWithMetadataAndBlockHash, oldMessages []*arbostypes.MessageWithMetadata) ([]*execution.MessageResult, error) { return n.ExecEngine.Reorg(count, newMessages, oldMessages) } func (n *ExecutionNode) HeadMessageNumber() (arbutil.MessageIndex, error) { @@ -365,11 +394,13 @@ func (n *ExecutionNode) Pause() { n.Sequencer.Pause() } } + func (n *ExecutionNode) Activate() { if n.Sequencer != nil { n.Sequencer.Activate() } } + func (n *ExecutionNode) ForwardTo(url string) error { if n.Sequencer != nil { return n.Sequencer.ForwardTo(url) @@ -377,9 +408,12 @@ func (n *ExecutionNode) ForwardTo(url string) error { return errors.New("forwardTo not supported - sequencer not active") } } -func (n *ExecutionNode) SetTransactionStreamer(streamer execution.TransactionStreamer) { - n.ExecEngine.SetTransactionStreamer(streamer) + +func (n *ExecutionNode) SetConsensusClient(consensus execution.FullConsensusClient) { + n.ExecEngine.SetConsensus(consensus) + n.SyncMonitor.SetConsensusInfo(consensus) } + func (n *ExecutionNode) MessageIndexToBlockNumber(messageNum arbutil.MessageIndex) uint64 { return n.ExecEngine.MessageIndexToBlockNumber(messageNum) } diff --git a/execution/gethexec/sequencer.go b/execution/gethexec/sequencer.go index 5db38cbb4d..2ba286aac7 100644 --- a/execution/gethexec/sequencer.go +++ b/execution/gethexec/sequencer.go @@ -10,6 +10,7 @@ import ( "math" "math/big" "runtime/debug" + "strconv" "strings" "sync" "sync/atomic" @@ -25,10 +26,12 @@ import ( "github.com/ethereum/go-ethereum/arbitrum" "github.com/ethereum/go-ethereum/arbitrum_types" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/misc/eip4844" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/txpool" "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" @@ -51,21 +54,31 @@ var ( successfulBlocksCounter = metrics.NewRegisteredCounter("arb/sequencer/block/successful", nil) conditionalTxRejectedBySequencerCounter = metrics.NewRegisteredCounter("arb/sequencer/condtionaltx/rejected", nil) conditionalTxAcceptedBySequencerCounter = metrics.NewRegisteredCounter("arb/sequencer/condtionaltx/accepted", nil) + l1GasPriceGauge = metrics.NewRegisteredGauge("arb/sequencer/l1gasprice", nil) + callDataUnitsBacklogGauge = metrics.NewRegisteredGauge("arb/sequencer/calldataunitsbacklog", nil) + unusedL1GasChargeGauge = metrics.NewRegisteredGauge("arb/sequencer/unusedl1gascharge", nil) + currentSurplusGauge = metrics.NewRegisteredGauge("arb/sequencer/currentsurplus", nil) + expectedSurplusGauge = metrics.NewRegisteredGauge("arb/sequencer/expectedsurplus", nil) ) type SequencerConfig struct { - Enable bool `koanf:"enable"` - MaxBlockSpeed time.Duration `koanf:"max-block-speed" reload:"hot"` - MaxRevertGasReject uint64 `koanf:"max-revert-gas-reject" reload:"hot"` - MaxAcceptableTimestampDelta time.Duration `koanf:"max-acceptable-timestamp-delta" reload:"hot"` - SenderWhitelist string `koanf:"sender-whitelist"` - Forwarder ForwarderConfig `koanf:"forwarder"` - QueueSize int `koanf:"queue-size"` - QueueTimeout time.Duration `koanf:"queue-timeout" reload:"hot"` - NonceCacheSize int `koanf:"nonce-cache-size" reload:"hot"` - MaxTxDataSize int `koanf:"max-tx-data-size" reload:"hot"` - NonceFailureCacheSize int `koanf:"nonce-failure-cache-size" reload:"hot"` - NonceFailureCacheExpiry time.Duration `koanf:"nonce-failure-cache-expiry" reload:"hot"` + Enable bool `koanf:"enable"` + MaxBlockSpeed time.Duration `koanf:"max-block-speed" reload:"hot"` + MaxRevertGasReject uint64 `koanf:"max-revert-gas-reject" reload:"hot"` + MaxAcceptableTimestampDelta time.Duration `koanf:"max-acceptable-timestamp-delta" reload:"hot"` + SenderWhitelist string `koanf:"sender-whitelist"` + Forwarder ForwarderConfig `koanf:"forwarder"` + QueueSize int `koanf:"queue-size"` + QueueTimeout time.Duration `koanf:"queue-timeout" reload:"hot"` + NonceCacheSize int `koanf:"nonce-cache-size" reload:"hot"` + MaxTxDataSize int `koanf:"max-tx-data-size" reload:"hot"` + NonceFailureCacheSize int `koanf:"nonce-failure-cache-size" reload:"hot"` + NonceFailureCacheExpiry time.Duration `koanf:"nonce-failure-cache-expiry" reload:"hot"` + ExpectedSurplusSoftThreshold string `koanf:"expected-surplus-soft-threshold" reload:"hot"` + ExpectedSurplusHardThreshold string `koanf:"expected-surplus-hard-threshold" reload:"hot"` + EnableProfiling bool `koanf:"enable-profiling" reload:"hot"` + expectedSurplusSoftThreshold int + expectedSurplusHardThreshold int } func (c *SequencerConfig) Validate() error { @@ -78,6 +91,23 @@ func (c *SequencerConfig) Validate() error { return fmt.Errorf("sequencer sender whitelist entry \"%v\" is not a valid address", address) } } + var err error + if c.ExpectedSurplusSoftThreshold != "default" { + if c.expectedSurplusSoftThreshold, err = strconv.Atoi(c.ExpectedSurplusSoftThreshold); err != nil { + return fmt.Errorf("invalid expected-surplus-soft-threshold value provided in batchposter config %w", err) + } + } + if c.ExpectedSurplusHardThreshold != "default" { + if c.expectedSurplusHardThreshold, err = strconv.Atoi(c.ExpectedSurplusHardThreshold); err != nil { + return fmt.Errorf("invalid expected-surplus-hard-threshold value provided in batchposter config %w", err) + } + } + if c.expectedSurplusSoftThreshold < c.expectedSurplusHardThreshold { + return errors.New("expected-surplus-soft-threshold cannot be lower than expected-surplus-hard-threshold") + } + if c.MaxTxDataSize > arbostypes.MaxL2MessageSize-50000 { + return errors.New("max-tx-data-size too large for MaxL2MessageSize") + } return nil } @@ -86,7 +116,7 @@ type SequencerConfigFetcher func() *SequencerConfig var DefaultSequencerConfig = SequencerConfig{ Enable: false, MaxBlockSpeed: time.Millisecond * 250, - MaxRevertGasReject: params.TxGas + 10000, + MaxRevertGasReject: 0, MaxAcceptableTimestampDelta: time.Hour, Forwarder: DefaultSequencerForwarderConfig, QueueSize: 1024, @@ -94,24 +124,30 @@ var DefaultSequencerConfig = SequencerConfig{ NonceCacheSize: 1024, // 95% of the default batch poster limit, leaving 5KB for headers and such // This default is overridden for L3 chains in applyChainParameters in cmd/nitro/nitro.go - MaxTxDataSize: 95000, - NonceFailureCacheSize: 1024, - NonceFailureCacheExpiry: time.Second, + MaxTxDataSize: 95000, + NonceFailureCacheSize: 1024, + NonceFailureCacheExpiry: time.Second, + ExpectedSurplusSoftThreshold: "default", + ExpectedSurplusHardThreshold: "default", + EnableProfiling: false, } var TestSequencerConfig = SequencerConfig{ - Enable: true, - MaxBlockSpeed: time.Millisecond * 10, - MaxRevertGasReject: params.TxGas + 10000, - MaxAcceptableTimestampDelta: time.Hour, - SenderWhitelist: "", - Forwarder: DefaultTestForwarderConfig, - QueueSize: 128, - QueueTimeout: time.Second * 5, - NonceCacheSize: 4, - MaxTxDataSize: 95000, - NonceFailureCacheSize: 1024, - NonceFailureCacheExpiry: time.Second, + Enable: true, + MaxBlockSpeed: time.Millisecond * 10, + MaxRevertGasReject: params.TxGas + 10000, + MaxAcceptableTimestampDelta: time.Hour, + SenderWhitelist: "", + Forwarder: DefaultTestForwarderConfig, + QueueSize: 128, + QueueTimeout: time.Second * 5, + NonceCacheSize: 4, + MaxTxDataSize: 95000, + NonceFailureCacheSize: 1024, + NonceFailureCacheExpiry: time.Second, + ExpectedSurplusSoftThreshold: "default", + ExpectedSurplusHardThreshold: "default", + EnableProfiling: false, } func SequencerConfigAddOptions(prefix string, f *flag.FlagSet) { @@ -127,10 +163,14 @@ func SequencerConfigAddOptions(prefix string, f *flag.FlagSet) { f.Int(prefix+".max-tx-data-size", DefaultSequencerConfig.MaxTxDataSize, "maximum transaction size the sequencer will accept") f.Int(prefix+".nonce-failure-cache-size", DefaultSequencerConfig.NonceFailureCacheSize, "number of transactions with too high of a nonce to keep in memory while waiting for their predecessor") f.Duration(prefix+".nonce-failure-cache-expiry", DefaultSequencerConfig.NonceFailureCacheExpiry, "maximum amount of time to wait for a predecessor before rejecting a tx with nonce too high") + f.String(prefix+".expected-surplus-soft-threshold", DefaultSequencerConfig.ExpectedSurplusSoftThreshold, "if expected surplus is lower than this value, warnings are posted") + f.String(prefix+".expected-surplus-hard-threshold", DefaultSequencerConfig.ExpectedSurplusHardThreshold, "if expected surplus is lower than this value, new incoming transactions will be denied") + f.Bool(prefix+".enable-profiling", DefaultSequencerConfig.EnableProfiling, "enable CPU profiling and tracing") } type txQueueItem struct { tx *types.Transaction + txSize int // size in bytes of the marshalled transaction options *arbitrum_types.ConditionalOptions resultChan chan<- error returnedResult bool @@ -291,6 +331,10 @@ type Sequencer struct { activeMutex sync.Mutex pauseChan chan struct{} forwarder *TxForwarder + + expectedSurplusMutex sync.RWMutex + expectedSurplus int64 + expectedSurplusUpdated bool } func NewSequencer(execEngine *ExecutionEngine, l1Reader *headerreader.HeaderReader, configFetcher SequencerConfigFetcher) (*Sequencer, error) { @@ -364,6 +408,17 @@ func ctxWithTimeout(ctx context.Context, timeout time.Duration) (context.Context } func (s *Sequencer) PublishTransaction(parentCtx context.Context, tx *types.Transaction, options *arbitrum_types.ConditionalOptions) error { + config := s.config() + // Only try to acquire Rlock and check for hard threshold if l1reader is not nil + // And hard threshold was enabled, this prevents spamming of read locks when not needed + if s.l1Reader != nil && config.ExpectedSurplusHardThreshold != "default" { + s.expectedSurplusMutex.RLock() + if s.expectedSurplusUpdated && s.expectedSurplus < int64(config.expectedSurplusHardThreshold) { + return errors.New("currently not accepting transactions due to expected surplus being below threshold") + } + s.expectedSurplusMutex.RUnlock() + } + sequencerBacklogGauge.Inc(1) defer sequencerBacklogGauge.Dec(1) @@ -392,7 +447,12 @@ func (s *Sequencer) PublishTransaction(parentCtx context.Context, tx *types.Tran return types.ErrTxTypeNotSupported } - queueTimeout := s.config().QueueTimeout + txBytes, err := tx.MarshalBinary() + if err != nil { + return err + } + + queueTimeout := config.QueueTimeout queueCtx, cancelFunc := ctxWithTimeout(parentCtx, queueTimeout) defer cancelFunc() @@ -403,6 +463,7 @@ func (s *Sequencer) PublishTransaction(parentCtx context.Context, tx *types.Tran resultChan := make(chan error, 1) queueItem := txQueueItem{ tx, + len(txBytes), options, resultChan, false, @@ -481,7 +542,7 @@ func (s *Sequencer) CheckHealth(ctx context.Context) error { if pauseChan != nil { return nil } - return s.execEngine.streamer.ExpectChosenSequencer() + return s.execEngine.consensus.ExpectChosenSequencer() } func (s *Sequencer) ForwardTarget() string { @@ -628,7 +689,8 @@ func (s *Sequencer) expireNonceFailures() *time.Timer { } // There's no guarantee that returned tx nonces will be correct -func (s *Sequencer) precheckNonces(queueItems []txQueueItem) []txQueueItem { +func (s *Sequencer) precheckNonces(queueItems []txQueueItem, totalBlockSize int) []txQueueItem { + config := s.config() bc := s.execEngine.bc latestHeader := bc.CurrentBlock() latestState, err := bc.StateAt(latestHeader.Root) @@ -678,7 +740,13 @@ func (s *Sequencer) precheckNonces(queueItems []txQueueItem) []txQueueItem { if err != nil { revivingFailure.queueItem.returnResult(err) } else { - nextQueueItem = &revivingFailure.queueItem + if arbmath.SaturatingAdd(totalBlockSize, queueItem.txSize) > config.MaxTxDataSize { + // This tx would be too large to add to this block + s.txRetryQueue.Push(queueItem) + } else { + nextQueueItem = &revivingFailure.queueItem + totalBlockSize += queueItem.txSize + } } } } else if txNonce < stateNonce || txNonce > pendingNonce { @@ -714,7 +782,7 @@ func (s *Sequencer) precheckNonces(queueItems []txQueueItem) []txQueueItem { func (s *Sequencer) createBlock(ctx context.Context) (returnValue bool) { var queueItems []txQueueItem - var totalBatchSize int + var totalBlockSize int defer func() { panicErr := recover() @@ -785,37 +853,47 @@ func (s *Sequencer) createBlock(ctx context.Context) (returnValue bool) { queueItem.returnResult(err) continue } - txBytes, err := queueItem.tx.MarshalBinary() - if err != nil { - queueItem.returnResult(err) - continue - } - if len(txBytes) > config.MaxTxDataSize { + if queueItem.txSize > config.MaxTxDataSize { // This tx is too large queueItem.returnResult(txpool.ErrOversizedData) continue } - if totalBatchSize+len(txBytes) > config.MaxTxDataSize { + if totalBlockSize+queueItem.txSize > config.MaxTxDataSize { // This tx would be too large to add to this batch s.txRetryQueue.Push(queueItem) // End the batch here to put this tx in the next one break } - totalBatchSize += len(txBytes) + totalBlockSize += queueItem.txSize queueItems = append(queueItems, queueItem) } s.nonceCache.Resize(config.NonceCacheSize) // Would probably be better in a config hook but this is basically free s.nonceCache.BeginNewBlock() - queueItems = s.precheckNonces(queueItems) + queueItems = s.precheckNonces(queueItems, totalBlockSize) txes := make([]*types.Transaction, len(queueItems)) hooks := s.makeSequencingHooks() hooks.ConditionalOptionsForTx = make([]*arbitrum_types.ConditionalOptions, len(queueItems)) + totalBlockSize = 0 // recompute the totalBlockSize to double check it for i, queueItem := range queueItems { txes[i] = queueItem.tx + totalBlockSize = arbmath.SaturatingAdd(totalBlockSize, queueItem.txSize) hooks.ConditionalOptionsForTx[i] = queueItem.options } + if totalBlockSize > config.MaxTxDataSize { + for _, queueItem := range queueItems { + s.txRetryQueue.Push(queueItem) + } + log.Error( + "put too many transactions in a block", + "numTxes", len(queueItems), + "totalBlockSize", totalBlockSize, + "maxTxDataSize", config.MaxTxDataSize, + ) + return false + } + if s.handleInactive(ctx, queueItems) { return false } @@ -827,13 +905,16 @@ func (s *Sequencer) createBlock(ctx context.Context) (returnValue bool) { s.L1BlockAndTimeMutex.Unlock() if s.l1Reader != nil && (l1Block == 0 || math.Abs(float64(l1Timestamp)-float64(timestamp)) > config.MaxAcceptableTimestampDelta.Seconds()) { + for _, queueItem := range queueItems { + s.txRetryQueue.Push(queueItem) + } log.Error( "cannot sequence: unknown L1 block or L1 timestamp too far from local clock time", "l1Block", l1Block, "l1Timestamp", time.Unix(int64(l1Timestamp), 0), "localTimestamp", time.Unix(int64(timestamp), 0), ) - return false + return true } header := &arbostypes.L1IncomingMessageHeader{ @@ -846,7 +927,15 @@ func (s *Sequencer) createBlock(ctx context.Context) (returnValue bool) { } start := time.Now() - block, err := s.execEngine.SequenceTransactions(header, txes, hooks) + var ( + block *types.Block + err error + ) + if config.EnableProfiling { + block, err = s.execEngine.SequenceTransactionsWithProfiling(header, txes, hooks) + } else { + block, err = s.execEngine.SequenceTransactions(header, txes, hooks) + } elapsed := time.Since(start) blockCreationTimer.Update(elapsed) if elapsed >= time.Second*5 { @@ -944,14 +1033,84 @@ func (s *Sequencer) Initialize(ctx context.Context) error { return nil } +var ( + usableBytesInBlob = big.NewInt(int64(len(kzg4844.Blob{}) * 31 / 32)) + blobTxBlobGasPerBlob = big.NewInt(params.BlobTxBlobGasPerBlob) +) + +func (s *Sequencer) updateExpectedSurplus(ctx context.Context) (int64, error) { + header, err := s.l1Reader.LastHeader(ctx) + if err != nil { + return 0, fmt.Errorf("error encountered getting latest header from l1reader while updating expectedSurplus: %w", err) + } + l1GasPrice := header.BaseFee.Uint64() + if header.BlobGasUsed != nil { + if header.ExcessBlobGas != nil { + blobFeePerByte := eip4844.CalcBlobFee(eip4844.CalcExcessBlobGas(*header.ExcessBlobGas, *header.BlobGasUsed)) + blobFeePerByte.Mul(blobFeePerByte, blobTxBlobGasPerBlob) + blobFeePerByte.Div(blobFeePerByte, usableBytesInBlob) + if l1GasPrice > blobFeePerByte.Uint64()/16 { + l1GasPrice = blobFeePerByte.Uint64() / 16 + } + } + } + surplus, err := s.execEngine.getL1PricingSurplus() + if err != nil { + return 0, fmt.Errorf("error encountered getting l1 pricing surplus while updating expectedSurplus: %w", err) + } + backlogL1GasCharged := int64(s.execEngine.consensus.BacklogL1GasCharged()) + backlogCallDataUnits := int64(s.execEngine.consensus.BacklogCallDataUnits()) + expectedSurplus := int64(surplus) + backlogL1GasCharged - backlogCallDataUnits*int64(l1GasPrice) + // update metrics + l1GasPriceGauge.Update(int64(l1GasPrice)) + callDataUnitsBacklogGauge.Update(backlogCallDataUnits) + unusedL1GasChargeGauge.Update(backlogL1GasCharged) + currentSurplusGauge.Update(surplus) + expectedSurplusGauge.Update(expectedSurplus) + config := s.config() + if config.ExpectedSurplusSoftThreshold != "default" && expectedSurplus < int64(config.expectedSurplusSoftThreshold) { + log.Warn("expected surplus is below soft threshold", "value", expectedSurplus, "threshold", config.expectedSurplusSoftThreshold) + } + return expectedSurplus, nil +} + func (s *Sequencer) Start(ctxIn context.Context) error { s.StopWaiter.Start(ctxIn, s) + config := s.config() + if (config.ExpectedSurplusHardThreshold != "default" || config.ExpectedSurplusSoftThreshold != "default") && s.l1Reader == nil { + return errors.New("expected surplus soft/hard thresholds are enabled but l1Reader is nil") + } + if s.l1Reader != nil { initialBlockNr := atomic.LoadUint64(&s.l1BlockNumber) if initialBlockNr == 0 { return errors.New("sequencer not initialized") } + expectedSurplus, err := s.updateExpectedSurplus(ctxIn) + if err != nil { + if config.ExpectedSurplusHardThreshold != "default" { + return fmt.Errorf("expected-surplus-hard-threshold is enabled but error fetching initial expected surplus value: %w", err) + } + log.Error("expected-surplus-soft-threshold is enabled but error fetching initial expected surplus value", "err", err) + } else { + s.expectedSurplus = expectedSurplus + s.expectedSurplusUpdated = true + } + s.CallIteratively(func(ctx context.Context) time.Duration { + expectedSurplus, err := s.updateExpectedSurplus(ctxIn) + s.expectedSurplusMutex.Lock() + defer s.expectedSurplusMutex.Unlock() + if err != nil { + s.expectedSurplusUpdated = false + log.Error("expected surplus soft/hard thresholds are enabled but unable to fetch latest expected surplus, retrying", "err", err) + return 0 + } + s.expectedSurplusUpdated = true + s.expectedSurplus = expectedSurplus + return 5 * time.Second + }) + headerChan, cancel := s.l1Reader.Subscribe(false) s.LaunchThread(func(ctx context.Context) { @@ -973,8 +1132,7 @@ func (s *Sequencer) Start(ctxIn context.Context) error { s.CallIteratively(func(ctx context.Context) time.Duration { nextBlock := time.Now().Add(s.config().MaxBlockSpeed) - madeBlock := s.createBlock(ctx) - if madeBlock { + if s.createBlock(ctx) { // Note: this may return a negative duration, but timers are fine with that (they treat negative durations as 0). return time.Until(nextBlock) } diff --git a/execution/gethexec/sync_monitor.go b/execution/gethexec/sync_monitor.go new file mode 100644 index 0000000000..564c6d74bd --- /dev/null +++ b/execution/gethexec/sync_monitor.go @@ -0,0 +1,120 @@ +package gethexec + +import ( + "context" + + "github.com/offchainlabs/nitro/execution" + "github.com/pkg/errors" + flag "github.com/spf13/pflag" +) + +type SyncMonitorConfig struct { + SafeBlockWaitForBlockValidator bool `koanf:"safe-block-wait-for-block-validator"` + FinalizedBlockWaitForBlockValidator bool `koanf:"finalized-block-wait-for-block-validator"` +} + +var DefaultSyncMonitorConfig = SyncMonitorConfig{ + SafeBlockWaitForBlockValidator: false, + FinalizedBlockWaitForBlockValidator: false, +} + +func SyncMonitorConfigAddOptions(prefix string, f *flag.FlagSet) { + f.Bool(prefix+".safe-block-wait-for-block-validator", DefaultSyncMonitorConfig.SafeBlockWaitForBlockValidator, "wait for block validator to complete before returning safe block number") + f.Bool(prefix+".finalized-block-wait-for-block-validator", DefaultSyncMonitorConfig.FinalizedBlockWaitForBlockValidator, "wait for block validator to complete before returning finalized block number") +} + +type SyncMonitor struct { + config *SyncMonitorConfig + consensus execution.ConsensusInfo + exec *ExecutionEngine +} + +func NewSyncMonitor(config *SyncMonitorConfig, exec *ExecutionEngine) *SyncMonitor { + return &SyncMonitor{ + config: config, + exec: exec, + } +} + +func (s *SyncMonitor) FullSyncProgressMap() map[string]interface{} { + res := s.consensus.FullSyncProgressMap() + + res["consensusSyncTarget"] = s.consensus.SyncTargetMessageCount() + + header, err := s.exec.getCurrentHeader() + if err != nil { + res["currentHeaderError"] = err + } else { + blockNum := header.Number.Uint64() + res["blockNum"] = blockNum + messageNum, err := s.exec.BlockNumberToMessageIndex(blockNum) + if err != nil { + res["messageOfLastBlockError"] = err + } else { + res["messageOfLastBlock"] = messageNum + } + } + + return res +} + +func (s *SyncMonitor) SyncProgressMap() map[string]interface{} { + if s.consensus.Synced() { + built, err := s.exec.HeadMessageNumber() + consensusSyncTarget := s.consensus.SyncTargetMessageCount() + if err == nil && built+1 >= consensusSyncTarget { + return make(map[string]interface{}) + } + } + return s.FullSyncProgressMap() +} + +func (s *SyncMonitor) SafeBlockNumber(ctx context.Context) (uint64, error) { + if s.consensus == nil { + return 0, errors.New("not set up for safeblock") + } + msg, err := s.consensus.GetSafeMsgCount(ctx) + if err != nil { + return 0, err + } + if s.config.SafeBlockWaitForBlockValidator { + latestValidatedCount, err := s.consensus.ValidatedMessageCount() + if err != nil { + return 0, err + } + if msg > latestValidatedCount { + msg = latestValidatedCount + } + } + block := s.exec.MessageIndexToBlockNumber(msg - 1) + return block, nil +} + +func (s *SyncMonitor) FinalizedBlockNumber(ctx context.Context) (uint64, error) { + if s.consensus == nil { + return 0, errors.New("not set up for safeblock") + } + msg, err := s.consensus.GetFinalizedMsgCount(ctx) + if err != nil { + return 0, err + } + if s.config.FinalizedBlockWaitForBlockValidator { + latestValidatedCount, err := s.consensus.ValidatedMessageCount() + if err != nil { + return 0, err + } + if msg > latestValidatedCount { + msg = latestValidatedCount + } + } + block := s.exec.MessageIndexToBlockNumber(msg - 1) + return block, nil +} + +func (s *SyncMonitor) Synced() bool { + return len(s.SyncProgressMap()) == 0 +} + +func (s *SyncMonitor) SetConsensusInfo(consensus execution.ConsensusInfo) { + s.consensus = consensus +} diff --git a/execution/gethexec/tx_pre_checker.go b/execution/gethexec/tx_pre_checker.go index cff8b04d32..1a48d75fda 100644 --- a/execution/gethexec/tx_pre_checker.go +++ b/execution/gethexec/tx_pre_checker.go @@ -187,7 +187,7 @@ func PreCheckTx(bc *core.BlockChain, chainConfig *params.ChainConfig, header *ty } balance := statedb.GetBalance(sender) cost := tx.Cost() - if arbmath.BigLessThan(balance, cost) { + if arbmath.BigLessThan(balance.ToBig(), cost) { return fmt.Errorf("%w: address %v have %v want %v", core.ErrInsufficientFunds, sender, balance, cost) } if config.Strictness >= TxPreCheckerStrictnessFullValidation && tx.Nonce() > stateNonce { diff --git a/execution/interface.go b/execution/interface.go index 2cbbf550ad..66aefe9a5e 100644 --- a/execution/interface.go +++ b/execution/interface.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/validator" @@ -21,6 +22,7 @@ type RecordResult struct { BlockHash common.Hash Preimages map[common.Hash][]byte BatchInfo []validator.BatchInfo + UserWasms state.UserWasms } var ErrRetrySequencer = errors.New("please retry transaction") @@ -28,8 +30,8 @@ var ErrSequencerInsertLockTaken = errors.New("insert lock taken") // always needed type ExecutionClient interface { - DigestMessage(num arbutil.MessageIndex, msg *arbostypes.MessageWithMetadata, msgForPrefetch *arbostypes.MessageWithMetadata) error - Reorg(count arbutil.MessageIndex, newMessages []arbostypes.MessageWithMetadata, oldMessages []*arbostypes.MessageWithMetadata) error + DigestMessage(num arbutil.MessageIndex, msg *arbostypes.MessageWithMetadata, msgForPrefetch *arbostypes.MessageWithMetadata) (*MessageResult, error) + Reorg(count arbutil.MessageIndex, newMessages []arbostypes.MessageWithMetadataAndBlockHash, oldMessages []*arbostypes.MessageWithMetadata) ([]*MessageResult, error) HeadMessageNumber() (arbutil.MessageIndex, error) HeadMessageNumberSync(t *testing.T) (arbutil.MessageIndex, error) ResultAtPos(pos arbutil.MessageIndex) (*MessageResult, error) @@ -54,7 +56,7 @@ type ExecutionSequencer interface { ForwardTo(url string) error SequenceDelayedMessage(message *arbostypes.L1IncomingMessage, delayedSeqNum uint64) error NextDelayedMessageNumber() (uint64, error) - SetTransactionStreamer(streamer TransactionStreamer) + GetL1GasPriceEstimate() (uint64, error) } type FullExecutionClient interface { @@ -67,19 +69,38 @@ type FullExecutionClient interface { Maintenance() error - // TODO: only used to get safe/finalized block numbers - MessageIndexToBlockNumber(messageNum arbutil.MessageIndex) uint64 - ArbOSVersionForMessageNumber(messageNum arbutil.MessageIndex) (uint64, error) } // not implemented in execution, used as input +// BatchFetcher is required for any execution node type BatchFetcher interface { - FetchBatch(batchNum uint64) ([]byte, common.Hash, error) + FetchBatch(ctx context.Context, batchNum uint64) ([]byte, common.Hash, error) + FindInboxBatchContainingMessage(message arbutil.MessageIndex) (uint64, bool, error) + GetBatchParentChainBlock(seqNum uint64) (uint64, error) } -type TransactionStreamer interface { - BatchFetcher - WriteMessageFromSequencer(pos arbutil.MessageIndex, msgWithMeta arbostypes.MessageWithMetadata) error +type ConsensusInfo interface { + Synced() bool + FullSyncProgressMap() map[string]interface{} + SyncTargetMessageCount() arbutil.MessageIndex + + // TODO: switch from pulling to pushing safe/finalized + GetSafeMsgCount(ctx context.Context) (arbutil.MessageIndex, error) + GetFinalizedMsgCount(ctx context.Context) (arbutil.MessageIndex, error) + ValidatedMessageCount() (arbutil.MessageIndex, error) +} + +type ConsensusSequencer interface { + WriteMessageFromSequencer(pos arbutil.MessageIndex, msgWithMeta arbostypes.MessageWithMetadata, msgResult MessageResult) error ExpectChosenSequencer() error + CacheL1PriceDataOfMsg(pos arbutil.MessageIndex, callDataUnits uint64, l1GasCharged uint64) + BacklogL1GasCharged() uint64 + BacklogCallDataUnits() uint64 +} + +type FullConsensusClient interface { + BatchFetcher + ConsensusInfo + ConsensusSequencer } diff --git a/nodeInterface/NodeInterface.go b/execution/nodeInterface/NodeInterface.go similarity index 88% rename from nodeInterface/NodeInterface.go rename to execution/nodeInterface/NodeInterface.go index bdcfb569f4..9179a52718 100644 --- a/nodeInterface/NodeInterface.go +++ b/execution/nodeInterface/NodeInterface.go @@ -20,14 +20,12 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" - "github.com/offchainlabs/nitro/arbnode" "github.com/offchainlabs/nitro/arbos" "github.com/offchainlabs/nitro/arbos/l1pricing" "github.com/offchainlabs/nitro/arbos/retryables" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/solgen/go/node_interfacegen" - "github.com/offchainlabs/nitro/staker" "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/merkletree" ) @@ -53,90 +51,129 @@ var merkleTopic common.Hash var l2ToL1TxTopic common.Hash var l2ToL1TransactionTopic common.Hash -var blockInGenesis = errors.New("") -var blockAfterLatestBatch = errors.New("") - func (n NodeInterface) NitroGenesisBlock(c ctx) (huge, error) { block := n.backend.ChainConfig().ArbitrumChainParams.GenesisBlockNum return arbmath.UintToBig(block), nil } +// bool will be false but no error if behind genesis +func (n NodeInterface) blockNumToMessageIndex(blockNum uint64) (arbutil.MessageIndex, bool, error) { + node, err := gethExecFromNodeInterfaceBackend(n.backend) + if err != nil { + return 0, false, err + } + blockchain, err := blockchainFromNodeInterfaceBackend(n.backend) + if err != nil { + return 0, false, err + } + if blockNum < blockchain.Config().ArbitrumChainParams.GenesisBlockNum { + return 0, true, nil + } + msgIndex, err := node.ExecEngine.BlockNumberToMessageIndex(blockNum) + if err != nil { + return 0, false, err + } + return msgIndex, true, nil +} + +func (n NodeInterface) msgNumToInboxBatch(msgIndex arbutil.MessageIndex) (uint64, bool, error) { + node, err := gethExecFromNodeInterfaceBackend(n.backend) + if err != nil { + return 0, false, err + } + fetcher := node.ExecEngine.GetBatchFetcher() + if fetcher == nil { + return 0, false, errors.New("batch fetcher not set") + } + return fetcher.FindInboxBatchContainingMessage(msgIndex) +} + func (n NodeInterface) FindBatchContainingBlock(c ctx, evm mech, blockNum uint64) (uint64, error) { - node, err := arbNodeFromNodeInterfaceBackend(n.backend) + msgIndex, found, err := n.blockNumToMessageIndex(blockNum) if err != nil { return 0, err } - return findBatchContainingBlock(node, node.TxStreamer.GenesisBlockNumber(), blockNum) + if !found { + return 0, fmt.Errorf("block %v is part of genesis", blockNum) + } + res, found, err := n.msgNumToInboxBatch(msgIndex) + if err == nil && !found { + return 0, errors.New("block not yet found on any batch") + } + return res, err } func (n NodeInterface) GetL1Confirmations(c ctx, evm mech, blockHash bytes32) (uint64, error) { - node, err := arbNodeFromNodeInterfaceBackend(n.backend) + node, err := gethExecFromNodeInterfaceBackend(n.backend) if err != nil { return 0, err } - if node.InboxReader == nil { - return 0, nil - } - bc, err := blockchainFromNodeInterfaceBackend(n.backend) + blockchain, err := blockchainFromNodeInterfaceBackend(n.backend) if err != nil { return 0, err } - header := bc.GetHeaderByHash(blockHash) + header := blockchain.GetHeaderByHash(blockHash) if header == nil { return 0, errors.New("unknown block hash") } blockNum := header.Number.Uint64() - genesis := node.TxStreamer.GenesisBlockNumber() - batch, err := findBatchContainingBlock(node, genesis, blockNum) + + // blocks behind genesis are treated as belonging to batch 0 + msgNum, _, err := n.blockNumToMessageIndex(blockNum) if err != nil { - if errors.Is(err, blockInGenesis) { - batch = 0 - } else if errors.Is(err, blockAfterLatestBatch) { - return 0, nil - } else { - return 0, err - } + return 0, err + } + // batches not yet posted have 0 confirmations but no error + batchNum, found, err := n.msgNumToInboxBatch(msgNum) + if err != nil { + return 0, err + } + if !found { + return 0, nil } - meta, err := node.InboxTracker.GetBatchMetadata(batch) + parentChainBlockNum, err := node.ExecEngine.GetBatchFetcher().GetBatchParentChainBlock(batchNum) if err != nil { return 0, err } - if node.L1Reader.IsParentChainArbitrum() { - parentChainClient := node.L1Reader.Client() + + if node.ParentChainReader.IsParentChainArbitrum() { + parentChainClient := node.ParentChainReader.Client() parentNodeInterface, err := node_interfacegen.NewNodeInterface(types.NodeInterfaceAddress, parentChainClient) if err != nil { return 0, err } - parentChainBlock, err := parentChainClient.BlockByNumber(n.context, new(big.Int).SetUint64(meta.ParentChainBlock)) + parentChainBlock, err := parentChainClient.BlockByNumber(n.context, new(big.Int).SetUint64(parentChainBlockNum)) if err != nil { // Hide the parent chain RPC error from the client in case it contains sensitive information. // Likely though, this error is just "not found" because the block got reorg'd. - return 0, fmt.Errorf("failed to get parent chain block %v containing batch", meta.ParentChainBlock) + return 0, fmt.Errorf("failed to get parent chain block %v containing batch", parentChainBlockNum) } confs, err := parentNodeInterface.GetL1Confirmations(&bind.CallOpts{Context: n.context}, parentChainBlock.Hash()) if err != nil { log.Warn( "Failed to get L1 confirmations from parent chain", - "blockNumber", meta.ParentChainBlock, + "blockNumber", parentChainBlockNum, "blockHash", parentChainBlock.Hash(), "err", err, ) return 0, fmt.Errorf("failed to get L1 confirmations from parent chain for block %v", parentChainBlock.Hash()) } return confs, nil } - latestL1Block, latestBatchCount := node.InboxReader.GetLastReadBlockAndBatchCount() - if latestBatchCount <= batch { - return 0, nil // batch was reorg'd out? - } - if latestL1Block < meta.ParentChainBlock || arbutil.BlockNumberToMessageCount(blockNum, genesis) > meta.MessageCount { + if node.ParentChainReader == nil { return 0, nil } - canonicalHash := bc.GetCanonicalHash(header.Number.Uint64()) - if canonicalHash != header.Hash() { - return 0, errors.New("block hash is non-canonical") + latestHeader, err := node.ParentChainReader.LastHeaderWithError() + if err != nil { + return 0, err + } + if latestHeader == nil { + return 0, errors.New("no headers read from l1") } - confs := (latestL1Block - meta.ParentChainBlock) + 1 + node.InboxReader.GetDelayBlocks() - return confs, nil + latestBlockNum := latestHeader.Number.Uint64() + if latestBlockNum < parentChainBlockNum { + return 0, nil + } + return (latestBlockNum - parentChainBlockNum), nil } func (n NodeInterface) EstimateRetryableTicket( @@ -176,12 +213,11 @@ func (n NodeInterface) EstimateRetryableTicket( } // ArbitrumSubmitRetryableTx is unsigned so the following won't panic - msg, err := core.TransactionToMessage(types.NewTx(submitTx), types.NewArbitrumSigner(nil), nil) + msg, err := core.TransactionToMessage(types.NewTx(submitTx), types.NewArbitrumSigner(nil), nil, core.MessageGasEstimationMode) if err != nil { return err } - msg.TxRunMode = core.MessageGasEstimationMode *n.returnMessage.message = *msg *n.returnMessage.changed = true return nil @@ -561,42 +597,18 @@ func (n NodeInterface) GasEstimateComponents( return total, gasForL1, baseFee, l1BaseFeeEstimate, nil } -func findBatchContainingBlock(node *arbnode.Node, genesis uint64, block uint64) (uint64, error) { - if block <= genesis { - return 0, fmt.Errorf("%wblock %v is part of genesis", blockInGenesis, block) - } - pos := arbutil.BlockNumberToMessageCount(block, genesis) - 1 - high, err := node.InboxTracker.GetBatchCount() - if err != nil { - return 0, err - } - high-- - latestCount, err := node.InboxTracker.GetBatchMessageCount(high) - if err != nil { - return 0, err - } - latestBlock := arbutil.MessageCountToBlockNumber(latestCount, genesis) - if int64(block) > latestBlock { - return 0, fmt.Errorf( - "%wrequested block %v is after latest on-chain block %v published in batch %v", - blockAfterLatestBatch, block, latestBlock, high, - ) - } - return staker.FindBatchContainingMessageIndex(node.InboxTracker, pos, high) -} - func (n NodeInterface) LegacyLookupMessageBatchProof(c ctx, evm mech, batchNum huge, index uint64) ( proof []bytes32, path huge, l2Sender addr, l1Dest addr, l2Block huge, l1Block huge, timestamp huge, amount huge, calldataForL1 []byte, err error) { - node, err := arbNodeFromNodeInterfaceBackend(n.backend) + node, err := gethExecFromNodeInterfaceBackend(n.backend) if err != nil { return } - if node.ClassicOutboxRetriever == nil { + if node.ClassicOutbox == nil { err = errors.New("this node doesnt support classicLookupMessageBatchProof") return } - msg, err := node.ClassicOutboxRetriever.GetMsg(batchNum, index) + msg, err := node.ClassicOutbox.GetMsg(batchNum, index) if err != nil { return } diff --git a/nodeInterface/NodeInterfaceDebug.go b/execution/nodeInterface/NodeInterfaceDebug.go similarity index 100% rename from nodeInterface/NodeInterfaceDebug.go rename to execution/nodeInterface/NodeInterfaceDebug.go diff --git a/nodeInterface/virtual-contracts.go b/execution/nodeInterface/virtual-contracts.go similarity index 94% rename from nodeInterface/virtual-contracts.go rename to execution/nodeInterface/virtual-contracts.go index b35381a77a..d72ad0da8e 100644 --- a/nodeInterface/virtual-contracts.go +++ b/execution/nodeInterface/virtual-contracts.go @@ -15,10 +15,10 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbnode" "github.com/offchainlabs/nitro/arbos" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/l1pricing" + "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/gethhook" "github.com/offchainlabs/nitro/precompiles" "github.com/offchainlabs/nitro/solgen/go/node_interfacegen" @@ -88,7 +88,7 @@ func init() { return msg, nil, nil } - evm, vmError := backend.GetEVM(ctx, msg, statedb, header, &vm.Config{NoBaseFee: true}, blockCtx) + evm := backend.GetEVM(ctx, msg, statedb, header, &vm.Config{NoBaseFee: true}, blockCtx) go func() { <-ctx.Done() evm.Cancel() @@ -110,7 +110,7 @@ func init() { ReturnData: output, ScheduledTxes: nil, } - return msg, res, vmError() + return msg, res, statedb.Error() } return msg, nil, nil } @@ -173,16 +173,16 @@ func init() { merkleTopic = arbSys.Events["SendMerkleUpdate"].ID } -func arbNodeFromNodeInterfaceBackend(backend BackendAPI) (*arbnode.Node, error) { +func gethExecFromNodeInterfaceBackend(backend BackendAPI) (*gethexec.ExecutionNode, error) { apiBackend, ok := backend.(*arbitrum.APIBackend) if !ok { return nil, errors.New("API backend isn't Arbitrum") } - arbNode, ok := apiBackend.GetArbitrumNode().(*arbnode.Node) + exec, ok := apiBackend.GetArbitrumNode().(*gethexec.ExecutionNode) if !ok { return nil, errors.New("failed to get Arbitrum Node from backend") } - return arbNode, nil + return exec, nil } func blockchainFromNodeInterfaceBackend(backend BackendAPI) (*core.BlockChain, error) { diff --git a/fastcache b/fastcache index 8053d350d7..f9d9f11052 160000 --- a/fastcache +++ b/fastcache @@ -1 +1 @@ -Subproject commit 8053d350d785b5dd877e208e1f0205bbd36faee7 +Subproject commit f9d9f11052817d478af08b64d139d5f09ec3a68f diff --git a/gethhook/geth-hook.go b/gethhook/geth-hook.go index dcd1788710..776e8cc452 100644 --- a/gethhook/geth-hook.go +++ b/gethhook/geth-hook.go @@ -10,6 +10,7 @@ import ( "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbos" "github.com/offchainlabs/nitro/precompiles" ) @@ -55,16 +56,33 @@ func init() { vm.PrecompiledContractsArbitrum[k] = v } + for k, v := range vm.PrecompiledContractsCancun { + vm.PrecompiledAddressesArbOS30 = append(vm.PrecompiledAddressesArbOS30, k) + vm.PrecompiledContractsArbOS30[k] = v + } + precompileErrors := make(map[[4]byte]abi.Error) for addr, precompile := range precompiles.Precompiles() { for _, errABI := range precompile.Precompile().GetErrorABIs() { - var id [4]byte - copy(id[:], errABI.ID[:4]) - precompileErrors[id] = errABI + precompileErrors[[4]byte(errABI.ID.Bytes())] = errABI } var wrapped vm.AdvancedPrecompile = ArbosPrecompileWrapper{precompile} - vm.PrecompiledContractsArbitrum[addr] = wrapped - vm.PrecompiledAddressesArbitrum = append(vm.PrecompiledAddressesArbitrum, addr) + vm.PrecompiledContractsArbOS30[addr] = wrapped + vm.PrecompiledAddressesArbOS30 = append(vm.PrecompiledAddressesArbOS30, addr) + + if precompile.Precompile().ArbosVersion() < params.ArbosVersion_Stylus { + vm.PrecompiledContractsArbitrum[addr] = wrapped + vm.PrecompiledAddressesArbitrum = append(vm.PrecompiledAddressesArbitrum, addr) + } + } + + for addr, precompile := range vm.PrecompiledContractsArbitrum { + vm.PrecompiledContractsArbOS30[addr] = precompile + vm.PrecompiledAddressesArbOS30 = append(vm.PrecompiledAddressesArbOS30, addr) + } + for addr, precompile := range vm.PrecompiledContractsP256Verify { + vm.PrecompiledContractsArbOS30[addr] = precompile + vm.PrecompiledAddressesArbOS30 = append(vm.PrecompiledAddressesArbOS30, addr) } core.RenderRPCError = func(data []byte) error { diff --git a/gethhook/geth_test.go b/gethhook/geth_test.go index 6274a54119..99bfa4ae1c 100644 --- a/gethhook/geth_test.go +++ b/gethhook/geth_test.go @@ -110,7 +110,7 @@ func TestEthDepositMessage(t *testing.T) { RunMessagesThroughAPI(t, [][]byte{serialized, serialized2}, statedb) - balanceAfter := statedb.GetBalance(addr) + balanceAfter := statedb.GetBalance(addr).ToBig() if balanceAfter.Cmp(new(big.Int).Add(balance.Big(), balance2.Big())) != 0 { Fail(t) } diff --git a/go-ethereum b/go-ethereum index 21caebd7ef..6449e9fd28 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 21caebd7ef0530b8d983c020db864b82ea10a802 +Subproject commit 6449e9fd28eaee5556acab543aefdaa0083e9cdb diff --git a/go.mod b/go.mod index ac8c4cdee9..440f502c8d 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/offchainlabs/nitro -go 1.20 +go 1.21 replace github.com/VictoriaMetrics/fastcache => ./fastcache @@ -9,11 +9,11 @@ replace github.com/ethereum/go-ethereum => ./go-ethereum require ( github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible github.com/Shopify/toxiproxy v2.1.4+incompatible - github.com/alicebob/miniredis/v2 v2.21.0 + github.com/alicebob/miniredis/v2 v2.32.1 github.com/andybalholm/brotli v1.0.4 - github.com/aws/aws-sdk-go-v2 v1.16.4 - github.com/aws/aws-sdk-go-v2/config v1.15.5 - github.com/aws/aws-sdk-go-v2/credentials v1.12.0 + github.com/aws/aws-sdk-go-v2 v1.21.2 + github.com/aws/aws-sdk-go-v2/config v1.18.45 + github.com/aws/aws-sdk-go-v2/credentials v1.13.43 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.10 github.com/aws/aws-sdk-go-v2/service/s3 v1.26.9 github.com/cavaliergopher/grab/v3 v3.0.1 @@ -23,322 +23,156 @@ require ( github.com/enescakir/emoji v1.0.0 github.com/ethereum/go-ethereum v1.10.26 github.com/fatih/structtag v1.2.0 - github.com/gdamore/tcell/v2 v2.6.0 + github.com/gdamore/tcell/v2 v2.7.1 + github.com/go-redis/redis/v8 v8.11.5 + github.com/gobwas/httphead v0.1.0 + github.com/gobwas/ws v1.2.1 + github.com/gobwas/ws-examples v0.0.0-20190625122829-a9e8908d9484 github.com/google/go-cmp v0.6.0 - github.com/hashicorp/golang-lru/v2 v2.0.2 - github.com/holiman/uint256 v1.2.3 - github.com/ipfs/go-cid v0.4.1 - github.com/ipfs/go-libipfs v0.6.2 - github.com/ipfs/interface-go-ipfs-core v0.11.0 - github.com/ipfs/kubo v0.19.1 + github.com/google/uuid v1.3.1 + github.com/hashicorp/golang-lru/v2 v2.0.7 + github.com/holiman/uint256 v1.2.4 github.com/knadh/koanf v1.4.0 - github.com/libp2p/go-libp2p v0.27.8 - github.com/multiformats/go-multiaddr v0.12.1 - github.com/multiformats/go-multihash v0.2.3 + github.com/mailru/easygo v0.0.0-20190618140210-3c14a0dc985f + github.com/mitchellh/mapstructure v1.5.0 + github.com/pkg/errors v0.9.1 github.com/r3labs/diff/v3 v3.0.1 - github.com/rivo/tview v0.0.0-20230814110005-ccc2c8119703 + github.com/rivo/tview v0.0.0-20240307173318-e804876934a1 github.com/spf13/pflag v1.0.5 + github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/wealdtech/go-merkletree v1.0.0 golang.org/x/crypto v0.21.0 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa golang.org/x/sys v0.18.0 golang.org/x/term v0.18.0 - golang.org/x/tools v0.13.0 + golang.org/x/tools v0.16.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0 ) -require github.com/gofrs/flock v0.8.1 // indirect - require ( - bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc // indirect - github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect - github.com/DataDog/zstd v1.5.2 // indirect + github.com/DataDog/zstd v1.4.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect - github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/VictoriaMetrics/fastcache v1.12.1 // indirect github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.11 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.5 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.11 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.11.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.16.4 // indirect - github.com/aws/smithy-go v1.11.2 // indirect - github.com/benbjohnson/clock v1.3.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.15.2 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 // indirect + github.com/aws/smithy-go v1.15.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bits-and-blooms/bitset v1.7.0 // indirect - github.com/blang/semver/v4 v4.0.0 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect github.com/blendle/zapdriver v1.3.2-0.20200203083823-9200777f8a3d // indirect github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 // indirect - github.com/cenkalti/backoff v2.2.1+incompatible // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/ceramicnetwork/go-dag-jose v0.1.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cockroachdb/errors v1.9.1 // indirect - github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect github.com/cockroachdb/redact v1.1.3 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/consensys/bavard v0.1.13 // indirect github.com/consensys/gnark-crypto v0.12.1 // indirect - github.com/containerd/cgroups v1.1.0 // indirect - github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect - github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect + github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect - github.com/cskr/pubsub v1.0.2 // indirect - github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/deckarep/golang-set/v2 v2.1.0 // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect - github.com/dgraph-io/badger v1.6.2 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dlclark/regexp2 v1.7.0 // indirect - github.com/docker/go-units v0.5.0 // indirect github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 // indirect - github.com/dustin/go-humanize v1.0.1 // indirect - github.com/elastic/gosigar v0.14.2 // indirect - github.com/emirpasic/gods v1.18.1 // indirect + github.com/dustin/go-humanize v1.0.0 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect - github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect - github.com/flynn/noise v1.0.0 // indirect - github.com/francoispqt/gojay v1.2.13 // indirect + github.com/fjl/memsize v0.0.2 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gammazero/deque v0.2.1 // indirect + github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect + github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/getsentry/sentry-go v0.18.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect - github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect + github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.3.0 // indirect - github.com/golang/glog v1.2.0 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/mock v1.6.0 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/flatbuffers v23.5.26+incompatible // indirect - github.com/google/gopacket v1.1.19 // indirect - github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // indirect - github.com/gorilla/mux v1.8.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang/glog v1.1.2 // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/flatbuffers v1.12.1 // indirect + github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/graph-gophers/graphql-go v1.3.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 // indirect github.com/h2non/filetype v1.0.6 // indirect - github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e // indirect - github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-bexpr v0.1.10 // indirect github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 // indirect + github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/huin/goupnp v1.3.0 // indirect - github.com/ipfs/bbloom v0.0.4 // indirect - github.com/ipfs/go-bitfield v1.1.0 // indirect - github.com/ipfs/go-block-format v0.1.1 // indirect - github.com/ipfs/go-blockservice v0.5.1 // indirect - github.com/ipfs/go-cidutil v0.1.0 // indirect - github.com/ipfs/go-datastore v0.6.0 // indirect - github.com/ipfs/go-delegated-routing v0.7.0 // indirect - github.com/ipfs/go-ds-badger v0.3.0 // indirect - github.com/ipfs/go-ds-flatfs v0.5.1 // indirect - github.com/ipfs/go-ds-leveldb v0.5.0 // indirect - github.com/ipfs/go-ds-measure v0.2.0 // indirect - github.com/ipfs/go-fetcher v1.6.1 // indirect - github.com/ipfs/go-filestore v1.2.0 // indirect - github.com/ipfs/go-fs-lock v0.0.7 // indirect - github.com/ipfs/go-graphsync v0.14.1 // indirect - github.com/ipfs/go-ipfs-blockstore v1.2.0 // indirect - github.com/ipfs/go-ipfs-chunker v0.0.5 // indirect - github.com/ipfs/go-ipfs-delay v0.0.1 // indirect - github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect - github.com/ipfs/go-ipfs-exchange-interface v0.2.0 // indirect - github.com/ipfs/go-ipfs-exchange-offline v0.3.0 // indirect - github.com/ipfs/go-ipfs-keystore v0.1.0 // indirect - github.com/ipfs/go-ipfs-pinner v0.3.0 // indirect - github.com/ipfs/go-ipfs-posinfo v0.0.1 // indirect - github.com/ipfs/go-ipfs-pq v0.0.3 // indirect - github.com/ipfs/go-ipfs-provider v0.8.1 // indirect - github.com/ipfs/go-ipfs-routing v0.3.0 // indirect - github.com/ipfs/go-ipfs-util v0.0.2 // indirect - github.com/ipfs/go-ipld-cbor v0.0.6 // indirect - github.com/ipfs/go-ipld-format v0.4.0 // indirect - github.com/ipfs/go-ipld-git v0.1.1 // indirect - github.com/ipfs/go-ipld-legacy v0.1.1 // indirect - github.com/ipfs/go-ipns v0.3.0 // indirect - github.com/ipfs/go-log v1.0.5 // indirect - github.com/ipfs/go-log/v2 v2.5.1 // indirect - github.com/ipfs/go-merkledag v0.9.0 // indirect - github.com/ipfs/go-metrics-interface v0.0.1 // indirect - github.com/ipfs/go-mfs v0.2.1 // indirect - github.com/ipfs/go-namesys v0.7.0 // indirect - github.com/ipfs/go-path v0.3.1 // indirect - github.com/ipfs/go-peertaskqueue v0.8.1 // indirect - github.com/ipfs/go-unixfs v0.4.4 // indirect - github.com/ipfs/go-unixfsnode v1.5.2 // indirect - github.com/ipfs/go-verifcid v0.0.2 // indirect - github.com/ipld/edelweiss v0.2.0 // indirect - github.com/ipld/go-codec-dagpb v1.5.0 // indirect - github.com/ipld/go-ipld-prime v0.19.0 // indirect - github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect - github.com/jbenet/goprocess v0.1.4 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 // indirect - github.com/klauspost/compress v1.17.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/koron/go-ssdp v0.0.4 // indirect + github.com/juju/loggo v0.0.0-20180524022052-584905176618 // indirect + github.com/klauspost/compress v1.17.2 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/libp2p/go-buffer-pool v0.1.0 // indirect - github.com/libp2p/go-cidranger v1.1.0 // indirect - github.com/libp2p/go-doh-resolver v0.4.0 // indirect - github.com/libp2p/go-flow-metrics v0.1.0 // indirect - github.com/libp2p/go-libp2p-asn-util v0.3.0 // indirect - github.com/libp2p/go-libp2p-kad-dht v0.21.1 // indirect - github.com/libp2p/go-libp2p-kbucket v0.5.0 // indirect - github.com/libp2p/go-libp2p-pubsub v0.9.0 // indirect - github.com/libp2p/go-libp2p-pubsub-router v0.6.0 // indirect - github.com/libp2p/go-libp2p-record v0.2.0 // indirect - github.com/libp2p/go-libp2p-routing-helpers v0.6.2 // indirect - github.com/libp2p/go-libp2p-xor v0.1.0 // indirect - github.com/libp2p/go-mplex v0.7.0 // indirect - github.com/libp2p/go-msgio v0.3.0 // indirect - github.com/libp2p/go-nat v0.1.0 // indirect - github.com/libp2p/go-netroute v0.2.1 // indirect - github.com/libp2p/go-reuseport v0.2.0 // indirect - github.com/libp2p/go-yamux/v4 v4.0.0 // indirect - github.com/libp2p/zeroconf/v2 v2.2.0 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/miekg/dns v1.1.53 // indirect - github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect - github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect - github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect - github.com/mr-tron/base58 v1.2.0 // indirect - github.com/multiformats/go-base32 v0.1.0 // indirect - github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect - github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multibase v0.2.0 // indirect - github.com/multiformats/go-multicodec v0.8.1 // indirect - github.com/multiformats/go-multistream v0.4.1 // indirect - github.com/multiformats/go-varint v0.0.7 // indirect - github.com/onsi/ginkgo/v2 v2.9.2 // indirect - github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/onsi/gomega v1.27.4 // indirect github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/openzipkin/zipkin-go v0.4.1 // indirect - github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/polydawn/refmt v0.89.0 // indirect github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.0 // indirect - github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-19 v0.3.3 // indirect - github.com/quic-go/qtls-go1-20 v0.2.3 // indirect - github.com/quic-go/quic-go v0.33.0 // indirect - github.com/quic-go/webtransport-go v0.5.2 // indirect - github.com/raulk/go-watchdog v1.3.0 // indirect github.com/rhnvrm/simples3 v0.6.1 // indirect - github.com/rivo/uniseg v0.4.3 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/samber/lo v1.36.0 // indirect - github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/streamingfast/eth-go v0.0.0-20240129180546-72c8e66e24b3 // indirect github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/urfave/cli/v2 v2.25.7 // indirect github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect - github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect - github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa // indirect - github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect - github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect - github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 // indirect + github.com/yuin/gopher-lua v1.1.1 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel v1.18.0 // indirect - go.opentelemetry.io/otel/exporters/jaeger v1.15.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.15.1 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.15.1 // indirect - go.opentelemetry.io/otel/exporters/zipkin v1.15.1 // indirect - go.opentelemetry.io/otel/metric v1.18.0 // indirect - go.opentelemetry.io/otel/sdk v1.16.0 // indirect - go.opentelemetry.io/otel/trace v1.18.0 // indirect - go.opentelemetry.io/proto/otlp v0.19.0 // indirect - go.uber.org/atomic v1.10.0 // indirect - go.uber.org/dig v1.16.1 // indirect - go.uber.org/fx v1.19.2 // indirect + go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect - go4.org v0.0.0-20200411211856-f5505b9728dd // indirect - golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/mod v0.12.0 // indirect + golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.22.0 // indirect - golang.org/x/sync v0.3.0 // indirect + golang.org/x/sync v0.5.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 // indirect - google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.33.0 // indirect - gopkg.in/square/go-jose.v2 v2.5.1 // indirect - lukechampine.com/blake3 v1.1.7 // indirect - nhooyr.io/websocket v1.8.7 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) require ( - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/gobwas/httphead v0.1.0 - github.com/gobwas/pool v0.2.1 // indirect - github.com/gobwas/ws v1.1.0 - github.com/gobwas/ws-examples v0.0.0-20190625122829-a9e8908d9484 - github.com/mailru/easygo v0.0.0-20190618140210-3c14a0dc985f -) - -require ( - github.com/StackExchange/wmi v1.2.1 // indirect - github.com/VictoriaMetrics/fastcache v1.6.0 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect - github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff // indirect - github.com/go-ole/go-ole v1.2.5 // indirect - github.com/go-redis/redis/v8 v8.11.4 - github.com/go-stack/stack v1.8.1 // indirect - github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect - github.com/google/uuid v1.3.1 // indirect - github.com/gorilla/websocket v1.5.0 // indirect - github.com/hashicorp/go-bexpr v0.1.10 // indirect - github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect - github.com/holiman/bloomfilter/v2 v2.0.3 // indirect - github.com/jackpal/go-nat-pmp v1.0.2 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect - github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/pointerstructure v1.2.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/rs/cors v1.10.0 // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/status-im/keycard-go v0.2.0 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect diff --git a/go.sum b/go.sum index aa045002cb..d54abd36cf 100644 --- a/go.sum +++ b/go.sum @@ -1,132 +1,66 @@ -bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc h1:utDghgcjE8u+EBjHOgYT+dJPcnDF05KqWMBcjuJy510= -bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= -github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= -github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo= -github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= -github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= -github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= -github.com/Stebalien/go-bitfield v0.0.1/go.mod h1:GNjFpasyUVkHMsfEOk8EFLJ9syQ6SI+XWrX9Wf2XH0s= -github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM= -github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis/v2 v2.21.0 h1:CdmwIlKUWFBDS+4464GtQiQ0R1vpzOgu4Vnd74rBL7M= -github.com/alicebob/miniredis/v2 v2.21.0/go.mod h1:XNqvJdQJv5mSuVMc0ynneafpnL/zv52acZ6kqeS0t88= +github.com/alicebob/miniredis/v2 v2.32.1 h1:Bz7CciDnYSaa0mX5xODh6GUITRSx+cVhjNoOR4JssBo= +github.com/alicebob/miniredis/v2 v2.32.1/go.mod h1:AqkLNAfUm0K07J28hnAyyQKf/x0YkCY/g5DCtuL01Mw= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/arduino/go-paths-helper v1.2.0 h1:qDW93PR5IZUN/jzO4rCtexiwF8P4OIcOmcSgAYLZfY4= github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= -github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= -github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2 v1.16.3/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= -github.com/aws/aws-sdk-go-v2 v1.16.4 h1:swQTEQUyJF/UkEA94/Ga55miiKFoXmm/Zd67XHgmjSg= -github.com/aws/aws-sdk-go-v2 v1.16.4/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= +github.com/aws/aws-sdk-go-v2 v1.21.2 h1:+LXZ0sgo8quN9UOKXXzAWRT3FWd4NxeXWOZom9pE7GA= +github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1 h1:SdK4Ppk5IzLs64ZMvr6MrSficMtjY2oS0WOORXTlxwU= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1/go.mod h1:n8Bs1ElDD2wJ9kCRTczA83gYbBmjSwZp3umc6zF4EeM= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= -github.com/aws/aws-sdk-go-v2/config v1.15.5 h1:P+xwhr6kabhxDTXTVH9YoHkqjLJ0wVVpIUHtFNr2hjU= github.com/aws/aws-sdk-go-v2/config v1.15.5/go.mod h1:ZijHHh0xd/A+ZY53az0qzC5tT46kt4JVCePf2NX9Lk4= +github.com/aws/aws-sdk-go-v2/config v1.18.45 h1:Aka9bI7n8ysuwPeFdm77nfbyHCAKQ3z9ghB3S/38zes= +github.com/aws/aws-sdk-go-v2/config v1.18.45/go.mod h1:ZwDUgFnQgsazQTnWfeLWk5GjeqTQTL8lMkoE1UXzxdE= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= -github.com/aws/aws-sdk-go-v2/credentials v1.12.0 h1:4R/NqlcRFSkR0wxOhgHi+agGpbEr5qMCjn7VqUIJY+E= github.com/aws/aws-sdk-go-v2/credentials v1.12.0/go.mod h1:9YWk7VW+eyKsoIL6/CljkTrNVWBSK9pkqOPUuijid4A= +github.com/aws/aws-sdk-go-v2/credentials v1.13.43 h1:LU8vo40zBlo3R7bAvBVy/ku4nxGEyZe9N8MqAeFTzF8= +github.com/aws/aws-sdk-go-v2/credentials v1.13.43/go.mod h1:zWJBz1Yf1ZtX5NGax9ZdNjhhI4rgjfgsyk6vTY1yfVg= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.6.0/go.mod h1:gqlclDEZp4aqJOancXK6TN24aKhT0W0Ae9MHk3wzTMM= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4 h1:FP8gquGeGHHdfY6G5llaMQDF+HAf20VKc8opRwmjf04= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.4/go.mod h1:u/s5/Z+ohUQOPXl00m2yJVyioWDECsbpXTQlaqSlufc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13 h1:PIktER+hwIG286DqXyvVENjgLTAwGgoeriLDD5C+YlQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13/go.mod h1:f/Ib/qYjhV2/qdsf79H3QP/eRE4AkVyEf6sk7XfZ1tg= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.10 h1:JL7cY85hyjlgfA29MMyAlItX+JYIH9XsxgMBS7jtlqA= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.10/go.mod h1:p+ul5bLZSDRRXCZ/vePvfmZBH9akozXBJA5oMshWa5U= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.10/go.mod h1:F+EZtuIwjlv35kRJPyBGcsA4f7bnSoz15zOQ2lJq1Z4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.11 h1:gsqHplNh1DaQunEKZISK56wlpbCg0yKxNVvGWCFuF1k= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.11/go.mod h1:tmUB6jakq5DFNcXsXOA/ZQ7/C8VnSKYkx58OI7Fh79g= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43 h1:nFBQlGtkbPzp/NjZLuFxRqmT91rLJkgvsEQs68h962Y= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43/go.mod h1:auo+PiyLl0n1l8A0e8RIeR8tOzYPfZZH/JNlrJ8igTQ= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.4/go.mod h1:8glyUqVIM4AmeenIsPo0oVh3+NUwnsQml2OFupfQW+0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.5 h1:PLFj+M2PgIDHG//hw3T0O0KLI4itVtAjtxrZx4AHPLg= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.5/go.mod h1:fV1AaS2gFc1tM0RCb015FJ0pvWVUfJZANzjwoO4YakM= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37 h1:JRVhO25+r3ar2mKGP7E0LDl8K9/G36gjlqca5iQbaqc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37/go.mod h1:Qe+2KtKml+FEsQF/DHmDV+xjtche/hwoF75EG4UlHW8= github.com/aws/aws-sdk-go-v2/internal/ini v1.2.4/go.mod h1:ZcBrrI3zBKlhGFNYWvju0I3TR93I7YIgAfy82Fh4lcQ= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.11 h1:6cZRymlLEIlDTEB0+5+An6Zj1CKt6rSE69tOmFeu1nk= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.11/go.mod h1:0MR+sS1b/yxsfAPvAESrw8NfwUoxMinDyw6EYR9BS2U= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45 h1:hze8YsjSh8Wl1rYa1CJpRmXP21BvOBuc76YhW0HsuQ4= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45/go.mod h1:lD5M20o09/LCuQ2mE62Mb/iSdSlCNuj6H5ci7tW7OsE= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.1 h1:C21IDZCm9Yu5xqjb3fKmxDoYvJXtw1DNlOmLZEIlY1M= github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.1/go.mod h1:l/BbcfqDCT3hePawhy4ZRtewjtdkl6GWtd9/U+1penQ= github.com/aws/aws-sdk-go-v2/service/appconfig v1.4.2/go.mod h1:FZ3HkCe+b10uFZZkFdvf98LHW21k49W8o8J366lqVKY= @@ -135,110 +69,72 @@ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1/go.mod h1:G github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.5 h1:9LSZqt4v1JiehyZTrQnRFf2mY/awmyYNNY/b7zqtduU= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.5/go.mod h1:S8TVP66AAkMMdYYCNZGvrdEq9YRm+qLXjio4FqRnrEE= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.2/go.mod h1:72HRZDLMtmVQiLG2tLfQcaWLCssELvGl+Zf2WVxMmR8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4 h1:b16QW0XWl0jWjLABFc1A+uh145Oqv+xDcObNk0iQgUk= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.4/go.mod h1:uKkN7qmSIsNJVyMtxNQoCEYMvFEXbOg9fwCJPdfp2u8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37 h1:WWZA/I2K4ptBS1kg0kV1JbBtG/umed0vwHRrmcr9z7k= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37/go.mod h1:vBmDnwWXWxNPFRMmG2m/3MKOe+xEcMDo1tanpaWCcck= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.4 h1:RE/DlZLYrz1OOmq8F28IXHLksuuvlpzUbvJ+SESCZBI= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.4/go.mod h1:oudbsSdDtazNj47z1ut1n37re9hDsKpk2ZI3v7KSxq0= github.com/aws/aws-sdk-go-v2/service/s3 v1.26.9 h1:LCQKnopq2t4oQS3VKivlYTzAHCTJZZoQICM9fny7KHY= github.com/aws/aws-sdk-go-v2/service/s3 v1.26.9/go.mod h1:iMYipLPXlWpBJ0KFX7QJHZ84rBydHBY8as2aQICTPWk= github.com/aws/aws-sdk-go-v2/service/sso v1.4.2/go.mod h1:NBvT9R1MEF+Ud6ApJKM0G+IkPchKS7p7c2YPKwHmBOk= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.4 h1:Uw5wBybFQ1UeA9ts0Y07gbv0ncZnIAyw858tDW0NP2o= github.com/aws/aws-sdk-go-v2/service/sso v1.11.4/go.mod h1:cPDwJwsP4Kff9mldCXAmddjJL6JGQqtA3Mzer2zyr88= +github.com/aws/aws-sdk-go-v2/service/sso v1.15.2 h1:JuPGc7IkOP4AaqcZSIcyqLpFSqBWK32rM9+a1g6u73k= +github.com/aws/aws-sdk-go-v2/service/sso v1.15.2/go.mod h1:gsL4keucRCgW+xA85ALBpRFfdSLH4kHOVSnLMSuBECo= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3 h1:HFiiRkf1SdaAmV3/BHOFZ9DjFynPHj8G/UIO1lQS+fk= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3/go.mod h1:a7bHA82fyUXOm+ZSWKU6PIoBxrjSprdLoM8xPYvzYVg= github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21TfrhJ8AEMzVybRNSb/b4g= -github.com/aws/aws-sdk-go-v2/service/sts v1.16.4 h1:+xtV90n3abQmgzk1pS++FdxZTrPEDgQng6e4/56WR2A= github.com/aws/aws-sdk-go-v2/service/sts v1.16.4/go.mod h1:lfSYenAXtavyX2A1LsViglqlG9eEFYxNryTZS5rn3QE= +github.com/aws/aws-sdk-go-v2/service/sts v1.23.2 h1:0BkLfgeDjfZnZ+MhB3ONb01u9pwFYTCZVhlsSSBvlbU= +github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.11.2 h1:eG/N+CcUMAvsdffgMvjMKwfyDzIkjM6pfxMJ8Mzc6mE= github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= +github.com/aws/smithy-go v1.15.0 h1:PS/durmlzvAFpQHDs4wi4sNNP9ExsqZh6IlfdHXgKK8= +github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo= -github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc= github.com/blendle/zapdriver v1.3.2-0.20200203083823-9200777f8a3d h1:fSlGu5ePbkjBidXuj2O5j9EcYrVB5Cr6/wdkYyDgxZk= github.com/blendle/zapdriver v1.3.2-0.20200203083823-9200777f8a3d/go.mod h1:yCBkgASmKHgUOFjK9h1sOytUVgA+JkQjqj3xYP4AdWY= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= -github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= -github.com/btcsuite/btcd v0.0.0-20190605094302-a0d1e3e36d50/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= -github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= -github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94= github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2 h1:KdUfX2zKommPRa+PD0sWZUyXe9w277ABlgELO7H04IM= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.2/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= -github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= -github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= -github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= -github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= -github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= -github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= -github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= -github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cavaliergopher/grab/v3 v3.0.1 h1:4z7TkBfmPjmLAAmkkAZNX/6QJ1nNFdv3SdIHXju0Fr4= github.com/cavaliergopher/grab/v3 v3.0.1/go.mod h1:1U/KNnD+Ft6JJiYoYBAimKH2XrYptb8Kl3DFGmsjpq4= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= -github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/ceramicnetwork/go-dag-jose v0.1.0 h1:yJ/HVlfKpnD3LdYP03AHyTvbm3BpPiz2oZiOeReJRdU= -github.com/ceramicnetwork/go-dag-jose v0.1.0/go.mod h1:qYA1nYt0X8u4XoMAVoOV3upUVKtrxy/I670Dg5F0wjI= github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= github.com/cockroachdb/errors v1.9.1 h1:yFVvsI0VxmRShfawbt/laCIDy/mtTqqnvoNgiy5bEV8= github.com/cockroachdb/errors v1.9.1/go.mod h1:2sxOtL2WIc096WSZqZ5h8fa17rdDq9HZOZLBCor4mBk= +github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f h1:6jduT9Hfc0njg5jJ1DdKCFPdMBrp/mdZfCpa5h+WM74= github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= -github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= -github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= github.com/cockroachdb/redact v1.1.3 h1:AKZds10rFSIj7qADf0g46UixK8NNLwWTNdCIGS5wfSQ= github.com/cockroachdb/redact v1.1.3/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codeclysm/extract/v3 v3.0.2 h1:sB4LcE3Php7LkhZwN0n2p8GCwZe92PEQutdbGURf5xc= github.com/codeclysm/extract/v3 v3.0.2/go.mod h1:NKsw+hqua9H+Rlwy/w/3Qgt9jDonYEgB6wJu+25eOKw= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= @@ -246,58 +142,31 @@ github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/Yj github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= -github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= -github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 h1:HVTnpeuvF6Owjd5mniCL8DEXo7uYXdQEmOP4FJbV5tg= -github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3/go.mod h1:p1d6YEZWvFzEh4KLyvBcVSnrfNDDvK2zfK/4x2v/4pE= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cskr/pubsub v1.0.2 h1:vlOzMhl6PFn60gRlTQQsIfVwaPB/B/8MziK8FhEPt/0= -github.com/cskr/pubsub v1.0.2/go.mod h1:/8MzYXk/NJAz782G8RPkFzXTZVu63VotefPnR9TIRis= -github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= -github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= -github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= -github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= -github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= -github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= -github.com/dgraph-io/badger v1.6.1/go.mod h1:FRmFw3uxvcpa8zG3Rxs0th+hCLIuaQg8HlNV5bjgnuU= -github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8= -github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE= github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs= github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak= -github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= @@ -305,62 +174,31 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk= github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 h1:qwcF+vdFrvPSEUDSX5RVoRccG8a5DhOdWdQ4zN62zzo= github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4= github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y= github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= -github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= -github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= -github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= -github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= -github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog= github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0= -github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 h1:BBso6MBKW8ncyZLv37o+KNyy0HrrHgfnOaGQC2qvN+A= -github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5/go.mod h1:JpoxHjuQauoxiFMl1ie8Xc/7TfLuMZ5eOCONd1sUBHg= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= -github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= -github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= -github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= +github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= +github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= @@ -370,63 +208,31 @@ github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZ github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= -github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg= -github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y= +github.com/gdamore/tcell/v2 v2.7.1 h1:TiCcmpWHiAU7F0rA2I3S2Y4mmLmO9KHxJ7E1QhYzQbc= +github.com/gdamore/tcell/v2 v2.7.1/go.mod h1:dSXtXTSK0VsW1biw65DZLZ2NKr7j0qP/0J7ONmsraWg= github.com/getsentry/sentry-go v0.12.0/go.mod h1:NSap0JBYWzHND8oMbyi0+XZhUalc1TBdRL1M71JZW2c= github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= -github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= -github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= -github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ= -github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg= -github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU= github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= @@ -434,59 +240,30 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= -github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= -github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= +github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gobwas/ws-examples v0.0.0-20190625122829-a9e8908d9484 h1:XC9N1eiAyO1zg62dpOU8bex8emB/zluUtKcbLNjJxGI= github.com/gobwas/ws-examples v0.0.0-20190625122829-a9e8908d9484/go.mod h1:5nDZF4afNA1S7ZKcBXCMvDo4nuCTp1931DND7/W4aXo= -github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog= -github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= -github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68= -github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191027212112-611e8accdfc9/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -497,100 +274,48 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg= -github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= +github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw= +github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= -github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= -github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg= -github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= -github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b h1:RMpPgZTSApbPf7xaVel+QkoGPRLFLrwFO89uDUHEGf0= +github.com/google/pprof v0.0.0-20231023181126-ff6d637d2a7b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= -github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/graph-gophers/graphql-go v1.3.0 h1:Eb9x/q6MFpCLz7jBCiP/WTxjSDrYLR1QY41SORZyNJ0= github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= -github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= -github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/h2non/filetype v1.0.6 h1:g84/+gdkAT1hnYO+tHpCLoikm13Ju55OkN4KCb1uGEQ= github.com/h2non/filetype v1.0.6/go.mod h1:isekKqOuhMj+s/7r3rIeTErIRy4Rub5uBWHfvMusLMU= -github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e h1:3YKHER4nmd7b5qy5t0GWDTwSn4OyRgfAXSmo6VnryBY= -github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e/go.mod h1:I8h3MITA53gN9OnWGCgaMa0JWVRdXthWw4M3CPM54OY= -github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= -github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= @@ -598,35 +323,21 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/golang-lru/v2 v2.0.2 h1:Dwmkdr5Nc/oBiXgJS3CDHNhJtIHkuZ3DZF5twqnfBdU= -github.com/hashicorp/golang-lru/v2 v2.0.2/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= @@ -635,621 +346,113 @@ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZ github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= -github.com/holiman/uint256 v1.2.3 h1:K8UWO1HUJpRMXBxbmaY1Y8IAMZC/RsKB+ArEnnK4l5o= -github.com/holiman/uint256 v1.2.3/go.mod h1:SC8Ryt4n+UBbPbIBKaG9zbbDlp4jOru9xFZmPzLUTxw= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= -github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= -github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= github.com/hydrogen18/memlistener v0.0.0-20200120041712-dcc25e7acd91/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI= -github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= -github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/go-bitfield v1.0.0/go.mod h1:N/UiujQy+K+ceU1EF5EkVd1TNqevLrCQMIcAEPrdtus= -github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= -github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= -github.com/ipfs/go-bitswap v0.1.0/go.mod h1:FFJEf18E9izuCqUtHxbWEvq+reg7o4CW5wSAE1wsxj0= -github.com/ipfs/go-bitswap v0.1.2/go.mod h1:qxSWS4NXGs7jQ6zQvoPY3+NmOfHHG47mhkiLzBpJQIs= -github.com/ipfs/go-bitswap v0.5.1/go.mod h1:P+ckC87ri1xFLvk74NlXdP0Kj9RmWAh4+H78sC6Qopo= -github.com/ipfs/go-bitswap v0.6.0/go.mod h1:Hj3ZXdOC5wBJvENtdqsixmzzRukqd8EHLxZLZc3mzRA= -github.com/ipfs/go-bitswap v0.11.0 h1:j1WVvhDX1yhG32NTC9xfxnqycqYIlhzEzLXG/cU1HyQ= -github.com/ipfs/go-block-format v0.0.1/go.mod h1:DK/YYcsSUIVAFNwo/KZCdIIbpN0ROH/baNLgayt4pFc= -github.com/ipfs/go-block-format v0.0.2/go.mod h1:AWR46JfpcObNfg3ok2JHDUfdiHRgWhJgCQF+KIgOPJY= -github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= -github.com/ipfs/go-block-format v0.1.1 h1:129vSO3zwbsYADcyQWcOYiuCpAqt462SFfqFHdFJhhI= -github.com/ipfs/go-block-format v0.1.1/go.mod h1:+McEIT+g52p+zz5xGAABGSOKrzmrdX97bc0USBdWPUs= -github.com/ipfs/go-blockservice v0.1.0/go.mod h1:hzmMScl1kXHg3M2BjTymbVPjv627N7sYcvYaKbop39M= -github.com/ipfs/go-blockservice v0.2.1/go.mod h1:k6SiwmgyYgs4M/qt+ww6amPeUH9EISLRBnvUurKJhi8= -github.com/ipfs/go-blockservice v0.3.0/go.mod h1:P5ppi8IHDC7O+pA0AlGTF09jruB2h+oP3wVVaZl8sfk= -github.com/ipfs/go-blockservice v0.5.1 h1:9pAtkyKAz/skdHTh0kH8VulzWp+qmSDD0aI17TYP/s0= -github.com/ipfs/go-blockservice v0.5.1/go.mod h1:VpMblFEqG67A/H2sHKAemeH9vlURVavlysbdUI632yk= -github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= -github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= -github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= -github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= -github.com/ipfs/go-cid v0.1.0/go.mod h1:rH5/Xv83Rfy8Rw6xG+id3DYAMUVmem1MowoKwdXmN2o= -github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= -github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= -github.com/ipfs/go-cidutil v0.1.0 h1:RW5hO7Vcf16dplUU60Hs0AKDkQAVPVplr7lk97CFL+Q= -github.com/ipfs/go-cidutil v0.1.0/go.mod h1:e7OEVBMIv9JaOxt9zaGEmAoSlXW9jdFZ5lP/0PwcfpA= -github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.0.5/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= -github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= -github.com/ipfs/go-datastore v0.3.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= -github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= -github.com/ipfs/go-datastore v0.4.5/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w3fyfrmmJs= -github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk= -github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk= -github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8= -github.com/ipfs/go-delegated-routing v0.7.0 h1:43FyMnKA+8XnyX68Fwg6aoGkqrf8NS5aG7p644s26PU= -github.com/ipfs/go-delegated-routing v0.7.0/go.mod h1:u4zxjUWIe7APUW5ds9CfD0tJX3vM9JhIeNqA8kE4vHE= -github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= -github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= -github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8= -github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s= -github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= -github.com/ipfs/go-ds-badger v0.2.1/go.mod h1:Tx7l3aTph3FMFrRS838dcSJh+jjA7cX9DrGVwx/NOwE= -github.com/ipfs/go-ds-badger v0.2.3/go.mod h1:pEYw0rgg3FIrywKKnL+Snr+w/LjJZVMTBRn4FS6UHUk= -github.com/ipfs/go-ds-badger v0.3.0 h1:xREL3V0EH9S219kFFueOYJJTcjgNSZ2HY1iSvN7U1Ro= -github.com/ipfs/go-ds-badger v0.3.0/go.mod h1:1ke6mXNqeV8K3y5Ak2bAA0osoTfmxUdupVCGm4QUIek= -github.com/ipfs/go-ds-flatfs v0.5.1 h1:ZCIO/kQOS/PSh3vcF1H6a8fkRGS7pOfwfPdx4n/KJH4= -github.com/ipfs/go-ds-flatfs v0.5.1/go.mod h1:RWTV7oZD/yZYBKdbVIFXTX2fdY2Tbvl94NsWqmoyAX4= -github.com/ipfs/go-ds-leveldb v0.0.1/go.mod h1:feO8V3kubwsEF22n0YRQCffeb79OOYIykR4L04tMOYc= -github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= -github.com/ipfs/go-ds-leveldb v0.4.1/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= -github.com/ipfs/go-ds-leveldb v0.4.2/go.mod h1:jpbku/YqBSsBc1qgME8BkWS4AxzF2cEu1Ii2r79Hh9s= -github.com/ipfs/go-ds-leveldb v0.5.0 h1:s++MEBbD3ZKc9/8/njrn4flZLnCuY9I79v94gBUNumo= -github.com/ipfs/go-ds-leveldb v0.5.0/go.mod h1:d3XG9RUDzQ6V4SHi8+Xgj9j1XuEk1z82lquxrVbml/Q= -github.com/ipfs/go-ds-measure v0.2.0 h1:sG4goQe0KDTccHMyT45CY1XyUbxe5VwTKpg2LjApYyQ= -github.com/ipfs/go-ds-measure v0.2.0/go.mod h1:SEUD/rE2PwRa4IQEC5FuNAmjJCyYObZr9UvVh8V3JxE= -github.com/ipfs/go-fetcher v1.6.1 h1:UFuRVYX5AIllTiRhi5uK/iZkfhSpBCGX7L70nSZEmK8= -github.com/ipfs/go-fetcher v1.6.1/go.mod h1:27d/xMV8bodjVs9pugh/RCjjK2OZ68UgAMspMdingNo= -github.com/ipfs/go-filestore v1.2.0 h1:O2wg7wdibwxkEDcl7xkuQsPvJFRBVgVSsOJ/GP6z3yU= -github.com/ipfs/go-filestore v1.2.0/go.mod h1:HLJrCxRXquTeEEpde4lTLMaE/MYJZD7WHLkp9z6+FF8= -github.com/ipfs/go-fs-lock v0.0.7 h1:6BR3dajORFrFTkb5EpCUFIAypsoxpGpDSVUdFwzgL9U= -github.com/ipfs/go-fs-lock v0.0.7/go.mod h1:Js8ka+FNYmgQRLrRXzU3CB/+Csr1BwrRilEcvYrHhhc= -github.com/ipfs/go-graphsync v0.14.1 h1:tvFpBY9LcehIB7zi5SZIa+7aoxBOrGbdekhOXdnlT70= -github.com/ipfs/go-graphsync v0.14.1/go.mod h1:S6O/c5iXOXqDgrQgiZSgOTRUSiVvpKEhrzqFHKnLVcs= -github.com/ipfs/go-ipfs-blockstore v0.0.1/go.mod h1:d3WClOmRQKFnJ0Jz/jj/zmksX0ma1gROTlovZKBmN08= -github.com/ipfs/go-ipfs-blockstore v0.1.0/go.mod h1:5aD0AvHPi7mZc6Ci1WCAhiBQu2IsfTduLl+422H6Rqw= -github.com/ipfs/go-ipfs-blockstore v0.2.1/go.mod h1:jGesd8EtCM3/zPgx+qr0/feTXGUeRai6adgwC+Q+JvE= -github.com/ipfs/go-ipfs-blockstore v1.2.0 h1:n3WTeJ4LdICWs/0VSfjHrlqpPpl6MZ+ySd3j8qz0ykw= -github.com/ipfs/go-ipfs-blockstore v1.2.0/go.mod h1:eh8eTFLiINYNSNawfZOC7HOxNTxpB1PFuA5E1m/7exE= -github.com/ipfs/go-ipfs-blocksutil v0.0.1 h1:Eh/H4pc1hsvhzsQoMEP3Bke/aW5P5rVM1IWFJMcGIPQ= -github.com/ipfs/go-ipfs-blocksutil v0.0.1/go.mod h1:Yq4M86uIOmxmGPUHv/uI7uKqZNtLb449gwKqXjIsnRk= -github.com/ipfs/go-ipfs-chunker v0.0.1/go.mod h1:tWewYK0we3+rMbOh7pPFGDyypCtvGcBFymgY4rSDLAw= -github.com/ipfs/go-ipfs-chunker v0.0.5 h1:ojCf7HV/m+uS2vhUGWcogIIxiO5ubl5O57Q7NapWLY8= -github.com/ipfs/go-ipfs-chunker v0.0.5/go.mod h1:jhgdF8vxRHycr00k13FM8Y0E+6BoalYeobXmUyTreP8= -github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= -github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= -github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= -github.com/ipfs/go-ipfs-ds-help v0.0.1/go.mod h1:gtP9xRaZXqIQRh1HRpp595KbBEdgqWFxefeVKOV8sxo= -github.com/ipfs/go-ipfs-ds-help v0.1.1/go.mod h1:SbBafGJuGsPI/QL3j9Fc5YPLeAu+SzOkI0gFwAg+mOs= -github.com/ipfs/go-ipfs-ds-help v1.1.0 h1:yLE2w9RAsl31LtfMt91tRZcrx+e61O5mDxFRR994w4Q= -github.com/ipfs/go-ipfs-ds-help v1.1.0/go.mod h1:YR5+6EaebOhfcqVCyqemItCLthrpVNot+rsOU/5IatU= -github.com/ipfs/go-ipfs-exchange-interface v0.0.1/go.mod h1:c8MwfHjtQjPoDyiy9cFquVtVHkO9b9Ob3FG91qJnWCM= -github.com/ipfs/go-ipfs-exchange-interface v0.1.0/go.mod h1:ych7WPlyHqFvCi/uQI48zLZuAWVP5iTQPXEfVaw5WEI= -github.com/ipfs/go-ipfs-exchange-interface v0.2.0 h1:8lMSJmKogZYNo2jjhUs0izT+dck05pqUw4mWNW9Pw6Y= -github.com/ipfs/go-ipfs-exchange-interface v0.2.0/go.mod h1:z6+RhJuDQbqKguVyslSOuVDhqF9JtTrO3eptSAiW2/Y= -github.com/ipfs/go-ipfs-exchange-offline v0.0.1/go.mod h1:WhHSFCVYX36H/anEKQboAzpUws3x7UeEGkzQc3iNkM0= -github.com/ipfs/go-ipfs-exchange-offline v0.1.1/go.mod h1:vTiBRIbzSwDD0OWm+i3xeT0mO7jG2cbJYatp3HPk5XY= -github.com/ipfs/go-ipfs-exchange-offline v0.2.0/go.mod h1:HjwBeW0dvZvfOMwDP0TSKXIHf2s+ksdP4E3MLDRtLKY= -github.com/ipfs/go-ipfs-exchange-offline v0.3.0 h1:c/Dg8GDPzixGd0MC8Jh6mjOwU57uYokgWRFidfvEkuA= -github.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB02e++Z7521fMxqCNRrz9s= -github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4= -github.com/ipfs/go-ipfs-keystore v0.1.0 h1:gfuQUO/cyGZgZIHE6OrJas4OnwuxXCqJG7tI0lrB5Qc= -github.com/ipfs/go-ipfs-keystore v0.1.0/go.mod h1:LvLw7Qhnb0RlMOfCzK6OmyWxICip6lQ06CCmdbee75U= -github.com/ipfs/go-ipfs-pinner v0.3.0 h1:jwe5ViX3BON3KgOAYrrhav2+1ONB0QzFAWQd7HUlbuM= -github.com/ipfs/go-ipfs-pinner v0.3.0/go.mod h1:oX0I0nC6zlNIh0LslSrUnjfNKPq8ufoFtqV1/wcJvyo= -github.com/ipfs/go-ipfs-posinfo v0.0.1 h1:Esoxj+1JgSjX0+ylc0hUmJCOv6V2vFoZiETLR6OtpRs= -github.com/ipfs/go-ipfs-posinfo v0.0.1/go.mod h1:SwyeVP+jCwiDu0C313l/8jg6ZxM0qqtlt2a0vILTc1A= -github.com/ipfs/go-ipfs-pq v0.0.1/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= -github.com/ipfs/go-ipfs-pq v0.0.2/go.mod h1:LWIqQpqfRG3fNc5XsnIhz/wQ2XXGyugQwls7BgUmUfY= -github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE= -github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4= -github.com/ipfs/go-ipfs-provider v0.8.1 h1:qt670pYmcNH3BCjyXDgg07o2WsTRsOdMwYc25ukCdjQ= -github.com/ipfs/go-ipfs-provider v0.8.1/go.mod h1:qCpwpoohIRVXvNzkygzsM3qdqP/sXlrogtA5I45tClc= -github.com/ipfs/go-ipfs-routing v0.1.0/go.mod h1:hYoUkJLyAUKhF58tysKpids8RNDPO42BVMgK5dNsoqY= -github.com/ipfs/go-ipfs-routing v0.2.1/go.mod h1:xiNNiwgjmLqPS1cimvAw6EyB9rkVDbiocA4yY+wRNLM= -github.com/ipfs/go-ipfs-routing v0.3.0 h1:9W/W3N+g+y4ZDeffSgqhgo7BsBSJwPMcyssET9OWevc= -github.com/ipfs/go-ipfs-routing v0.3.0/go.mod h1:dKqtTFIql7e1zYsEuWLyuOU+E0WJWW8JjbTPLParDWo= -github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= -github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8= -github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ= -github.com/ipfs/go-ipld-cbor v0.0.2/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= -github.com/ipfs/go-ipld-cbor v0.0.3/go.mod h1:wTBtrQZA3SoFKMVkp6cn6HMRteIB1VsmHA0AQFOn7Nc= -github.com/ipfs/go-ipld-cbor v0.0.5/go.mod h1:BkCduEx3XBCO6t2Sfo5BaHzuok7hbhdMm9Oh8B2Ftq4= -github.com/ipfs/go-ipld-cbor v0.0.6 h1:pYuWHyvSpIsOOLw4Jy7NbBkCyzLDcl64Bf/LZW7eBQ0= -github.com/ipfs/go-ipld-cbor v0.0.6/go.mod h1:ssdxxaLJPXH7OjF5V4NSjBbcfh+evoR4ukuru0oPXMA= -github.com/ipfs/go-ipld-format v0.0.1/go.mod h1:kyJtbkDALmFHv3QR6et67i35QzO3S0dCDnkOJhcZkms= -github.com/ipfs/go-ipld-format v0.0.2/go.mod h1:4B6+FM2u9OJ9zCV+kSbgFAZlOrv1Hqbf0INGQgiKf9k= -github.com/ipfs/go-ipld-format v0.2.0/go.mod h1:3l3C1uKoadTPbeNfrDi+xMInYKlx2Cvg1BuydPSdzQs= -github.com/ipfs/go-ipld-format v0.3.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= -github.com/ipfs/go-ipld-format v0.4.0 h1:yqJSaJftjmjc9jEOFYlpkwOLVKv68OD27jFLlSghBlQ= -github.com/ipfs/go-ipld-format v0.4.0/go.mod h1:co/SdBE8h99968X0hViiw1MNlh6fvxxnHpvVLnH7jSM= -github.com/ipfs/go-ipld-git v0.1.1 h1:TWGnZjS0htmEmlMFEkA3ogrNCqWjIxwr16x1OsdhG+Y= -github.com/ipfs/go-ipld-git v0.1.1/go.mod h1:+VyMqF5lMcJh4rwEppV0e6g4nCCHXThLYYDpKUkJubI= -github.com/ipfs/go-ipld-legacy v0.1.0/go.mod h1:86f5P/srAmh9GcIcWQR9lfFLZPrIyyXQeVlOWeeWEuI= -github.com/ipfs/go-ipld-legacy v0.1.1 h1:BvD8PEuqwBHLTKqlGFTHSwrwFOMkVESEvwIYwR2cdcc= -github.com/ipfs/go-ipld-legacy v0.1.1/go.mod h1:8AyKFCjgRPsQFf15ZQgDB8Din4DML/fOmKZkkFkrIEg= -github.com/ipfs/go-ipns v0.3.0 h1:ai791nTgVo+zTuq2bLvEGmWP1M0A6kGTXUsgv/Yq67A= -github.com/ipfs/go-ipns v0.3.0/go.mod h1:3cLT2rbvgPZGkHJoPO1YMJeh6LtkxopCkKFcio/wE24= -github.com/ipfs/go-libipfs v0.6.2 h1:QUf3kS3RrCjgtE0QW2d18PFFfOLeEt24Ft892ipLzRI= -github.com/ipfs/go-libipfs v0.6.2/go.mod h1:FmhKgxMOQA572TK5DA3MZ5GL44ZqsMHIrkgK4gLn4A8= -github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= -github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= -github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= -github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= -github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= -github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= -github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= -github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= -github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= -github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= -github.com/ipfs/go-log/v2 v2.3.0/go.mod h1:QqGoj30OTpnKaG/LKTGTxoP2mmQtjVMEnK72gynbe/g= -github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= -github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= -github.com/ipfs/go-merkledag v0.2.3/go.mod h1:SQiXrtSts3KGNmgOzMICy5c0POOpUNQLvB3ClKnBAlk= -github.com/ipfs/go-merkledag v0.3.2/go.mod h1:fvkZNNZixVW6cKSZ/JfLlON5OlgTXNdRLz0p6QG/I2M= -github.com/ipfs/go-merkledag v0.5.1/go.mod h1:cLMZXx8J08idkp5+id62iVftUQV+HlYJ3PIhDfZsjA4= -github.com/ipfs/go-merkledag v0.6.0/go.mod h1:9HSEwRd5sV+lbykiYP+2NC/3o6MZbKNaa4hfNcH5iH0= -github.com/ipfs/go-merkledag v0.9.0 h1:DFC8qZ96Dz1hMT7dtIpcY524eFFDiEWAF8hNJHWW2pk= -github.com/ipfs/go-merkledag v0.9.0/go.mod h1:bPHqkHt5OZ0p1n3iqPeDiw2jIBkjAytRjS3WSBwjq90= -github.com/ipfs/go-metrics-interface v0.0.1 h1:j+cpbjYvu4R8zbleSs36gvB7jR+wsL2fGD6n0jO4kdg= -github.com/ipfs/go-metrics-interface v0.0.1/go.mod h1:6s6euYU4zowdslK0GKHmqaIZ3j/b/tL7HTWtJ4VPgWY= -github.com/ipfs/go-mfs v0.2.1 h1:5jz8+ukAg/z6jTkollzxGzhkl3yxm022Za9f2nL5ab8= -github.com/ipfs/go-mfs v0.2.1/go.mod h1:Woj80iuw4ajDnIP6+seRaoHpPsc9hmL0pk/nDNDWP88= -github.com/ipfs/go-namesys v0.7.0 h1:xqosk71GIVRkFDtF2UNRcXn4LdNeo7tzuy8feHD6NbU= -github.com/ipfs/go-namesys v0.7.0/go.mod h1:KYSZBVZG3VJC34EfqqJPG7T48aWgxseoMPAPA5gLyyQ= -github.com/ipfs/go-path v0.2.1/go.mod h1:NOScsVgxfC/eIw4nz6OiGwK42PjaSJ4Y/ZFPn1Xe07I= -github.com/ipfs/go-path v0.3.1 h1:wkeaCWE/NTuuPGlEkLTsED5UkzfKYZpxaFFPgk8ZVLE= -github.com/ipfs/go-path v0.3.1/go.mod h1:eNLsxJEEMxn/CDzUJ6wuNl+6No6tEUhOZcPKsZsYX0E= -github.com/ipfs/go-peertaskqueue v0.1.0/go.mod h1:Jmk3IyCcfl1W3jTW3YpghSwSEC6IJ3Vzz/jUmWw8Z0U= -github.com/ipfs/go-peertaskqueue v0.7.0/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68ow0Rrb04donIU= -github.com/ipfs/go-peertaskqueue v0.8.1 h1:YhxAs1+wxb5jk7RvS0LHdyiILpNmRIRnZVztekOF0pg= -github.com/ipfs/go-peertaskqueue v0.8.1/go.mod h1:Oxxd3eaK279FxeydSPPVGHzbwVeHjatZ2GA8XD+KbPU= -github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw= -github.com/ipfs/go-unixfs v0.3.1/go.mod h1:h4qfQYzghiIc8ZNFKiLMFWOTzrWIAtzYQ59W/pCFf1o= -github.com/ipfs/go-unixfs v0.4.4 h1:D/dLBOJgny5ZLIur2vIXVQVW0EyDHdOMBDEhgHrt6rY= -github.com/ipfs/go-unixfs v0.4.4/go.mod h1:TSG7G1UuT+l4pNj91raXAPkX0BhJi3jST1FDTfQ5QyM= -github.com/ipfs/go-unixfsnode v1.1.2/go.mod h1:5dcE2x03pyjHk4JjamXmunTMzz+VUtqvPwZjIEkfV6s= -github.com/ipfs/go-unixfsnode v1.5.2 h1:CvsiTt58W2uR5dD8bqQv+aAY0c1qolmXmSyNbPHYiew= -github.com/ipfs/go-unixfsnode v1.5.2/go.mod h1:NlOebRwYx8lMCNMdhAhEspYPBD3obp7TE0LvBqHY+ks= -github.com/ipfs/go-verifcid v0.0.1/go.mod h1:5Hrva5KBeIog4A+UpqlaIU+DEstipcJYQQZc0g37pY0= -github.com/ipfs/go-verifcid v0.0.2 h1:XPnUv0XmdH+ZIhLGKg6U2vaPaRDXb9urMyNVCE7uvTs= -github.com/ipfs/go-verifcid v0.0.2/go.mod h1:40cD9x1y4OWnFXbLNJYRe7MpNvWlMn3LZAG5Wb4xnPU= -github.com/ipfs/interface-go-ipfs-core v0.11.0 h1:n1tplrwsz7oZXkpkZM5a3MDBxksMfSQ103ej4e+l7NA= -github.com/ipfs/interface-go-ipfs-core v0.11.0/go.mod h1:xmnoccUXY7N/Q8AIx0vFqgW926/FAZ8+do/1NTEHKsU= -github.com/ipfs/kubo v0.19.1 h1:jQmwct9gurfZcpShmfwZf/0CXSgxgTVWJxx//l4Ob3M= -github.com/ipfs/kubo v0.19.1/go.mod h1:jD1cb+H5ax9EzxLflHG8dz5LHfuAMO+r00/h3MwYkd4= -github.com/ipld/edelweiss v0.2.0 h1:KfAZBP8eeJtrLxLhi7r3N0cBCo7JmwSRhOJp3WSpNjk= -github.com/ipld/edelweiss v0.2.0/go.mod h1:FJAzJRCep4iI8FOFlRriN9n0b7OuX3T/S9++NpBDmA4= -github.com/ipld/go-car v0.5.0 h1:kcCEa3CvYMs0iE5BzD5sV7O2EwMiCIp3uF8tA6APQT8= -github.com/ipld/go-car/v2 v2.5.1 h1:U2ux9JS23upEgrJScW8VQuxmE94560kYxj9CQUpcfmk= -github.com/ipld/go-codec-dagpb v1.3.0/go.mod h1:ga4JTU3abYApDC3pZ00BC2RSvC3qfBb9MSJkMLSwnhA= -github.com/ipld/go-codec-dagpb v1.5.0 h1:RspDRdsJpLfgCI0ONhTAnbHdySGD4t+LHSPK4X1+R0k= -github.com/ipld/go-codec-dagpb v1.5.0/go.mod h1:0yRIutEFD8o1DGVqw4RSHh+BUTlJA9XWldxaaWR/o4g= -github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvBLMr4PX1gWptgkzRjVZCrLmSGcZCb/jioOQwCqZN8= -github.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHtEaLglS3ZeV8= -github.com/ipld/go-ipld-prime v0.14.1/go.mod h1:QcE4Y9n/ZZr8Ijg5bGPT0GqYWgZ1704nH0RDcQtgTP0= -github.com/ipld/go-ipld-prime v0.19.0 h1:5axC7rJmPc17Emw6TelxGwnzALk0PdupZ2oj2roDj04= -github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4= github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI= github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0= github.com/iris-contrib/jade v1.1.3/go.mod h1:H/geBymxJhShH5kecoiOCSssPX7QWYH7UaeZTSWddIk= github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0GqwkjqxNd0u65g= github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= -github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= -github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= -github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs= -github.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc= -github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= -github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c h1:uUx61FiAa1GI6ZmVd2wf2vULeQZIKG66eybjNXKYCz4= -github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs= -github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= -github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= -github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= -github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= -github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= -github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= -github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/juju/clock v0.0.0-20180524022203-d293bb356ca4/go.mod h1:nD0vlnrUjcjJhqN5WuCWZyzfd5AHZAC9/ajvbSx69xA= github.com/juju/errors v0.0.0-20150916125642-1b5e39b83d18/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5 h1:rhqTjzJlm7EbkELJDKMTU7udov+Se0xZkWmugr6zGok= github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= -github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9 h1:Y+lzErDTURqeXqlqYi4YBYbDd7ycU74gW1ADt57/bgY= github.com/juju/loggo v0.0.0-20170605014607-8232ab8918d9/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/loggo v0.0.0-20180524022052-584905176618 h1:MK144iBQF9hTSwBW/9eJm034bVoG30IshVm688T2hi8= +github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= github.com/juju/retry v0.0.0-20160928201858-1998d01ba1c3/go.mod h1:OohPQGsr4pnxwD5YljhQ+TZnuVRYpa5irjugL1Yuif4= github.com/juju/testing v0.0.0-20200510222523-6c8c298c77a0 h1:+WWUkhnTjV6RNOxkcwk79qrjeyHEHvBzlneueBsatX4= github.com/juju/testing v0.0.0-20200510222523-6c8c298c77a0/go.mod h1:hpGvhGHPVbNBraRLZEhoQwFLMrjK8PSlO4D3nDjKYXo= github.com/juju/utils v0.0.0-20180808125547-9dfc6dbfb02b/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk= github.com/juju/version v0.0.0-20161031051906-1f41e27e54f2/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= -github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= github.com/kataras/golog v0.0.10/go.mod h1:yJ8YKCmyL+nWjERB90Qwn+bdyBZsaQwU3bTVFgkFIp8= github.com/kataras/iris/v12 v12.1.8/go.mod h1:LMYy4VlP67TQ3Zgriz8RE2h2kMZV2SgMYbq3UhfoFmE= github.com/kataras/neffos v0.0.14/go.mod h1:8lqADm8PnbeFfL7CLXh1WHw53dG27MC3pgi2R1rmoTE= github.com/kataras/pio v0.0.2/go.mod h1:hAoW0t9UmXi4R5Oyq5Z4irTbaTsOemSrDGUtaTl7Dro= github.com/kataras/sitemap v0.0.5/go.mod h1:KY2eugMKiPwsJgx7+U103YZehfvNGOXURubcGyk0Bz8= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= -github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= -github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= +github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= -github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/knadh/koanf v1.4.0 h1:/k0Bh49SqLyLNfte9r6cvuZWrApOQhglOmhIU3L/zDw= github.com/knadh/koanf v1.4.0/go.mod h1:1cfH5223ZeZUOs8FU2UdTmaNfHpqgtjV0+NHjRO43gs= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= -github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= -github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= -github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= -github.com/libp2p/go-buffer-pool v0.0.1/go.mod h1:xtyIz9PMobb13WaxR6Zo1Pd1zXJKYg0a8KiIvDp3TzQ= -github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM= -github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= -github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= -github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= -github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= -github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= -github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= -github.com/libp2p/go-conn-security-multistream v0.2.1/go.mod h1:cR1d8gA0Hr59Fj6NhaTpFhJZrjSYuNmhpT2r25zYR70= -github.com/libp2p/go-doh-resolver v0.4.0 h1:gUBa1f1XsPwtpE1du0O+nnZCUqtG7oYi7Bb+0S7FQqw= -github.com/libp2p/go-doh-resolver v0.4.0/go.mod h1:v1/jwsFusgsWIGX/c6vCRrnJ60x7bhTiq/fs2qt0cAg= -github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= -github.com/libp2p/go-eventbus v0.2.1/go.mod h1:jc2S4SoEVPP48H9Wpzm5aiGwUCBMfGhVhhBjyhhCJs8= -github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= -github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= -github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= -github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.1.0/go.mod h1:6D/2OBauqLUoqcADOJpn9WbKqvaM07tDw68qHM0BxUM= -github.com/libp2p/go-libp2p v0.1.1/go.mod h1:I00BRo1UuUSdpuc8Q2mN7yDF/oTUTRAX6JWpTiK9Rp8= -github.com/libp2p/go-libp2p v0.6.1/go.mod h1:CTFnWXogryAHjXAKEbOf1OWY+VeAP3lDMZkfEI5sT54= -github.com/libp2p/go-libp2p v0.7.0/go.mod h1:hZJf8txWeCduQRDC/WSqBGMxaTHCOYHt2xSU1ivxn0k= -github.com/libp2p/go-libp2p v0.7.4/go.mod h1:oXsBlTLF1q7pxr+9w6lqzS1ILpyHsaBPniVO7zIHGMw= -github.com/libp2p/go-libp2p v0.8.1/go.mod h1:QRNH9pwdbEBpx5DTJYg+qxcVaDMAz3Ee/qDKwXujH5o= -github.com/libp2p/go-libp2p v0.14.3/go.mod h1:d12V4PdKbpL0T1/gsUNN8DfgMuRPDX8bS2QxCZlwRH0= -github.com/libp2p/go-libp2p v0.27.8 h1:IX5x/4yKwyPQeVS2AXHZ3J4YATM9oHBGH1gBc23jBAI= -github.com/libp2p/go-libp2p v0.27.8/go.mod h1:eCFFtd0s5i/EVKR7+5Ki8bM7qwkNW3TPTTSSW9sz8NE= -github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= -github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= -github.com/libp2p/go-libp2p-autonat v0.1.0/go.mod h1:1tLf2yXxiE/oKGtDwPYWTSYG3PtvYlJmg7NeVtPRqH8= -github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= -github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI= -github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI= -github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A= -github.com/libp2p/go-libp2p-autonat v0.4.2/go.mod h1:YxaJlpr81FhdOv3W3BTconZPfhaYivRdf53g+S2wobk= -github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= -github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= -github.com/libp2p/go-libp2p-blankhost v0.2.0/go.mod h1:eduNKXGTioTuQAUcZ5epXi9vMl+t4d8ugUBRQ4SqaNQ= -github.com/libp2p/go-libp2p-circuit v0.1.0/go.mod h1:Ahq4cY3V9VJcHcn1SBXjr78AbFkZeIRmfunbA7pmFh8= -github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= -github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo= -github.com/libp2p/go-libp2p-circuit v0.4.0/go.mod h1:t/ktoFIUzM6uLQ+o1G6NuBl2ANhBKN9Bc8jRIk31MoA= -github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= -github.com/libp2p/go-libp2p-core v0.0.2/go.mod h1:9dAcntw/n46XycV4RnlBq3BpgrmyUi9LuoTNdPrbUco= -github.com/libp2p/go-libp2p-core v0.0.3/go.mod h1:j+YQMNz9WNSkNezXOsahp9kwZBKBvxLpKD316QWSJXE= -github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= -github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= -github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= -github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= -github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= -github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= -github.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= -github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= -github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM= -github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= -github.com/libp2p/go-libp2p-core v0.5.7/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= -github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= -github.com/libp2p/go-libp2p-core v0.7.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.0/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.1/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.2/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-core v0.8.5/go.mod h1:FfewUH/YpvWbEB+ZY9AQRQ4TAD8sJBt/G1rVvhz5XT8= -github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= -github.com/libp2p/go-libp2p-discovery v0.1.0/go.mod h1:4F/x+aldVHjHDHuX85x1zWoFTGElt8HnoDzwkFZm29g= -github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= -github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw= -github.com/libp2p/go-libp2p-discovery v0.5.0/go.mod h1:+srtPIU9gDaBNu//UHvcdliKBIcr4SfDcm0/PfPJLug= -github.com/libp2p/go-libp2p-kad-dht v0.21.1 h1:xpfp8/t9+X2ip1l8Umap1/UGNnJ3RHJgKGAEsnRAlTo= -github.com/libp2p/go-libp2p-kad-dht v0.21.1/go.mod h1:Oy8wvbdjpB70eS5AaFaI68tOtrdo3KylTvXDjikxqFo= -github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= -github.com/libp2p/go-libp2p-kbucket v0.5.0 h1:g/7tVm8ACHDxH29BGrpsQlnNeu+6OF1A9bno/4/U1oA= -github.com/libp2p/go-libp2p-kbucket v0.5.0/go.mod h1:zGzGCpQd78b5BNTDGHNDLaTt9aDK/A02xeZp9QeFC4U= -github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= -github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= -github.com/libp2p/go-libp2p-mplex v0.2.1/go.mod h1:SC99Rxs8Vuzrf/6WhmH41kNn13TiYdAWNYHrwImKLnE= -github.com/libp2p/go-libp2p-mplex v0.2.2/go.mod h1:74S9eum0tVQdAfFiKxAyKzNdSuLqw5oadDq7+L/FELo= -github.com/libp2p/go-libp2p-mplex v0.2.3/go.mod h1:CK3p2+9qH9x+7ER/gWWDYJ3QW5ZxWDkm+dVvjfuG3ek= -github.com/libp2p/go-libp2p-mplex v0.4.0/go.mod h1:yCyWJE2sc6TBTnFpjvLuEJgTSw/u+MamvzILKdX7asw= -github.com/libp2p/go-libp2p-mplex v0.4.1/go.mod h1:cmy+3GfqfM1PceHTLL7zQzAAYaryDu6iPSC+CIb094g= -github.com/libp2p/go-libp2p-nat v0.0.4/go.mod h1:N9Js/zVtAXqaeT99cXgTV9e75KpnWCvVOiGzlcHmBbY= -github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE= -github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw= -github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU= -github.com/libp2p/go-libp2p-noise v0.2.0/go.mod h1:IEbYhBBzGyvdLBoxxULL/SGbJARhUeqlO8lVSREYu2Q= -github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY= -github.com/libp2p/go-libp2p-peerstore v0.1.0/go.mod h1:2CeHkQsr8svp4fZ+Oi9ykN1HBb6u0MOvdJ7YIsmcwtY= -github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1ciXAXV397W6h1GH/uKI= -github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= -github.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ= -github.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= -github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= -github.com/libp2p/go-libp2p-peerstore v0.2.6/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-peerstore v0.2.7/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= -github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= -github.com/libp2p/go-libp2p-pubsub v0.9.0 h1:mcLb4WzwhUG4OKb0rp1/bYMd/DYhvMyzJheQH3LMd1s= -github.com/libp2p/go-libp2p-pubsub v0.9.0/go.mod h1:OEsj0Cc/BpkqikXRTrVspWU/Hx7bMZwHP+6vNMd+c7I= -github.com/libp2p/go-libp2p-pubsub-router v0.6.0 h1:D30iKdlqDt5ZmLEYhHELCMRj8b4sFAqrUcshIUvVP/s= -github.com/libp2p/go-libp2p-pubsub-router v0.6.0/go.mod h1:FY/q0/RBTKsLA7l4vqC2cbRbOvyDotg8PJQ7j8FDudE= -github.com/libp2p/go-libp2p-quic-transport v0.10.0/go.mod h1:RfJbZ8IqXIhxBRm5hqUEJqjiiY8xmEuq3HUDS993MkA= -github.com/libp2p/go-libp2p-record v0.1.0/go.mod h1:ujNc8iuE5dlKWVy6wuL6dd58t0n7xI4hAIl8pE6wu5Q= -github.com/libp2p/go-libp2p-record v0.2.0 h1:oiNUOCWno2BFuxt3my4i1frNrt7PerzB3queqa1NkQ0= -github.com/libp2p/go-libp2p-record v0.2.0/go.mod h1:I+3zMkvvg5m2OcSdoL0KPljyJyvNDFGKX7QdlpYUcwk= -github.com/libp2p/go-libp2p-routing-helpers v0.6.2 h1:u6SWfX+3LoqqTAFxWVl79RkcIDE3Zsay5d+JohlEBaE= -github.com/libp2p/go-libp2p-routing-helpers v0.6.2/go.mod h1:R289GUxUMzRXIbWGSuUUTPrlVJZ3Y/pPz495+qgXJX8= -github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= -github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= -github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8= -github.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncHG0uVwGrwvjYlNY= -github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= -github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= -github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM= -github.com/libp2p/go-libp2p-swarm v0.2.8/go.mod h1:JQKMGSth4SMqonruY0a8yjlPVIkb0mdNSwckW7OYziM= -github.com/libp2p/go-libp2p-swarm v0.3.0/go.mod h1:hdv95GWCTmzkgeJpP+GK/9D9puJegb7H57B5hWQR5Kk= -github.com/libp2p/go-libp2p-swarm v0.5.0/go.mod h1:sU9i6BoHE0Ve5SKz3y9WfKrh8dUat6JknzUehFx8xW4= -github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= -github.com/libp2p/go-libp2p-testing v0.1.0/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= -github.com/libp2p/go-libp2p-testing v0.1.1/go.mod h1:xaZWMJrPUM5GlDBxCeGUi7kI4eqnjVyavGroI2nxEM0= -github.com/libp2p/go-libp2p-testing v0.1.2-0.20200422005655-8775583591d8/go.mod h1:Qy8sAncLKpwXtS2dSnDOP8ktexIAHKu+J+pnZOFZLTc= -github.com/libp2p/go-libp2p-testing v0.3.0/go.mod h1:efZkql4UZ7OVsEfaxNHZPzIehtsBXMrXnCfJIgDti5g= -github.com/libp2p/go-libp2p-testing v0.4.0/go.mod h1:Q+PFXYoiYFN5CAEG2w3gLPEzotlKsNSbKQ/lImlOWF0= -github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= -github.com/libp2p/go-libp2p-tls v0.1.3/go.mod h1:wZfuewxOndz5RTnCAxFliGjvYSDA40sKitV4c50uI1M= -github.com/libp2p/go-libp2p-transport-upgrader v0.1.1/go.mod h1:IEtA6or8JUbsV07qPW4r01GnTenLW4oi3lOPbUMGJJA= -github.com/libp2p/go-libp2p-transport-upgrader v0.2.0/go.mod h1:mQcrHj4asu6ArfSoMuyojOdjx73Q47cYD7s5+gZOlns= -github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D1dwydnTmKRPXiAR/fyvi1dXuL4o= -github.com/libp2p/go-libp2p-transport-upgrader v0.4.2/go.mod h1:NR8ne1VwfreD5VIWIU62Agt/J18ekORFU/j1i2y8zvk= -github.com/libp2p/go-libp2p-xor v0.1.0 h1:hhQwT4uGrBcuAkUGXADuPltalOdpf9aag9kaYNT2tLA= -github.com/libp2p/go-libp2p-xor v0.1.0/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQJscoIL/u6InY= -github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= -github.com/libp2p/go-libp2p-yamux v0.2.1/go.mod h1:1FBXiHDk1VyRM1C0aez2bCfHQ4vMZKkAQzZbkSQt5fI= -github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw= -github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA= -github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU= -github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4= -github.com/libp2p/go-libp2p-yamux v0.4.0/go.mod h1:+DWDjtFMzoAwYLVkNZftoucn7PelNoy5nm3tZ3/Zw30= -github.com/libp2p/go-libp2p-yamux v0.5.0/go.mod h1:AyR8k5EzyM2QN9Bbdg6X1SkVVuqLwTGf0L4DFq9g6po= -github.com/libp2p/go-libp2p-yamux v0.5.4/go.mod h1:tfrXbyaTqqSU654GTvK3ocnSZL3BuHoeTSqhcel1wsE= -github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= -github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= -github.com/libp2p/go-maddr-filter v0.1.0/go.mod h1:VzZhTXkMucEGGEOSKddrwGiOv0tUhgnKqNEmIAz/bPU= -github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= -github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= -github.com/libp2p/go-mplex v0.1.1/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= -github.com/libp2p/go-mplex v0.1.2/go.mod h1:Xgz2RDCi3co0LeZfgjm4OgUF15+sVR8SRcu3SFXI1lk= -github.com/libp2p/go-mplex v0.2.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= -github.com/libp2p/go-mplex v0.3.0/go.mod h1:0Oy/A9PQlwBytDRp4wSkFnzHYDKcpLot35JQ6msjvYQ= -github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY= -github.com/libp2p/go-mplex v0.7.0/go.mod h1:rW8ThnRcYWft/Jb2jeORBmPd6xuG3dGxWN/W168L9EU= -github.com/libp2p/go-msgio v0.0.2/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.3/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= -github.com/libp2p/go-msgio v0.0.6/go.mod h1:4ecVB6d9f4BDSL5fqvPiC4A3KivjWn+Venn/1ALLMWA= -github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= -github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= -github.com/libp2p/go-nat v0.0.3/go.mod h1:88nUEt0k0JD45Bk93NIwDqjlhiOwOoV36GchpcVc1yI= -github.com/libp2p/go-nat v0.0.4/go.mod h1:Nmw50VAvKuk38jUBcmNh6p9lUJLoODbJRvYAa/+KSDo= -github.com/libp2p/go-nat v0.0.5/go.mod h1:B7NxsVNPZmRLvMOwiEO1scOSyjA56zxYAGv1yQgRkEU= -github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= -github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= -github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= -github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A= -github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ= -github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= -github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= -github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= -github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA= -github.com/libp2p/go-reuseport v0.0.2/go.mod h1:SPD+5RwGC7rcnzngoYC86GjPzjSywuQyMVAheVBD9nQ= -github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= -github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= -github.com/libp2p/go-reuseport-transport v0.0.2/go.mod h1:YkbSDrvjUVDL6b8XqriyA20obEtsW9BLkuOUyQAOCbs= -github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM= -github.com/libp2p/go-reuseport-transport v0.0.4/go.mod h1:trPa7r/7TJK/d+0hdBLOCGvpQQVOU74OXbNCIMkufGw= -github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.1/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= -github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= -github.com/libp2p/go-stream-muxer-multistream v0.3.0/go.mod h1:yDh8abSIzmZtqtOt64gFJUXEryejzNb0lisTt+fAMJA= -github.com/libp2p/go-tcp-transport v0.1.0/go.mod h1:oJ8I5VXryj493DEJ7OsBieu8fcg2nHGctwtInJVpipc= -github.com/libp2p/go-tcp-transport v0.1.1/go.mod h1:3HzGvLbx6etZjnFlERyakbaYPdfjg2pWP97dFZworkY= -github.com/libp2p/go-tcp-transport v0.2.0/go.mod h1:vX2U0CnWimU4h0SGSEsg++AzvBcroCGYw28kh94oLe0= -github.com/libp2p/go-tcp-transport v0.2.3/go.mod h1:9dvr03yqrPyYGIEN6Dy5UvdJZjyPFvl1S/igQ5QD1SU= -github.com/libp2p/go-testutil v0.1.0/go.mod h1:81b2n5HypcVyrCg/MJx4Wgfp/VHojytjVe/gLzZ2Ehc= -github.com/libp2p/go-ws-transport v0.1.0/go.mod h1:rjw1MG1LU9YDC6gzmwObkPd/Sqwhw7yT74kj3raBFuo= -github.com/libp2p/go-ws-transport v0.2.0/go.mod h1:9BHJz/4Q5A9ludYWKoGCFC5gUElzlHoKzu0yY9p/klM= -github.com/libp2p/go-ws-transport v0.3.0/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1fiZuEaBYusP5zsk= -github.com/libp2p/go-ws-transport v0.4.0/go.mod h1:EcIEKqf/7GDjth6ksuS/6p7R49V4CBY6/E7R/iyhYUA= -github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.2.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux v1.4.0/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux v1.4.1/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= -github.com/libp2p/go-yamux/v2 v2.2.0/go.mod h1:3So6P6TV6r75R9jiBpiIKgU/66lOarCZjqROGxzPpPQ= -github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ= -github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= -github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q= -github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= -github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= -github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lucas-clemente/quic-go v0.19.3/go.mod h1:ADXpNbTQjq1hIzCpB+y/k5iz4n4z4IwqoLb94Kh5Hu8= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easygo v0.0.0-20190618140210-3c14a0dc985f h1:4+gHs0jJFJ06bfN8PshnM6cHcxGjRUVRLo5jndDiKRQ= github.com/mailru/easygo v0.0.0-20190618140210-3c14a0dc985f/go.mod h1:tHCZHV8b2A90ObojrEAzY0Lb03gxUxjDHr5IJyAh4ew= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs= -github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I= -github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= -github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mediocregopher/radix/v3 v3.4.2/go.mod h1:8FL3F6UQRXHXIBSPUs5h0RybMF8i4n7wVopoX3x7Bv8= -github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= -github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= -github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= -github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= -github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= -github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= -github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= -github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= -github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= -github.com/minio/sha256-simd v0.0.0-20190131020904-2d45a736cd16/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= -github.com/minio/sha256-simd v0.0.0-20190328051042-05b4dd3047e5/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= -github.com/minio/sha256-simd v0.1.0/go.mod h1:2FMWW+8GMoPweT6+pI63m9YE3Lmw4J71hV56Chs1E/U= -github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= -github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -1263,229 +466,68 @@ github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iP github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= -github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= -github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= -github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= -github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= -github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= -github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= -github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4= -github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= -github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= -github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= -github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= -github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= -github.com/multiformats/go-multiaddr v0.3.1/go.mod h1:uPbspcUPd5AfaP6ql3ujFY+QWzmBD8uLLL4bXW0XfGc= -github.com/multiformats/go-multiaddr v0.3.3/go.mod h1:lCKNGP1EQ1eZ35Za2wlqnabm9xQkib3fyB+nZXHLag0= -github.com/multiformats/go-multiaddr v0.12.1 h1:vm+BA/WZA8QZDp1pF1FWhi5CT3g1tbi5GJmqpb6wnlk= -github.com/multiformats/go-multiaddr v0.12.1/go.mod h1:7mPkiBMmLeFipt+nNSq9pHZUeJSt8lHBgH6yhj0YQzE= -github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= -github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= -github.com/multiformats/go-multiaddr-dns v0.2.0/go.mod h1:TJ5pr5bBO7Y1B18djPuRsVkduhQH2YqYSbxWJzYGdK0= -github.com/multiformats/go-multiaddr-dns v0.3.0/go.mod h1:mNzQ4eTGDg0ll1N9jKPOUogZPoJ30W8a7zk66FQPpdQ= -github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= -github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= -github.com/multiformats/go-multiaddr-fmt v0.0.1/go.mod h1:aBYjqL4T/7j4Qx+R73XSv/8JsgnRFlf0w2KGLCmXl3Q= -github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= -github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= -github.com/multiformats/go-multiaddr-net v0.0.1/go.mod h1:nw6HSxNmCIQH27XPGBuX+d1tnvM7ihcFwHMSstNAVUU= -github.com/multiformats/go-multiaddr-net v0.1.0/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= -github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= -github.com/multiformats/go-multiaddr-net v0.1.2/go.mod h1:QsWt3XK/3hwvNxZJp92iMQKME1qHfpYmyIjFVsSOY6Y= -github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multiaddr-net v0.2.0/go.mod h1:gGdH3UXny6U3cKKYCvpXI5rnK7YaOIEOPVDI9tsJbEA= -github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= -github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= -github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= -github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk= -github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ= -github.com/multiformats/go-multicodec v0.8.1 h1:ycepHwavHafh3grIbR1jIXnKCsFm0fqsfEOsJ8NtKE8= -github.com/multiformats/go-multicodec v0.8.1/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= -github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= -github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= -github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= -github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= -github.com/multiformats/go-multihash v0.0.15/go.mod h1:D6aZrWNLFTV/ynMpKsNtB40mJzmCl4jb1alC0OvHiHg= -github.com/multiformats/go-multihash v0.1.0/go.mod h1:RJlXsxt6vHGaia+S8We0ErjhojtKzPP2AH4+kYM7k84= -github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= -github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= -github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= -github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= -github.com/multiformats/go-multistream v0.2.1/go.mod h1:5GZPQZbkWOLOn3J2y4Y99vVW7vOfsAflxARk3x14o6k= -github.com/multiformats/go-multistream v0.2.2/go.mod h1:UIcnm7Zuo8HKG+HkWgfQsGL+/MIEhyTqbODbIUwSXKs= -github.com/multiformats/go-multistream v0.4.1 h1:rFy0Iiyn3YT0asivDUIR05leAdwZq3de4741sbiSdfo= -github.com/multiformats/go-multistream v0.4.1/go.mod h1:Mz5eykRVAjJWckE2U78c6xqdtyNUEhKSM0Lwar2p77Q= -github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= -github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= -github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= -github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= -github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= -github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= -github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= -github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= -github.com/openzipkin/zipkin-go v0.4.1 h1:kNd/ST2yLLWhaWrkgchya40TJabe8Hioj9udfPcEO5A= -github.com/openzipkin/zipkin-go v0.4.1/go.mod h1:qY0VqDSN1pOBN94dBc6w2GJlWLiovAyg7Qt6/I9HecM= -github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= -github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI= github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= -github.com/petar/GoLLRB v0.0.0-20210522233825-ae3b015fd3e9 h1:1/WtZae0yGtPq+TI6+Tv1WTxkukpXeMlviSxvL7SRgk= -github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polydawn/refmt v0.0.0-20190221155625-df39d6c2d992/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190408063855-01bf1e26dd14/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20190807091052-3d65705ee9f1/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e/go.mod h1:uIp+gprXxxrWSjjklXD+mN4wed/tMfjMMmN/9+JsA9o= -github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= -github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.11.0 h1:5EAgkfkMl659uZPbe9AS2N68a7Cc1TJbPEuGzFuRbyk= github.com/prometheus/procfs v0.11.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= -github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= -github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= -github.com/quic-go/qtls-go1-19 v0.3.3 h1:wznEHvJwd+2X3PqftRha0SUKmGsnb6dfArMhy9PeJVE= -github.com/quic-go/qtls-go1-19 v0.3.3/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= -github.com/quic-go/qtls-go1-20 v0.2.3 h1:m575dovXn1y2ATOb1XrRFcrv0F+EQmlowTkoraNkDPI= -github.com/quic-go/qtls-go1-20 v0.2.3/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= -github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= -github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= -github.com/quic-go/webtransport-go v0.5.2 h1:GA6Bl6oZY+g/flt00Pnu0XtivSD8vukOu3lYhJjnGEk= -github.com/quic-go/webtransport-go v0.5.2/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= github.com/r3labs/diff/v3 v3.0.1 h1:CBKqf3XmNRHXKmdU7mZP1w7TV0pDyVCis1AUHtA4Xtg= github.com/r3labs/diff/v3 v3.0.1/go.mod h1:f1S9bourRbiM66NskseyUdo0fTmEE0qKrikYJX63dgo= -github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= -github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rhnvrm/simples3 v0.6.1 h1:H0DJwybR6ryQE+Odi9eqkHuzjYAeJgtGcGtuBwOhsH8= github.com/rhnvrm/simples3 v0.6.1/go.mod h1:Y+3vYm2V7Y4VijFoJHHTrja6OgPrJ2cBti8dPGkC3sA= -github.com/rivo/tview v0.0.0-20230814110005-ccc2c8119703 h1:ZyM/+FYnpbZsFWuCohniM56kRoHRB4r5EuIzXEYkpxo= -github.com/rivo/tview v0.0.0-20230814110005-ccc2c8119703/go.mod h1:nVwGv4MP47T0jvlk7KuTTjjuSmrGO4JF0iaiNt4bufE= +github.com/rivo/tview v0.0.0-20240307173318-e804876934a1 h1:bWLHTRekAy497pE7+nXSuzXwwFHI0XauRzz6roUvY+s= +github.com/rivo/tview v0.0.0-20240307173318-e804876934a1/go.mod h1:02iFIz7K/A9jGCvrizLPvoqr4cEIx7q54RH5Qudkrss= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -1494,91 +536,33 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/rs/cors v1.10.0 h1:62NOS1h+r8p1mW6FM0FSB0exioXLhd/sh15KpjWBZ+8= github.com/rs/cors v1.10.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/samber/lo v1.36.0 h1:4LaOxH1mHnbDGhTVE0i1z8v/lWaQW8AIfOD3HU4mSaw= -github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= -github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= -github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= -github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/goconvey v0.0.0-20190222223459-a17d461953aa/go.mod h1:2RVY1rIf+2J2o/IM9+vPq9RzmHDSseB7FoXiSNIUsoU= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= -github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= -github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= -github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= -github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= -github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= -github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= -github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/streamingfast/eth-go v0.0.0-20240129180546-72c8e66e24b3 h1:iC/2SGGULSX/rkwJgWUdYhF+3WZtcByG1UDhvBzfK7Y= github.com/streamingfast/eth-go v0.0.0-20240129180546-72c8e66e24b3/go.mod h1:UEm8dqibr3c3A1iIA3CHpkhN7j3X78prN7/55sXf3A0= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091 h1:RN5mrigyirb8anBEtdjtHFIufXdacyTi6i4KBfeNXeo= github.com/streamingfast/logging v0.0.0-20230608130331-f22c91403091/go.mod h1:VlduQ80JcGJSargkRU4Sg9Xo63wZD/l8A5NC/Uo1/uU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -1593,35 +577,26 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= -github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= -github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo= +github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= @@ -1630,46 +605,15 @@ github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBn github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= -github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE= -github.com/warpfork/go-testmark v0.3.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= -github.com/warpfork/go-testmark v0.9.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= -github.com/warpfork/go-testmark v0.10.0 h1:E86YlUMYfwIacEsQGlnTvjk1IgYkyTGjPhF0RnwTCmw= -github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= -github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= -github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/wealdtech/go-merkletree v1.0.0 h1:DsF1xMzj5rK3pSQM6mPv8jlyJyHXhFxpnA2bwEjMMBY= github.com/wealdtech/go-merkletree v1.0.0/go.mod h1:cdil512d/8ZC7Kx3bfrDvGMQXB25NTKbsm0rFrmDax4= -github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc h1:BCPnHtcboadS0DvysUuJXZ4lWVv5Bh5i7+tbIyi+ck4= -github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc/go.mod h1:r45hJU7yEoA81k6MWNhpMj/kms0n14dkzkxYHoB96UM= -github.com/whyrusleeping/cbor v0.0.0-20171005072247-63513f603b11 h1:5HZfQkwe0mIfyDmc1Em5GqlNRzcdtlv4HTNmdpt7XH0= -github.com/whyrusleeping/cbor-gen v0.0.0-20200123233031-1cdf64d27158/go.mod h1:Xj/M2wWU+QdTdRbu/L/1dIZY8/Wb2K9pAhtroQuxJJI= -github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa h1:EyA027ZAkuaCLoxVX4r1TZMPy1d31fM6hbfQ4OU4I5o= -github.com/whyrusleeping/cbor-gen v0.0.0-20230126041949-52956bd4c9aa/go.mod h1:fgkXqYy7bV2cFeIEOkVTZS/WjXARfBqSH6Q2qHL33hQ= -github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f h1:jQa4QT2UP9WYv2nzyawpKMOCl+Z/jW7djv2/J50lj9E= -github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f/go.mod h1:p9UJB6dDgdPgMJZs7UjUOdulKyRr9fqkS+6JKAInPy8= -github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 h1:EKhdznlJHPMoKr0XTrX+IlJs1LH3lyx2nfr1dOlZ79k= -github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= -github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= -github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= -github.com/whyrusleeping/go-notifier v0.0.0-20170827234753-097c5d47330f/go.mod h1:cZNvX9cFybI01GriPRMXDtczuvUhgbcYr9iCGaNlRv8= -github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= -github.com/whyrusleeping/mdns v0.0.0-20180901202407-ef14215e6b30/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= -github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= -github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1:E9S12nwJwEOXe2d6gT6qxdvqMnNq+VnSsKPgm2ZZNds= -github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= -github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= @@ -1677,332 +621,121 @@ github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmv github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg= github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM= github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw= -github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= +github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= -go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= -go.opentelemetry.io/otel/exporters/jaeger v1.15.1 h1:x3SLvwli0OyAJapNcOIzf1xXBRBA+HD3elrMQmFfmXo= -go.opentelemetry.io/otel/exporters/jaeger v1.15.1/go.mod h1:0Ck9b5oLL/bFZvfAEEqtrb1U0jZXjm5fWXMCOCG3vvM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.7.0/go.mod h1:M1hVZHNxcbkAlcvrOMlpQ4YOO3Awf+4N2dxkZL3xm04= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.1 h1:XYDQtNzdb2T4uM1pku2m76eSMDJgqhJ+6KzkqgQBALc= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.15.1/go.mod h1:uOTV75+LOzV+ODmL8ahRLWkFA3eQcSC2aAsbxIu4duk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.7.0/go.mod h1:ceUgdyfNv4h4gLxHR0WNfDiiVmZFodZhZSbOLhpxqXE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.1 h1:tyoeaUh8REKay72DVYsSEBYV18+fGONe+YYPaOxgLoE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.15.1/go.mod h1:HUSnrjQQ19KX9ECjpQxufsF+3ioD3zISPMlauTPZu2g= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.15.1 h1:pIfoG5IAZFzp9EUlJzdSkpUwpaUAAnD+Ru1nBLTACIQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.15.1/go.mod h1:poNKBqF5+nR/6ke2oGTDjHfksrsHDOHXAl2g4+9ONsY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0 h1:pLP0MH4MAqeTEV0g/4flxw9O8Is48uAIauAnjznbW50= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.7.0/go.mod h1:aFXT9Ng2seM9eizF+LfKiyPBGy8xIZKwhusC1gIu3hA= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.15.1 h1:2PunuO5SbkN5MhCbuHCd3tC6qrcaj+uDAkX/qBU5BAs= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.15.1/go.mod h1:q8+Tha+5LThjeSU8BW93uUC5w5/+DnYHMKBMpRCsui0= -go.opentelemetry.io/otel/exporters/zipkin v1.15.1 h1:B6s/o48bx00ayJu7F+jIMJfhPTyxW+S8vthjTZMNBj0= -go.opentelemetry.io/otel/exporters/zipkin v1.15.1/go.mod h1:EjjV7/YfYXG+khxCOfG6PPeRGoOmtcSusyW66qPqpRQ= -go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= -go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ= -go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k= -go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU= -go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= -go.opentelemetry.io/otel/sdk v1.16.0/go.mod h1:tMsIuKXuuIWPBAOrH+eHtvhTL+SntFtXF9QD68aP6p4= -go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU= -go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= -go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10= -go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.16.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= -go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= -go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8= -go.uber.org/dig v1.16.1/go.mod h1:557JTAUZT5bUK0SvCwikmLPPtdQhfvLYtO5tJgQSbnk= -go.uber.org/fx v1.19.2 h1:SyFgYQFr1Wl0AYstE8vyYIzP4bFz2URrScjwC4cwUvY= -go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= -go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= -go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= -go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= -go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= -go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -go4.org v0.0.0-20200411211856-f5505b9728dd h1:BNJlw5kRTzdmyfh5U8F93HA2OwkP7ZGwA51eJ/0wKOU= -go4.org v0.0.0-20200411211856-f5505b9728dd/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= -golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180406214816-61147c48b25b/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190302025703-b6889370fb10/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210317225723-c4fcb01b228e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2012,26 +745,25 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -2042,186 +774,49 @@ golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= -google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= -google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a h1:myvhA4is3vrit1a6NZCWBIwN0kNEnX21DJOJX/NvIfI= -google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.28.1/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -2230,15 +825,11 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20160105164936-4f90aeace3a2/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2247,35 +838,22 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU= gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= -gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= @@ -2285,28 +863,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= -lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= -nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -pgregory.net/rapid v0.4.7 h1:MTNRktPuv5FNqOO151TM9mDTa+XHcX6ypYeISDVD14g= -pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/nitro-testnode b/nitro-testnode index 3922df9caf..c334820b2d 160000 --- a/nitro-testnode +++ b/nitro-testnode @@ -1 +1 @@ -Subproject commit 3922df9caf7a65dd4168b8158c1244c5fe88780e +Subproject commit c334820b2dba6dfa4078f81ed242afbbccc19c91 diff --git a/precompiles/ArbDebug.go b/precompiles/ArbDebug.go index ef059db3ff..bf85d5e18f 100644 --- a/precompiles/ArbDebug.go +++ b/precompiles/ArbDebug.go @@ -48,7 +48,7 @@ func (con ArbDebug) EventsView(c ctx, evm mech) error { } func (con ArbDebug) CustomRevert(c ctx, number uint64) error { - return con.CustomError(number, "This spider family wards off bugs: /\\oo/\\ //\\(oo)/\\ /\\oo/\\", true) + return con.CustomError(number, "This spider family wards off bugs: /\\oo/\\ //\\(oo)//\\ /\\oo/\\", true) } // Caller becomes a chain owner @@ -56,6 +56,11 @@ func (con ArbDebug) BecomeChainOwner(c ctx, evm mech) error { return c.State.ChainOwners().Add(c.caller) } +// Halts the chain by panicking in the STF +func (con ArbDebug) Panic(c ctx, evm mech) error { + panic("called ArbDebug's debug-only Panic method") +} + func (con ArbDebug) LegacyError(c ctx) error { return errors.New("example legacy error") } diff --git a/precompiles/ArbGasInfo.go b/precompiles/ArbGasInfo.go index cda5350a4a..25801109c7 100644 --- a/precompiles/ArbGasInfo.go +++ b/precompiles/ArbGasInfo.go @@ -187,7 +187,7 @@ func (con ArbGasInfo) GetGasBacklog(c ctx, evm mech) (uint64, error) { return c.State.L2PricingState().GasBacklog() } -// GetPricingInertia gets the L2 basefee in response to backlogged gas +// GetPricingInertia gets how slowly ArbOS updates the L2 basefee in response to backlogged gas func (con ArbGasInfo) GetPricingInertia(c ctx, evm mech) (uint64, error) { return c.State.L2PricingState().PricingInertia() } @@ -197,25 +197,13 @@ func (con ArbGasInfo) GetGasBacklogTolerance(c ctx, evm mech) (uint64, error) { return c.State.L2PricingState().BacklogTolerance() } +// GetL1PricingSurplus gets the surplus of funds for L1 batch posting payments (may be negative) func (con ArbGasInfo) GetL1PricingSurplus(c ctx, evm mech) (*big.Int, error) { if c.State.ArbOSVersion() < 10 { return con._preversion10_GetL1PricingSurplus(c, evm) } ps := c.State.L1PricingState() - fundsDueForRefunds, err := ps.BatchPosterTable().TotalFundsDue() - if err != nil { - return nil, err - } - fundsDueForRewards, err := ps.FundsDueForRewards() - if err != nil { - return nil, err - } - haveFunds, err := ps.L1FeesAvailable() - if err != nil { - return nil, err - } - needFunds := arbmath.BigAdd(fundsDueForRefunds, fundsDueForRewards) - return arbmath.BigSub(haveFunds, needFunds), nil + return ps.GetL1PricingSurplus() } func (con ArbGasInfo) _preversion10_GetL1PricingSurplus(c ctx, evm mech) (*big.Int, error) { @@ -230,37 +218,45 @@ func (con ArbGasInfo) _preversion10_GetL1PricingSurplus(c ctx, evm mech) (*big.I } haveFunds := evm.StateDB.GetBalance(l1pricing.L1PricerFundsPoolAddress) needFunds := arbmath.BigAdd(fundsDueForRefunds, fundsDueForRewards) - return arbmath.BigSub(haveFunds, needFunds), nil + return arbmath.BigSub(haveFunds.ToBig(), needFunds), nil } +// GetPerBatchGasCharge gets the base charge (in L1 gas) attributed to each data batch in the calldata pricer func (con ArbGasInfo) GetPerBatchGasCharge(c ctx, evm mech) (int64, error) { return c.State.L1PricingState().PerBatchGasCost() } +// GetAmortizedCostCapBips gets the cost amortization cap in basis points func (con ArbGasInfo) GetAmortizedCostCapBips(c ctx, evm mech) (uint64, error) { return c.State.L1PricingState().AmortizedCostCapBips() } +// GetL1FeesAvailable gets the available funds from L1 fees func (con ArbGasInfo) GetL1FeesAvailable(c ctx, evm mech) (huge, error) { return c.State.L1PricingState().L1FeesAvailable() } +// GetL1PricingEquilibrationUnits gets the equilibration units parameter for L1 price adjustment algorithm func (con ArbGasInfo) GetL1PricingEquilibrationUnits(c ctx, evm mech) (*big.Int, error) { return c.State.L1PricingState().EquilibrationUnits() } +// GetLastL1PricingUpdateTime gets the last time the L1 calldata pricer was updated func (con ArbGasInfo) GetLastL1PricingUpdateTime(c ctx, evm mech) (uint64, error) { return c.State.L1PricingState().LastUpdateTime() } +// GetL1PricingFundsDueForRewards gets the amount of L1 calldata payments due for rewards (per the L1 reward rate) func (con ArbGasInfo) GetL1PricingFundsDueForRewards(c ctx, evm mech) (*big.Int, error) { return c.State.L1PricingState().FundsDueForRewards() } +// GetL1PricingUnitsSinceUpdate gets the amount of L1 calldata posted since the last update func (con ArbGasInfo) GetL1PricingUnitsSinceUpdate(c ctx, evm mech) (uint64, error) { return c.State.L1PricingState().UnitsSinceUpdate() } +// GetLastL1PricingSurplus gets the L1 pricing surplus as of the last update (may be negative) func (con ArbGasInfo) GetLastL1PricingSurplus(c ctx, evm mech) (*big.Int, error) { return c.State.L1PricingState().LastSurplus() } diff --git a/precompiles/ArbInfo.go b/precompiles/ArbInfo.go index a260f7e7a4..9f8cf34532 100644 --- a/precompiles/ArbInfo.go +++ b/precompiles/ArbInfo.go @@ -18,7 +18,7 @@ func (con ArbInfo) GetBalance(c ctx, evm mech, account addr) (huge, error) { if err := c.Burn(params.BalanceGasEIP1884); err != nil { return nil, err } - return evm.StateDB.GetBalance(account), nil + return evm.StateDB.GetBalance(account).ToBig(), nil } // GetCode retrieves a contract's deployed code diff --git a/precompiles/ArbOwner.go b/precompiles/ArbOwner.go index 166768940b..066fc0a4c4 100644 --- a/precompiles/ArbOwner.go +++ b/precompiles/ArbOwner.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package precompiles @@ -11,6 +11,9 @@ import ( "math/big" "github.com/offchainlabs/nitro/arbos/l1pricing" + "github.com/offchainlabs/nitro/arbos/programs" + "github.com/offchainlabs/nitro/util/arbmath" + am "github.com/offchainlabs/nitro/util/arbmath" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/params" @@ -153,7 +156,7 @@ func (con ArbOwner) ReleaseL1PricerSurplusFunds(c ctx, evm mech, maxWeiToRelease if err != nil { return nil, err } - weiToTransfer := new(big.Int).Sub(balance, recognized) + weiToTransfer := new(big.Int).Sub(balance.ToBig(), recognized) if weiToTransfer.Sign() < 0 { return common.Big0, nil } @@ -166,6 +169,129 @@ func (con ArbOwner) ReleaseL1PricerSurplusFunds(c ctx, evm mech, maxWeiToRelease return weiToTransfer, nil } +// Sets the amount of ink 1 gas buys +func (con ArbOwner) SetInkPrice(c ctx, evm mech, inkPrice uint32) error { + params, err := c.State.Programs().Params() + if err != nil { + return err + } + ink, err := arbmath.IntToUint24(inkPrice) + if err != nil || ink == 0 { + return errors.New("ink price must be a positive uint24") + } + params.InkPrice = ink + return params.Save() +} + +// Sets the maximum depth (in wasm words) a wasm stack may grow +func (con ArbOwner) SetWasmMaxStackDepth(c ctx, evm mech, depth uint32) error { + params, err := c.State.Programs().Params() + if err != nil { + return err + } + params.MaxStackDepth = depth + return params.Save() +} + +// Gets the number of free wasm pages a tx gets +func (con ArbOwner) SetWasmFreePages(c ctx, evm mech, pages uint16) error { + params, err := c.State.Programs().Params() + if err != nil { + return err + } + params.FreePages = pages + return params.Save() +} + +// Sets the base cost of each additional wasm page +func (con ArbOwner) SetWasmPageGas(c ctx, evm mech, gas uint16) error { + params, err := c.State.Programs().Params() + if err != nil { + return err + } + params.PageGas = gas + return params.Save() +} + +// Sets the initial number of pages a wasm may allocate +func (con ArbOwner) SetWasmPageLimit(c ctx, evm mech, limit uint16) error { + params, err := c.State.Programs().Params() + if err != nil { + return err + } + params.PageLimit = limit + return params.Save() +} + +// Sets the minimum costs to invoke a program +func (con ArbOwner) SetWasmMinInitGas(c ctx, _ mech, gas, cached uint64) error { + params, err := c.State.Programs().Params() + if err != nil { + return err + } + params.MinInitGas = am.SaturatingUUCast[uint8](am.DivCeil(gas, programs.MinInitGasUnits)) + params.MinCachedInitGas = am.SaturatingUUCast[uint8](am.DivCeil(cached, programs.MinCachedGasUnits)) + return params.Save() +} + +// Sets the linear adjustment made to program init costs +func (con ArbOwner) SetWasmInitCostScalar(c ctx, _ mech, percent uint64) error { + params, err := c.State.Programs().Params() + if err != nil { + return err + } + params.InitCostScalar = am.SaturatingUUCast[uint8](am.DivCeil(percent, programs.CostScalarPercent)) + return params.Save() +} + +// Sets the number of days after which programs deactivate +func (con ArbOwner) SetWasmExpiryDays(c ctx, _ mech, days uint16) error { + params, err := c.State.Programs().Params() + if err != nil { + return err + } + params.ExpiryDays = days + return params.Save() +} + +// Sets the age a program must be to perform a keepalive +func (con ArbOwner) SetWasmKeepaliveDays(c ctx, _ mech, days uint16) error { + params, err := c.State.Programs().Params() + if err != nil { + return err + } + params.KeepaliveDays = days + return params.Save() +} + +// Sets the number of extra programs ArbOS caches during a given block +func (con ArbOwner) SetWasmBlockCacheSize(c ctx, _ mech, count uint16) error { + params, err := c.State.Programs().Params() + if err != nil { + return err + } + params.BlockCacheSize = count + return params.Save() +} + +// Adds account as a wasm cache manager +func (con ArbOwner) AddWasmCacheManager(c ctx, _ mech, manager addr) error { + return c.State.Programs().CacheManagers().Add(manager) +} + +// Removes account from the list of wasm cache managers +func (con ArbOwner) RemoveWasmCacheManager(c ctx, _ mech, manager addr) error { + managers := c.State.Programs().CacheManagers() + isMember, err := managers.IsMember(manager) + if err != nil { + return err + } + if !isMember { + return errors.New("tried to remove non-manager") + } + return managers.Remove(manager, c.State.ArbOSVersion()) +} + func (con ArbOwner) SetChainConfig(c ctx, evm mech, serializedChainConfig []byte) error { if c == nil { return errors.New("nil context") diff --git a/precompiles/ArbOwner_test.go b/precompiles/ArbOwner_test.go index 77ef922ef8..4212bd1f2b 100644 --- a/precompiles/ArbOwner_test.go +++ b/precompiles/ArbOwner_test.go @@ -15,6 +15,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" "github.com/offchainlabs/nitro/arbos/arbosState" "github.com/offchainlabs/nitro/arbos/burn" @@ -114,7 +115,7 @@ func TestArbOwner(t *testing.T) { Fail(t, avail) } deposited := big.NewInt(1000000) - evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, deposited, state.BalanceChangeUnspecified) + evm.StateDB.AddBalance(l1pricing.L1PricerFundsPoolAddress, uint256.MustFromBig(deposited), state.BalanceChangeUnspecified) avail, err = gasInfo.GetL1FeesAvailable(callCtx, evm) Require(t, err) if avail.Sign() != 0 { diff --git a/precompiles/ArbRetryableTx.go b/precompiles/ArbRetryableTx.go index 3cb7510f0b..d508d75752 100644 --- a/precompiles/ArbRetryableTx.go +++ b/precompiles/ArbRetryableTx.go @@ -127,7 +127,7 @@ func (con ArbRetryableTx) Redeem(c ctx, evm mech, ticketId bytes32) (bytes32, er // Add the gasToDonate back to the gas pool: the retryable attempt will then consume it. // This ensures that the gas pool has enough gas to run the retryable attempt. - return retryTxHash, c.State.L2PricingState().AddToGasPool(arbmath.SaturatingCast(gasToDonate)) + return retryTxHash, c.State.L2PricingState().AddToGasPool(arbmath.SaturatingCast[int64](gasToDonate)) } // GetLifetime gets the default lifetime period a retryable has at creation diff --git a/precompiles/ArbSys.go b/precompiles/ArbSys.go index 0d3df3bbfe..13f56d3b8e 100644 --- a/precompiles/ArbSys.go +++ b/precompiles/ArbSys.go @@ -96,7 +96,7 @@ func (con *ArbSys) MyCallersAddressWithoutAliasing(c ctx, evm mech) (addr, error address := addr{} if evm.Depth() > 1 { - address = c.txProcessor.Callers[evm.Depth()-2] + address = c.txProcessor.Contracts[evm.Depth()-2].Caller() } aliased, err := con.WasMyCallersAddressAliased(c, evm) @@ -209,5 +209,5 @@ func (con ArbSys) WithdrawEth(c ctx, evm mech, value huge, destination addr) (hu func (con ArbSys) isTopLevel(c ctx, evm mech) bool { depth := evm.Depth() - return depth < 2 || evm.Origin == c.txProcessor.Callers[depth-2] + return depth < 2 || evm.Origin == c.txProcessor.Contracts[depth-2].Caller() } diff --git a/precompiles/ArbWasm.go b/precompiles/ArbWasm.go new file mode 100644 index 0000000000..9f42cacb5a --- /dev/null +++ b/precompiles/ArbWasm.go @@ -0,0 +1,224 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package precompiles + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/offchainlabs/nitro/arbos/programs" + "github.com/offchainlabs/nitro/arbos/util" + "github.com/offchainlabs/nitro/util/arbmath" +) + +type ArbWasm struct { + Address addr // 0x71 + + ProgramActivated func(ctx, mech, hash, hash, addr, huge, uint16) error + ProgramActivatedGasCost func(hash, hash, addr, huge, uint16) (uint64, error) + ProgramLifetimeExtended func(ctx, mech, hash, huge) error + ProgramLifetimeExtendedGasCost func(hash, huge) (uint64, error) + + ProgramNotWasmError func() error + ProgramNotActivatedError func() error + ProgramNeedsUpgradeError func(version, stylusVersion uint16) error + ProgramExpiredError func(age uint64) error + ProgramUpToDateError func() error + ProgramKeepaliveTooSoonError func(age uint64) error + ProgramInsufficientValueError func(have, want huge) error +} + +// Compile a wasm program with the latest instrumentation +func (con ArbWasm) ActivateProgram(c ctx, evm mech, value huge, program addr) (uint16, huge, error) { + debug := evm.ChainConfig().DebugMode() + runMode := c.txProcessor.RunMode() + programs := c.State.Programs() + + // charge a fixed cost up front to begin activation + if err := c.Burn(1659168); err != nil { + return 0, nil, err + } + version, codeHash, moduleHash, dataFee, takeAllGas, err := programs.ActivateProgram(evm, program, runMode, debug) + if takeAllGas { + _ = c.BurnOut() + } + if err != nil { + return version, dataFee, err + } + if err := con.payActivationDataFee(c, evm, value, dataFee); err != nil { + return version, dataFee, err + } + return version, dataFee, con.ProgramActivated(c, evm, codeHash, moduleHash, program, dataFee, version) +} + +// Extends a program's expiration date (reverts if too soon) +func (con ArbWasm) CodehashKeepalive(c ctx, evm mech, value huge, codehash bytes32) error { + params, err := c.State.Programs().Params() + if err != nil { + return err + } + dataFee, err := c.State.Programs().ProgramKeepalive(codehash, evm.Context.Time, params) + if err != nil { + return err + } + if err := con.payActivationDataFee(c, evm, value, dataFee); err != nil { + return err + } + return con.ProgramLifetimeExtended(c, evm, codehash, dataFee) +} + +// Pays the data component of activation costs +func (con ArbWasm) payActivationDataFee(c ctx, evm mech, value, dataFee huge) error { + if arbmath.BigLessThan(value, dataFee) { + return con.ProgramInsufficientValueError(value, dataFee) + } + network, err := c.State.NetworkFeeAccount() + if err != nil { + return err + } + scenario := util.TracingDuringEVM + repay := arbmath.BigSub(value, dataFee) + + // transfer the fee to the network account, and the rest back to the user + err = util.TransferBalance(&con.Address, &network, dataFee, evm, scenario, "activate") + if err != nil { + return err + } + return util.TransferBalance(&con.Address, &c.caller, repay, evm, scenario, "reimburse") +} + +// Gets the latest stylus version +func (con ArbWasm) StylusVersion(c ctx, evm mech) (uint16, error) { + params, err := c.State.Programs().Params() + return params.Version, err +} + +// Gets the amount of ink 1 gas buys +func (con ArbWasm) InkPrice(c ctx, _ mech) (uint32, error) { + params, err := c.State.Programs().Params() + return params.InkPrice.ToUint32(), err +} + +// Gets the wasm stack size limit +func (con ArbWasm) MaxStackDepth(c ctx, _ mech) (uint32, error) { + params, err := c.State.Programs().Params() + return params.MaxStackDepth, err +} + +// Gets the number of free wasm pages a tx gets +func (con ArbWasm) FreePages(c ctx, _ mech) (uint16, error) { + params, err := c.State.Programs().Params() + return params.FreePages, err +} + +// Gets the base cost of each additional wasm page +func (con ArbWasm) PageGas(c ctx, _ mech) (uint16, error) { + params, err := c.State.Programs().Params() + return params.PageGas, err +} + +// Gets the ramp that drives exponential memory costs +func (con ArbWasm) PageRamp(c ctx, _ mech) (uint64, error) { + params, err := c.State.Programs().Params() + return params.PageRamp, err +} + +// Gets the maximum initial number of pages a wasm may allocate +func (con ArbWasm) PageLimit(c ctx, _ mech) (uint16, error) { + params, err := c.State.Programs().Params() + return params.PageLimit, err +} + +// Gets the minimum costs to invoke a program +func (con ArbWasm) MinInitGas(c ctx, _ mech) (uint64, uint64, error) { + params, err := c.State.Programs().Params() + init := uint64(params.MinInitGas) * programs.MinInitGasUnits + cached := uint64(params.MinCachedInitGas) * programs.MinCachedGasUnits + return init, cached, err +} + +// Gets the linear adjustment made to program init costs +func (con ArbWasm) InitCostScalar(c ctx, _ mech) (uint64, error) { + params, err := c.State.Programs().Params() + return uint64(params.InitCostScalar) * programs.CostScalarPercent, err +} + +// Gets the number of days after which programs deactivate +func (con ArbWasm) ExpiryDays(c ctx, _ mech) (uint16, error) { + params, err := c.State.Programs().Params() + return params.ExpiryDays, err +} + +// Gets the age a program must be to perform a keepalive +func (con ArbWasm) KeepaliveDays(c ctx, _ mech) (uint16, error) { + params, err := c.State.Programs().Params() + return params.KeepaliveDays, err +} + +// Gets the number of extra programs ArbOS caches during a given block. +func (con ArbWasm) BlockCacheSize(c ctx, _ mech) (uint16, error) { + params, err := c.State.Programs().Params() + return params.BlockCacheSize, err +} + +// Gets the stylus version that program with codehash was most recently compiled with +func (con ArbWasm) CodehashVersion(c ctx, evm mech, codehash bytes32) (uint16, error) { + params, err := c.State.Programs().Params() + if err != nil { + return 0, err + } + return c.State.Programs().CodehashVersion(codehash, evm.Context.Time, params) +} + +// Gets a program's asm size in bytes +func (con ArbWasm) CodehashAsmSize(c ctx, evm mech, codehash bytes32) (uint32, error) { + params, err := c.State.Programs().Params() + if err != nil { + return 0, err + } + return c.State.Programs().ProgramAsmSize(codehash, evm.Context.Time, params) +} + +// Gets the stylus version that program at addr was most recently compiled with +func (con ArbWasm) ProgramVersion(c ctx, evm mech, program addr) (uint16, error) { + codehash, err := c.GetCodeHash(program) + if err != nil { + return 0, err + } + return con.CodehashVersion(c, evm, codehash) +} + +// Gets the cost to invoke the program +func (con ArbWasm) ProgramInitGas(c ctx, evm mech, program addr) (uint64, uint64, error) { + codehash, params, err := con.getCodeHash(c, program) + if err != nil { + return 0, 0, err + } + return c.State.Programs().ProgramInitGas(codehash, evm.Context.Time, params) +} + +// Gets the footprint of program at addr +func (con ArbWasm) ProgramMemoryFootprint(c ctx, evm mech, program addr) (uint16, error) { + codehash, params, err := con.getCodeHash(c, program) + if err != nil { + return 0, err + } + return c.State.Programs().ProgramMemoryFootprint(codehash, evm.Context.Time, params) +} + +// Gets returns the amount of time remaining until the program expires +func (con ArbWasm) ProgramTimeLeft(c ctx, evm mech, program addr) (uint64, error) { + codehash, params, err := con.getCodeHash(c, program) + if err != nil { + return 0, err + } + return c.State.Programs().ProgramTimeLeft(codehash, evm.Context.Time, params) +} + +func (con ArbWasm) getCodeHash(c ctx, program addr) (hash, *programs.StylusParams, error) { + params, err := c.State.Programs().Params() + if err != nil { + return common.Hash{}, params, err + } + codehash, err := c.GetCodeHash(program) + return codehash, params, err +} diff --git a/precompiles/ArbWasmCache.go b/precompiles/ArbWasmCache.go new file mode 100644 index 0000000000..36b4e1ad31 --- /dev/null +++ b/precompiles/ArbWasmCache.go @@ -0,0 +1,68 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package precompiles + +type ArbWasmCache struct { + Address addr // 0x72 + + UpdateProgramCache func(ctx, mech, addr, bytes32, bool) error + UpdateProgramCacheGasCost func(addr, bytes32, bool) (uint64, error) +} + +// See if the user is a cache manager owner. +func (con ArbWasmCache) IsCacheManager(c ctx, _ mech, addr addr) (bool, error) { + return c.State.Programs().CacheManagers().IsMember(addr) +} + +// Retrieve all authorized address managers. +func (con ArbWasmCache) AllCacheManagers(c ctx, _ mech) ([]addr, error) { + return c.State.Programs().CacheManagers().AllMembers(65536) +} + +// Caches all programs with the given codehash. Caller must be a cache manager or chain owner. +func (con ArbWasmCache) CacheCodehash(c ctx, evm mech, codehash hash) error { + return con.setProgramCached(c, evm, codehash, true) +} + +// Evicts all programs with the given codehash. Caller must be a cache manager or chain owner. +func (con ArbWasmCache) EvictCodehash(c ctx, evm mech, codehash hash) error { + return con.setProgramCached(c, evm, codehash, false) +} + +// Gets whether a program is cached. Note that the program may be expired. +func (con ArbWasmCache) CodehashIsCached(c ctx, evm mech, codehash hash) (bool, error) { + return c.State.Programs().ProgramCached(codehash) +} + +// Caches all programs with the given codehash. +func (con ArbWasmCache) setProgramCached(c ctx, evm mech, codehash hash, cached bool) error { + if !con.hasAccess(c) { + return c.BurnOut() + } + programs := c.State.Programs() + params, err := programs.Params() + if err != nil { + return err + } + debugMode := evm.ChainConfig().DebugMode() + txRunMode := c.txProcessor.RunMode() + emitEvent := func() error { + return con.UpdateProgramCache(c, evm, c.caller, codehash, cached) + } + return programs.SetProgramCached( + emitEvent, evm.StateDB, codehash, cached, evm.Context.Time, params, txRunMode, debugMode, + ) +} + +func (con ArbWasmCache) hasAccess(c ctx) bool { + manager, err := c.State.Programs().CacheManagers().IsMember(c.caller) + if err != nil { + return false + } + if manager { + return true + } + owner, err := c.State.ChainOwners().IsMember(c.caller) + return owner && err == nil +} diff --git a/precompiles/context.go b/precompiles/context.go index 08eb0569f8..670ffa7443 100644 --- a/precompiles/context.go +++ b/precompiles/context.go @@ -37,8 +37,7 @@ type Context struct { func (c *Context) Burn(amount uint64) error { if c.gasLeft < amount { - c.gasLeft = 0 - return vm.ErrOutOfGas + return c.BurnOut() } c.gasLeft -= amount return nil @@ -49,6 +48,15 @@ func (c *Context) Burned() uint64 { return c.gasSupplied - c.gasLeft } +func (c *Context) BurnOut() error { + c.gasLeft = 0 + return vm.ErrOutOfGas +} + +func (c *Context) GasLeft() *uint64 { + return &c.gasLeft +} + func (c *Context) Restrict(err error) { log.Crit("A metered burner was used for access-controlled work", "error", err) } @@ -65,6 +73,10 @@ func (c *Context) TracingInfo() *util.TracingInfo { return c.tracingInfo } +func (c *Context) GetCodeHash(address common.Address) (common.Hash, error) { + return c.State.BackingStorage().GetCodeHash(address) +} + func testContext(caller addr, evm mech) *Context { tracingInfo := util.NewTracingInfo(evm, common.Address{}, types.ArbosAddress, util.TracingDuringEVM) ctx := &Context{ diff --git a/precompiles/precompile.go b/precompiles/precompile.go index 0627ef4c7b..c39f2bcb6d 100644 --- a/precompiles/precompile.go +++ b/precompiles/precompile.go @@ -1,4 +1,4 @@ -// Copyright 2021-2023, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE package precompiles @@ -16,8 +16,9 @@ import ( "github.com/offchainlabs/nitro/arbos" "github.com/offchainlabs/nitro/arbos/arbosState" + "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/arbos/util" - templates "github.com/offchainlabs/nitro/solgen/go/precompilesgen" + pgen "github.com/offchainlabs/nitro/solgen/go/precompilesgen" "github.com/offchainlabs/nitro/util/arbmath" "github.com/ethereum/go-ethereum/accounts/abi" @@ -514,12 +515,6 @@ func MakePrecompile(metadata *bind.MetaData, implementer interface{}) (addr, *Pr } func Precompiles() map[addr]ArbosPrecompile { - - //nolint:gocritic - hex := func(s string) addr { - return common.HexToAddress(s) - } - contracts := make(map[addr]ArbosPrecompile) insert := func(address addr, impl ArbosPrecompile) *Precompile { @@ -527,12 +522,12 @@ func Precompiles() map[addr]ArbosPrecompile { return impl.Precompile() } - insert(MakePrecompile(templates.ArbInfoMetaData, &ArbInfo{Address: hex("65")})) - insert(MakePrecompile(templates.ArbAddressTableMetaData, &ArbAddressTable{Address: hex("66")})) - insert(MakePrecompile(templates.ArbBLSMetaData, &ArbBLS{Address: hex("67")})) - insert(MakePrecompile(templates.ArbFunctionTableMetaData, &ArbFunctionTable{Address: hex("68")})) - insert(MakePrecompile(templates.ArbosTestMetaData, &ArbosTest{Address: hex("69")})) - ArbGasInfo := insert(MakePrecompile(templates.ArbGasInfoMetaData, &ArbGasInfo{Address: hex("6c")})) + insert(MakePrecompile(pgen.ArbInfoMetaData, &ArbInfo{Address: types.ArbInfoAddress})) + insert(MakePrecompile(pgen.ArbAddressTableMetaData, &ArbAddressTable{Address: types.ArbAddressTableAddress})) + insert(MakePrecompile(pgen.ArbBLSMetaData, &ArbBLS{Address: types.ArbBLSAddress})) + insert(MakePrecompile(pgen.ArbFunctionTableMetaData, &ArbFunctionTable{Address: types.ArbFunctionTableAddress})) + insert(MakePrecompile(pgen.ArbosTestMetaData, &ArbosTest{Address: types.ArbosTestAddress})) + ArbGasInfo := insert(MakePrecompile(pgen.ArbGasInfoMetaData, &ArbGasInfo{Address: types.ArbGasInfoAddress})) ArbGasInfo.methodsByName["GetL1FeesAvailable"].arbosVersion = 10 ArbGasInfo.methodsByName["GetL1RewardRate"].arbosVersion = 11 ArbGasInfo.methodsByName["GetL1RewardRecipient"].arbosVersion = 11 @@ -541,8 +536,8 @@ func Precompiles() map[addr]ArbosPrecompile { ArbGasInfo.methodsByName["GetL1PricingFundsDueForRewards"].arbosVersion = 20 ArbGasInfo.methodsByName["GetL1PricingUnitsSinceUpdate"].arbosVersion = 20 ArbGasInfo.methodsByName["GetLastL1PricingSurplus"].arbosVersion = 20 - insert(MakePrecompile(templates.ArbAggregatorMetaData, &ArbAggregator{Address: hex("6d")})) - insert(MakePrecompile(templates.ArbStatisticsMetaData, &ArbStatistics{Address: hex("6f")})) + insert(MakePrecompile(pgen.ArbAggregatorMetaData, &ArbAggregator{Address: types.ArbAggregatorAddress})) + insert(MakePrecompile(pgen.ArbStatisticsMetaData, &ArbStatistics{Address: types.ArbStatisticsAddress})) eventCtx := func(gasLimit uint64, err error) *Context { if err != nil { @@ -554,14 +549,35 @@ func Precompiles() map[addr]ArbosPrecompile { } } - ArbOwnerPublic := insert(MakePrecompile(templates.ArbOwnerPublicMetaData, &ArbOwnerPublic{Address: hex("6b")})) + ArbOwnerPublicImpl := &ArbOwnerPublic{Address: types.ArbOwnerPublicAddress} + ArbOwnerPublic := insert(MakePrecompile(pgen.ArbOwnerPublicMetaData, ArbOwnerPublicImpl)) ArbOwnerPublic.methodsByName["GetInfraFeeAccount"].arbosVersion = 5 ArbOwnerPublic.methodsByName["RectifyChainOwner"].arbosVersion = 11 ArbOwnerPublic.methodsByName["GetBrotliCompressionLevel"].arbosVersion = 20 ArbOwnerPublic.methodsByName["GetScheduledUpgrade"].arbosVersion = 20 + ArbWasmImpl := &ArbWasm{Address: types.ArbWasmAddress} + ArbWasm := insert(MakePrecompile(pgen.ArbWasmMetaData, ArbWasmImpl)) + ArbWasm.arbosVersion = params.ArbosVersion_Stylus + programs.ProgramNotWasmError = ArbWasmImpl.ProgramNotWasmError + programs.ProgramNotActivatedError = ArbWasmImpl.ProgramNotActivatedError + programs.ProgramNeedsUpgradeError = ArbWasmImpl.ProgramNeedsUpgradeError + programs.ProgramExpiredError = ArbWasmImpl.ProgramExpiredError + programs.ProgramUpToDateError = ArbWasmImpl.ProgramUpToDateError + programs.ProgramKeepaliveTooSoon = ArbWasmImpl.ProgramKeepaliveTooSoonError + for _, method := range ArbWasm.methods { + method.arbosVersion = ArbWasm.arbosVersion + } + + ArbWasmCacheImpl := &ArbWasmCache{Address: types.ArbWasmCacheAddress} + ArbWasmCache := insert(MakePrecompile(pgen.ArbWasmCacheMetaData, ArbWasmCacheImpl)) + ArbWasmCache.arbosVersion = params.ArbosVersion_Stylus + for _, method := range ArbWasmCache.methods { + method.arbosVersion = ArbWasmCache.arbosVersion + } + ArbRetryableImpl := &ArbRetryableTx{Address: types.ArbRetryableTxAddress} - ArbRetryable := insert(MakePrecompile(templates.ArbRetryableTxMetaData, ArbRetryableImpl)) + ArbRetryable := insert(MakePrecompile(pgen.ArbRetryableTxMetaData, ArbRetryableImpl)) arbos.ArbRetryableTxAddress = ArbRetryable.address arbos.RedeemScheduledEventID = ArbRetryable.events["RedeemScheduled"].template.ID arbos.EmitReedeemScheduledEvent = func( @@ -579,30 +595,46 @@ func Precompiles() map[addr]ArbosPrecompile { return ArbRetryableImpl.TicketCreated(context, evm, ticketId) } - ArbSys := insert(MakePrecompile(templates.ArbSysMetaData, &ArbSys{Address: types.ArbSysAddress})) + ArbSys := insert(MakePrecompile(pgen.ArbSysMetaData, &ArbSys{Address: types.ArbSysAddress})) arbos.ArbSysAddress = ArbSys.address arbos.L2ToL1TransactionEventID = ArbSys.events["L2ToL1Transaction"].template.ID arbos.L2ToL1TxEventID = ArbSys.events["L2ToL1Tx"].template.ID - ArbOwnerImpl := &ArbOwner{Address: hex("70")} + ArbOwnerImpl := &ArbOwner{Address: types.ArbOwnerAddress} emitOwnerActs := func(evm mech, method bytes4, owner addr, data []byte) error { context := eventCtx(ArbOwnerImpl.OwnerActsGasCost(method, owner, data)) return ArbOwnerImpl.OwnerActs(context, evm, method, owner, data) } - _, ArbOwner := MakePrecompile(templates.ArbOwnerMetaData, ArbOwnerImpl) + _, ArbOwner := MakePrecompile(pgen.ArbOwnerMetaData, ArbOwnerImpl) ArbOwner.methodsByName["GetInfraFeeAccount"].arbosVersion = 5 ArbOwner.methodsByName["SetInfraFeeAccount"].arbosVersion = 5 ArbOwner.methodsByName["ReleaseL1PricerSurplusFunds"].arbosVersion = 10 ArbOwner.methodsByName["SetChainConfig"].arbosVersion = 11 ArbOwner.methodsByName["SetBrotliCompressionLevel"].arbosVersion = 20 + stylusMethods := []string{ + "SetInkPrice", "SetWasmMaxStackDepth", "SetWasmFreePages", "SetWasmPageGas", + "SetWasmPageLimit", "SetWasmMinInitGas", "SetWasmInitCostScalar", + "SetWasmExpiryDays", "SetWasmKeepaliveDays", + "SetWasmBlockCacheSize", "AddWasmCacheManager", "RemoveWasmCacheManager", + } + for _, method := range stylusMethods { + ArbOwner.methodsByName[method].arbosVersion = params.ArbosVersion_Stylus + } insert(ownerOnly(ArbOwnerImpl.Address, ArbOwner, emitOwnerActs)) - insert(debugOnly(MakePrecompile(templates.ArbDebugMetaData, &ArbDebug{Address: hex("ff")}))) + _, arbDebug := MakePrecompile(pgen.ArbDebugMetaData, &ArbDebug{Address: types.ArbDebugAddress}) + arbDebug.methodsByName["Panic"].arbosVersion = params.ArbosVersion_Stylus + insert(debugOnly(arbDebug.address, arbDebug)) - ArbosActs := insert(MakePrecompile(templates.ArbosActsMetaData, &ArbosActs{Address: types.ArbosAddress})) + ArbosActs := insert(MakePrecompile(pgen.ArbosActsMetaData, &ArbosActs{Address: types.ArbosAddress})) arbos.InternalTxStartBlockMethodID = ArbosActs.GetMethodID("StartBlock") arbos.InternalTxBatchPostingReportMethodID = ArbosActs.GetMethodID("BatchPostingReport") + for _, contract := range contracts { + precompile := contract.Precompile() + arbosState.PrecompileMinArbOSVersions[precompile.address] = precompile.arbosVersion + } + return contracts } @@ -620,6 +652,10 @@ func (p *Precompile) GetMethodID(name string) bytes4 { return *(*bytes4)(method.template.ID) } +func (p *Precompile) ArbosVersion() uint64 { + return p.arbosVersion +} + // Call a precompile in typed form, deserializing its inputs and serializing its outputs func (p *Precompile) Call( input []byte, @@ -745,8 +781,14 @@ func (p *Precompile) Call( } return solErr.data, callerCtx.gasLeft, vm.ErrExecutionReverted } + if errors.Is(errRet, programs.ErrProgramActivation) { + return nil, 0, errRet + } if !errors.Is(errRet, vm.ErrOutOfGas) { - log.Debug("precompile reverted with non-solidity error", "precompile", precompileAddress, "input", input, "err", errRet) + log.Debug( + "precompile reverted with non-solidity error", + "precompile", precompileAddress, "input", input, "err", errRet, + ) } // nolint:errorlint if arbosVersion >= 11 || errRet == vm.ErrExecutionReverted { diff --git a/precompiles/precompile_test.go b/precompiles/precompile_test.go index 975856bced..86047038dc 100644 --- a/precompiles/precompile_test.go +++ b/precompiles/precompile_test.go @@ -5,6 +5,7 @@ package precompiles import ( "fmt" + "io" "math/big" "os" "testing" @@ -181,9 +182,10 @@ func TestEventCosts(t *testing.T) { func TestPrecompilesPerArbosVersion(t *testing.T) { // Set up a logger in case log.Crit is called by Precompiles() - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlWarn) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelWarn) + log.SetDefault(log.NewLogger(glogger)) expectedNewMethodsPerArbosVersion := map[uint64]int{ 0: 89, @@ -191,13 +193,15 @@ func TestPrecompilesPerArbosVersion(t *testing.T) { 10: 2, 11: 4, 20: 8, + 30: 38, } precompiles := Precompiles() newMethodsPerArbosVersion := make(map[uint64]int) for _, precompile := range precompiles { for _, method := range precompile.Precompile().methods { - newMethodsPerArbosVersion[method.arbosVersion]++ + version := arbmath.MaxInt(method.arbosVersion, precompile.Precompile().arbosVersion) + newMethodsPerArbosVersion[version]++ } } diff --git a/pubsub/common.go b/pubsub/common.go new file mode 100644 index 0000000000..9f05304e46 --- /dev/null +++ b/pubsub/common.go @@ -0,0 +1,29 @@ +package pubsub + +import ( + "context" + + "github.com/ethereum/go-ethereum/log" + "github.com/go-redis/redis/v8" +) + +// CreateStream tries to create stream with given name, if it already exists +// does not return an error. +func CreateStream(ctx context.Context, streamName string, client redis.UniversalClient) error { + _, err := client.XGroupCreateMkStream(ctx, streamName, streamName, "$").Result() + if err != nil && !StreamExists(ctx, streamName, client) { + return err + } + return nil +} + +// StreamExists returns whether there are any consumer group for specified +// redis stream. +func StreamExists(ctx context.Context, streamName string, client redis.UniversalClient) bool { + got, err := client.Do(ctx, "XINFO", "STREAM", streamName).Result() + if err != nil { + log.Error("Reading redis streams", "error", err) + return false + } + return got != nil +} diff --git a/pubsub/consumer.go b/pubsub/consumer.go new file mode 100644 index 0000000000..df3695606d --- /dev/null +++ b/pubsub/consumer.go @@ -0,0 +1,175 @@ +package pubsub + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/go-redis/redis/v8" + "github.com/google/uuid" + "github.com/offchainlabs/nitro/util/stopwaiter" + "github.com/spf13/pflag" +) + +type ConsumerConfig struct { + // Timeout of result entry in Redis. + ResponseEntryTimeout time.Duration `koanf:"response-entry-timeout"` + // Duration after which consumer is considered to be dead if heartbeat + // is not updated. + KeepAliveTimeout time.Duration `koanf:"keepalive-timeout"` +} + +var DefaultConsumerConfig = ConsumerConfig{ + ResponseEntryTimeout: time.Hour, + KeepAliveTimeout: 5 * time.Minute, +} + +var TestConsumerConfig = ConsumerConfig{ + ResponseEntryTimeout: time.Minute, + KeepAliveTimeout: 30 * time.Millisecond, +} + +func ConsumerConfigAddOptions(prefix string, f *pflag.FlagSet) { + f.Duration(prefix+".response-entry-timeout", DefaultConsumerConfig.ResponseEntryTimeout, "timeout for response entry") + f.Duration(prefix+".keepalive-timeout", DefaultConsumerConfig.KeepAliveTimeout, "timeout after which consumer is considered inactive if heartbeat wasn't performed") +} + +// Consumer implements a consumer for redis stream provides heartbeat to +// indicate it is alive. +type Consumer[Request any, Response any] struct { + stopwaiter.StopWaiter + id string + client redis.UniversalClient + redisStream string + redisGroup string + cfg *ConsumerConfig +} + +type Message[Request any] struct { + ID string + Value Request +} + +func NewConsumer[Request any, Response any](client redis.UniversalClient, streamName string, cfg *ConsumerConfig) (*Consumer[Request, Response], error) { + if streamName == "" { + return nil, fmt.Errorf("redis stream name cannot be empty") + } + return &Consumer[Request, Response]{ + id: uuid.NewString(), + client: client, + redisStream: streamName, + redisGroup: streamName, // There is 1-1 mapping of redis stream and consumer group. + cfg: cfg, + }, nil +} + +// Start starts the consumer to iteratively perform heartbeat in configured intervals. +func (c *Consumer[Request, Response]) Start(ctx context.Context) { + c.StopWaiter.Start(ctx, c) + c.StopWaiter.CallIteratively( + func(ctx context.Context) time.Duration { + c.heartBeat(ctx) + return c.cfg.KeepAliveTimeout / 10 + }, + ) +} + +func (c *Consumer[Request, Response]) StopAndWait() { + c.StopWaiter.StopAndWait() + c.deleteHeartBeat(c.GetParentContext()) +} + +func heartBeatKey(id string) string { + return fmt.Sprintf("consumer:%s:heartbeat", id) +} + +func (c *Consumer[Request, Response]) RedisClient() redis.UniversalClient { + return c.client +} + +func (c *Consumer[Request, Response]) StreamName() string { + return c.redisStream +} + +func (c *Consumer[Request, Response]) heartBeatKey() string { + return heartBeatKey(c.id) +} + +// deleteHeartBeat deletes the heartbeat to indicate it is being shut down. +func (c *Consumer[Request, Response]) deleteHeartBeat(ctx context.Context) { + if err := c.client.Del(ctx, c.heartBeatKey()).Err(); err != nil { + l := log.Info + if ctx.Err() != nil { + l = log.Error + } + l("Deleting heardbeat", "consumer", c.id, "error", err) + } +} + +// heartBeat updates the heartBeat key indicating aliveness. +func (c *Consumer[Request, Response]) heartBeat(ctx context.Context) { + if err := c.client.Set(ctx, c.heartBeatKey(), time.Now().UnixMilli(), 2*c.cfg.KeepAliveTimeout).Err(); err != nil { + l := log.Info + if ctx.Err() != nil { + l = log.Error + } + l("Updating heardbeat", "consumer", c.id, "error", err) + } +} + +// Consumer first checks it there exists pending message that is claimed by +// unresponsive consumer, if not then reads from the stream. +func (c *Consumer[Request, Response]) Consume(ctx context.Context) (*Message[Request], error) { + res, err := c.client.XReadGroup(ctx, &redis.XReadGroupArgs{ + Group: c.redisGroup, + Consumer: c.id, + // Receive only messages that were never delivered to any other consumer, + // that is, only new messages. + Streams: []string{c.redisStream, ">"}, + Count: 1, + Block: time.Millisecond, // 0 seems to block the read instead of immediately returning + }).Result() + if errors.Is(err, redis.Nil) { + return nil, nil + } + if err != nil { + return nil, fmt.Errorf("reading message for consumer: %q: %w", c.id, err) + } + if len(res) != 1 || len(res[0].Messages) != 1 { + return nil, fmt.Errorf("redis returned entries: %+v, for querying single message", res) + } + var ( + value = res[0].Messages[0].Values[messageKey] + data, ok = (value).(string) + ) + if !ok { + return nil, fmt.Errorf("casting request to string: %w", err) + } + var req Request + if err := json.Unmarshal([]byte(data), &req); err != nil { + return nil, fmt.Errorf("unmarshaling value: %v, error: %w", value, err) + } + log.Debug("Redis stream consuming", "consumer_id", c.id, "message_id", res[0].Messages[0].ID) + return &Message[Request]{ + ID: res[0].Messages[0].ID, + Value: req, + }, nil +} + +func (c *Consumer[Request, Response]) SetResult(ctx context.Context, messageID string, result Response) error { + resp, err := json.Marshal(result) + if err != nil { + return fmt.Errorf("marshaling result: %w", err) + } + acquired, err := c.client.SetNX(ctx, messageID, resp, c.cfg.ResponseEntryTimeout).Result() + if err != nil || !acquired { + return fmt.Errorf("setting result for message: %v, error: %w", messageID, err) + } + if _, err := c.client.XAck(ctx, c.redisStream, c.redisGroup, messageID).Result(); err != nil { + return fmt.Errorf("acking message: %v, error: %w", messageID, err) + } + return nil +} diff --git a/pubsub/producer.go b/pubsub/producer.go new file mode 100644 index 0000000000..074670ca0f --- /dev/null +++ b/pubsub/producer.go @@ -0,0 +1,299 @@ +// Package pubsub implements publisher/subscriber model (one to many). +// During normal operation, publisher returns "Promise" when publishing a +// message, which will return resposne from consumer when awaited. +// If the consumer processing the request becomes inactive, message is +// re-inserted (if EnableReproduce flag is enabled), and will be picked up by +// another consumer. +// We are assuming here that keeepAliveTimeout is set to some sensible value +// and once consumer becomes inactive, it doesn't activate without restart. +package pubsub + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "sync" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/go-redis/redis/v8" + "github.com/google/uuid" + "github.com/offchainlabs/nitro/util/containers" + "github.com/offchainlabs/nitro/util/stopwaiter" + "github.com/spf13/pflag" +) + +const ( + messageKey = "msg" + defaultGroup = "default_consumer_group" +) + +type Producer[Request any, Response any] struct { + stopwaiter.StopWaiter + id string + client redis.UniversalClient + redisStream string + redisGroup string + cfg *ProducerConfig + + promisesLock sync.RWMutex + promises map[string]*containers.Promise[Response] + + // Used for running checks for pending messages with inactive consumers + // and checking responses from consumers iteratively for the first time when + // Produce is called. + once sync.Once +} + +type ProducerConfig struct { + // When enabled, messages that are sent to consumers that later die before + // processing them, will be re-inserted into the stream to be proceesed by + // another consumer + EnableReproduce bool `koanf:"enable-reproduce"` + // Interval duration in which producer checks for pending messages delivered + // to the consumers that are currently inactive. + CheckPendingInterval time.Duration `koanf:"check-pending-interval"` + // Duration after which consumer is considered to be dead if heartbeat + // is not updated. + KeepAliveTimeout time.Duration `koanf:"keepalive-timeout"` + // Interval duration for checking the result set by consumers. + CheckResultInterval time.Duration `koanf:"check-result-interval"` +} + +var DefaultProducerConfig = ProducerConfig{ + EnableReproduce: true, + CheckPendingInterval: time.Second, + KeepAliveTimeout: 5 * time.Minute, + CheckResultInterval: 5 * time.Second, +} + +var TestProducerConfig = ProducerConfig{ + EnableReproduce: true, + CheckPendingInterval: 10 * time.Millisecond, + KeepAliveTimeout: 100 * time.Millisecond, + CheckResultInterval: 5 * time.Millisecond, +} + +func ProducerAddConfigAddOptions(prefix string, f *pflag.FlagSet) { + f.Bool(prefix+".enable-reproduce", DefaultProducerConfig.EnableReproduce, "when enabled, messages with dead consumer will be re-inserted into the stream") + f.Duration(prefix+".check-pending-interval", DefaultProducerConfig.CheckPendingInterval, "interval in which producer checks pending messages whether consumer processing them is inactive") + f.Duration(prefix+".check-result-interval", DefaultProducerConfig.CheckResultInterval, "interval in which producer checks pending messages whether consumer processing them is inactive") + f.Duration(prefix+".keepalive-timeout", DefaultProducerConfig.KeepAliveTimeout, "timeout after which consumer is considered inactive if heartbeat wasn't performed") +} + +func NewProducer[Request any, Response any](client redis.UniversalClient, streamName string, cfg *ProducerConfig) (*Producer[Request, Response], error) { + if client == nil { + return nil, fmt.Errorf("redis client cannot be nil") + } + if streamName == "" { + return nil, fmt.Errorf("stream name cannot be empty") + } + return &Producer[Request, Response]{ + id: uuid.NewString(), + client: client, + redisStream: streamName, + redisGroup: streamName, // There is 1-1 mapping of redis stream and consumer group. + cfg: cfg, + promises: make(map[string]*containers.Promise[Response]), + }, nil +} + +func (p *Producer[Request, Response]) errorPromisesFor(msgs []*Message[Request]) { + p.promisesLock.Lock() + defer p.promisesLock.Unlock() + for _, msg := range msgs { + if promise, found := p.promises[msg.ID]; found { + promise.ProduceError(fmt.Errorf("internal error, consumer died while serving the request")) + delete(p.promises, msg.ID) + } + } +} + +// checkAndReproduce reproduce pending messages that were sent to consumers +// that are currently inactive. +func (p *Producer[Request, Response]) checkAndReproduce(ctx context.Context) time.Duration { + msgs, err := p.checkPending(ctx) + if err != nil { + log.Error("Checking pending messages", "error", err) + return p.cfg.CheckPendingInterval + } + if len(msgs) == 0 { + return p.cfg.CheckPendingInterval + } + if !p.cfg.EnableReproduce { + p.errorPromisesFor(msgs) + return p.cfg.CheckPendingInterval + } + acked := make(map[string]Request) + for _, msg := range msgs { + if _, err := p.client.XAck(ctx, p.redisStream, p.redisGroup, msg.ID).Result(); err != nil { + log.Error("ACKing message", "error", err) + continue + } + acked[msg.ID] = msg.Value + } + for k, v := range acked { + // Only re-insert messages that were removed the the pending list first. + _, err := p.reproduce(ctx, v, k) + if err != nil { + log.Error("Re-inserting pending messages with inactive consumers", "error", err) + } + } + return p.cfg.CheckPendingInterval +} + +// checkResponses checks iteratively whether response for the promise is ready. +func (p *Producer[Request, Response]) checkResponses(ctx context.Context) time.Duration { + p.promisesLock.Lock() + defer p.promisesLock.Unlock() + for id, promise := range p.promises { + res, err := p.client.Get(ctx, id).Result() + if err != nil { + if errors.Is(err, redis.Nil) { + continue + } + log.Error("Error reading value in redis", "key", id, "error", err) + } + var resp Response + if err := json.Unmarshal([]byte(res), &resp); err != nil { + log.Error("Error unmarshaling", "value", res, "error", err) + continue + } + promise.Produce(resp) + delete(p.promises, id) + } + return p.cfg.CheckResultInterval +} + +func (p *Producer[Request, Response]) Start(ctx context.Context) { + p.StopWaiter.Start(ctx, p) +} + +func (p *Producer[Request, Response]) promisesLen() int { + p.promisesLock.Lock() + defer p.promisesLock.Unlock() + return len(p.promises) +} + +// reproduce is used when Producer claims ownership on the pending +// message that was sent to inactive consumer and reinserts it into the stream, +// so that seamlessly return the answer in the same promise. +func (p *Producer[Request, Response]) reproduce(ctx context.Context, value Request, oldKey string) (*containers.Promise[Response], error) { + val, err := json.Marshal(value) + if err != nil { + return nil, fmt.Errorf("marshaling value: %w", err) + } + id, err := p.client.XAdd(ctx, &redis.XAddArgs{ + Stream: p.redisStream, + Values: map[string]any{messageKey: val}, + }).Result() + if err != nil { + return nil, fmt.Errorf("adding values to redis: %w", err) + } + p.promisesLock.Lock() + defer p.promisesLock.Unlock() + promise := p.promises[oldKey] + if oldKey != "" && promise == nil { + // This will happen if the old consumer became inactive but then ack_d + // the message afterwards. + return nil, fmt.Errorf("error reproducing the message, could not find existing one") + } + if oldKey == "" || promise == nil { + pr := containers.NewPromise[Response](nil) + promise = &pr + } + delete(p.promises, oldKey) + p.promises[id] = promise + return promise, nil +} + +func (p *Producer[Request, Response]) Produce(ctx context.Context, value Request) (*containers.Promise[Response], error) { + log.Debug("Redis stream producing", "value", value) + p.once.Do(func() { + p.StopWaiter.CallIteratively(p.checkAndReproduce) + p.StopWaiter.CallIteratively(p.checkResponses) + }) + return p.reproduce(ctx, value, "") +} + +// Check if a consumer is with specified ID is alive. +func (p *Producer[Request, Response]) isConsumerAlive(ctx context.Context, consumerID string) bool { + if _, err := p.client.Get(ctx, heartBeatKey(consumerID)).Int64(); err != nil { + return false + } + return true +} + +func (p *Producer[Request, Response]) havePromiseFor(messageID string) bool { + p.promisesLock.Lock() + defer p.promisesLock.Unlock() + _, found := p.promises[messageID] + return found +} + +func (p *Producer[Request, Response]) checkPending(ctx context.Context) ([]*Message[Request], error) { + pendingMessages, err := p.client.XPendingExt(ctx, &redis.XPendingExtArgs{ + Stream: p.redisStream, + Group: p.redisGroup, + Start: "-", + End: "+", + Count: 100, + }).Result() + + if err != nil && !errors.Is(err, redis.Nil) { + return nil, fmt.Errorf("querying pending messages: %w", err) + } + if len(pendingMessages) == 0 { + return nil, nil + } + // IDs of the pending messages with inactive consumers. + var ids []string + active := make(map[string]bool) + for _, msg := range pendingMessages { + // Ignore messages not produced by this producer. + if !p.havePromiseFor(msg.ID) { + continue + } + alive, found := active[msg.Consumer] + if !found { + alive = p.isConsumerAlive(ctx, msg.Consumer) + active[msg.Consumer] = alive + } + if alive { + continue + } + ids = append(ids, msg.ID) + } + if len(ids) == 0 { + log.Trace("There are no pending messages with inactive consumers") + return nil, nil + } + log.Info("Attempting to claim", "messages", ids) + claimedMsgs, err := p.client.XClaim(ctx, &redis.XClaimArgs{ + Stream: p.redisStream, + Group: p.redisGroup, + Consumer: p.id, + MinIdle: p.cfg.KeepAliveTimeout, + Messages: ids, + }).Result() + if err != nil { + return nil, fmt.Errorf("claiming ownership on messages: %v, error: %w", ids, err) + } + var res []*Message[Request] + for _, msg := range claimedMsgs { + data, ok := (msg.Values[messageKey]).(string) + if !ok { + return nil, fmt.Errorf("casting request: %v to bytes", msg.Values[messageKey]) + } + var req Request + if err := json.Unmarshal([]byte(data), &req); err != nil { + return nil, fmt.Errorf("marshaling value: %v, error: %w", msg.Values[messageKey], err) + } + res = append(res, &Message[Request]{ + ID: msg.ID, + Value: req, + }) + } + return res, nil +} diff --git a/pubsub/pubsub_test.go b/pubsub/pubsub_test.go new file mode 100644 index 0000000000..72504602e3 --- /dev/null +++ b/pubsub/pubsub_test.go @@ -0,0 +1,336 @@ +package pubsub + +import ( + "context" + "errors" + "fmt" + "os" + "sort" + "testing" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/go-redis/redis/v8" + "github.com/google/go-cmp/cmp" + "github.com/google/uuid" + "github.com/offchainlabs/nitro/util/containers" + "github.com/offchainlabs/nitro/util/redisutil" +) + +var ( + consumersCount = 10 + messagesCount = 100 +) + +type testRequest struct { + Request string +} + +type testResponse struct { + Response string +} + +func createRedisGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { + t.Helper() + // Stream name and group name are the same. + if _, err := client.XGroupCreateMkStream(ctx, streamName, streamName, "$").Result(); err != nil { + t.Fatalf("Error creating stream group: %v", err) + } +} + +func destroyRedisGroup(ctx context.Context, t *testing.T, streamName string, client redis.UniversalClient) { + t.Helper() + if _, err := client.XGroupDestroy(ctx, streamName, streamName).Result(); err != nil { + log.Debug("Error destroying a stream group", "error", err) + } +} + +type configOpt interface { + apply(consCfg *ConsumerConfig, prodCfg *ProducerConfig) +} + +type disableReproduce struct{} + +func (e *disableReproduce) apply(_ *ConsumerConfig, prodCfg *ProducerConfig) { + prodCfg.EnableReproduce = false +} + +func producerCfg() *ProducerConfig { + return &ProducerConfig{ + EnableReproduce: TestProducerConfig.EnableReproduce, + CheckPendingInterval: TestProducerConfig.CheckPendingInterval, + KeepAliveTimeout: TestProducerConfig.KeepAliveTimeout, + CheckResultInterval: TestProducerConfig.CheckResultInterval, + } +} + +func consumerCfg() *ConsumerConfig { + return &ConsumerConfig{ + ResponseEntryTimeout: TestConsumerConfig.ResponseEntryTimeout, + KeepAliveTimeout: TestConsumerConfig.KeepAliveTimeout, + } +} + +func newProducerConsumers(ctx context.Context, t *testing.T, opts ...configOpt) (*Producer[testRequest, testResponse], []*Consumer[testRequest, testResponse]) { + t.Helper() + redisClient, err := redisutil.RedisClientFromURL(redisutil.CreateTestRedis(ctx, t)) + if err != nil { + t.Fatalf("RedisClientFromURL() unexpected error: %v", err) + } + prodCfg, consCfg := producerCfg(), consumerCfg() + streamName := fmt.Sprintf("stream:%s", uuid.NewString()) + for _, o := range opts { + o.apply(consCfg, prodCfg) + } + producer, err := NewProducer[testRequest, testResponse](redisClient, streamName, prodCfg) + if err != nil { + t.Fatalf("Error creating new producer: %v", err) + } + + var consumers []*Consumer[testRequest, testResponse] + for i := 0; i < consumersCount; i++ { + c, err := NewConsumer[testRequest, testResponse](redisClient, streamName, consCfg) + if err != nil { + t.Fatalf("Error creating new consumer: %v", err) + } + consumers = append(consumers, c) + } + createRedisGroup(ctx, t, streamName, producer.client) + t.Cleanup(func() { + ctx := context.Background() + destroyRedisGroup(ctx, t, streamName, producer.client) + var keys []string + for _, c := range consumers { + keys = append(keys, c.heartBeatKey()) + } + if _, err := producer.client.Del(ctx, keys...).Result(); err != nil { + log.Debug("Error deleting heartbeat keys", "error", err) + } + }) + return producer, consumers +} + +func messagesMaps(n int) []map[string]string { + ret := make([]map[string]string, n) + for i := 0; i < n; i++ { + ret[i] = make(map[string]string) + } + return ret +} + +func wantMessages(n int) []string { + var ret []string + for i := 0; i < n; i++ { + ret = append(ret, fmt.Sprintf("msg: %d", i)) + } + sort.Strings(ret) + return ret +} + +func flatten(responses [][]string) []string { + var ret []string + for _, v := range responses { + ret = append(ret, v...) + } + sort.Strings(ret) + return ret +} + +func produceMessages(ctx context.Context, msgs []string, producer *Producer[testRequest, testResponse]) ([]*containers.Promise[testResponse], error) { + var promises []*containers.Promise[testResponse] + for i := 0; i < messagesCount; i++ { + promise, err := producer.Produce(ctx, testRequest{Request: msgs[i]}) + if err != nil { + return nil, err + } + promises = append(promises, promise) + } + return promises, nil +} + +func awaitResponses(ctx context.Context, promises []*containers.Promise[testResponse]) ([]string, error) { + var ( + responses []string + errs []error + ) + for _, p := range promises { + res, err := p.Await(ctx) + if err != nil { + errs = append(errs, err) + continue + } + responses = append(responses, res.Response) + } + return responses, errors.Join(errs...) +} + +// consume messages from every consumer except stopped ones. +func consume(ctx context.Context, t *testing.T, consumers []*Consumer[testRequest, testResponse]) ([]map[string]string, [][]string) { + t.Helper() + gotMessages := messagesMaps(consumersCount) + wantResponses := make([][]string, consumersCount) + for idx := 0; idx < consumersCount; idx++ { + if consumers[idx].Stopped() { + continue + } + idx, c := idx, consumers[idx] + c.Start(ctx) + c.StopWaiter.LaunchThread( + func(ctx context.Context) { + for { + + res, err := c.Consume(ctx) + if err != nil { + if !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) { + t.Errorf("Consume() unexpected error: %v", err) + continue + } + return + } + if res == nil { + continue + } + gotMessages[idx][res.ID] = res.Value.Request + resp := fmt.Sprintf("result for: %v", res.ID) + if err := c.SetResult(ctx, res.ID, testResponse{Response: resp}); err != nil { + t.Errorf("Error setting a result: %v", err) + } + wantResponses[idx] = append(wantResponses[idx], resp) + } + }) + } + return gotMessages, wantResponses +} + +func TestRedisProduce(t *testing.T) { + log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelTrace, true))) + t.Parallel() + for _, tc := range []struct { + name string + killConsumers bool + }{ + { + name: "all consumers are active", + killConsumers: false, + }, + { + name: "some consumers killed, others should take over their work", + killConsumers: true, + }, + } { + t.Run(tc.name, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + producer, consumers := newProducerConsumers(ctx, t) + producer.Start(ctx) + wantMsgs := wantMessages(messagesCount) + promises, err := produceMessages(ctx, wantMsgs, producer) + if err != nil { + t.Fatalf("Error producing messages: %v", err) + } + if tc.killConsumers { + // Consumer messages in every third consumer but don't ack them to check + // that other consumers will claim ownership on those messages. + for i := 0; i < len(consumers); i += 3 { + consumers[i].Start(ctx) + if _, err := consumers[i].Consume(ctx); err != nil { + t.Errorf("Error consuming message: %v", err) + } + consumers[i].StopAndWait() + } + + } + time.Sleep(time.Second) + gotMessages, wantResponses := consume(ctx, t, consumers) + gotResponses, err := awaitResponses(ctx, promises) + if err != nil { + t.Fatalf("Error awaiting responses: %v", err) + } + producer.StopAndWait() + for _, c := range consumers { + c.StopAndWait() + } + got, err := mergeValues(gotMessages) + if err != nil { + t.Fatalf("mergeMaps() unexpected error: %v", err) + } + + if diff := cmp.Diff(wantMsgs, got); diff != "" { + t.Errorf("Unexpected diff (-want +got):\n%s\n", diff) + } + wantResp := flatten(wantResponses) + sort.Strings(gotResponses) + if diff := cmp.Diff(wantResp, gotResponses); diff != "" { + t.Errorf("Unexpected diff in responses:\n%s\n", diff) + } + if cnt := producer.promisesLen(); cnt != 0 { + t.Errorf("Producer still has %d unfullfilled promises", cnt) + } + }) + } +} + +func TestRedisReproduceDisabled(t *testing.T) { + t.Parallel() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + producer, consumers := newProducerConsumers(ctx, t, &disableReproduce{}) + producer.Start(ctx) + wantMsgs := wantMessages(messagesCount) + promises, err := produceMessages(ctx, wantMsgs, producer) + if err != nil { + t.Fatalf("Error producing messages: %v", err) + } + + // Consumer messages in every third consumer but don't ack them to check + // that other consumers will claim ownership on those messages. + for i := 0; i < len(consumers); i += 3 { + consumers[i].Start(ctx) + if _, err := consumers[i].Consume(ctx); err != nil { + t.Errorf("Error consuming message: %v", err) + } + consumers[i].StopAndWait() + } + + gotMessages, _ := consume(ctx, t, consumers) + gotResponses, err := awaitResponses(ctx, promises) + if err == nil { + t.Fatalf("All promises were fullfilled with reproduce disabled and some consumers killed") + } + producer.StopAndWait() + for _, c := range consumers { + c.StopWaiter.StopAndWait() + } + got, err := mergeValues(gotMessages) + if err != nil { + t.Fatalf("mergeMaps() unexpected error: %v", err) + } + wantMsgCnt := messagesCount - ((consumersCount + 2) / 3) + if len(got) != wantMsgCnt { + t.Fatalf("Got: %d messages, want %d", len(got), wantMsgCnt) + } + if len(gotResponses) != wantMsgCnt { + t.Errorf("Got %d responses want: %d\n", len(gotResponses), wantMsgCnt) + } + if cnt := producer.promisesLen(); cnt != 0 { + t.Errorf("Producer still has %d unfullfilled promises", cnt) + } +} + +// mergeValues merges maps from the slice and returns their values. +// Returns and error if there exists duplicate key. +func mergeValues(messages []map[string]string) ([]string, error) { + res := make(map[string]any) + var ret []string + for _, m := range messages { + for k, v := range m { + if _, found := res[k]; found { + return nil, fmt.Errorf("duplicate key: %v", k) + } + res[k] = v + ret = append(ret, v) + } + } + sort.Strings(ret) + return ret, nil +} diff --git a/relay/relay.go b/relay/relay.go index 8e29971384..89bb899f29 100644 --- a/relay/relay.go +++ b/relay/relay.go @@ -10,8 +10,6 @@ import ( flag "github.com/spf13/pflag" - "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/broadcastclient" "github.com/offchainlabs/nitro/broadcastclients" @@ -120,7 +118,7 @@ func (r *Relay) StopAndWait() { type Config struct { Conf genericconf.ConfConfig `koanf:"conf"` Chain L2Config `koanf:"chain"` - LogLevel int `koanf:"log-level"` + LogLevel string `koanf:"log-level"` LogType string `koanf:"log-type"` Metrics bool `koanf:"metrics"` MetricsServer genericconf.MetricsServerConfig `koanf:"metrics-server"` @@ -133,7 +131,7 @@ type Config struct { var ConfigDefault = Config{ Conf: genericconf.ConfConfigDefault, Chain: L2ConfigDefault, - LogLevel: int(log.LvlInfo), + LogLevel: "INFO", LogType: "plaintext", Metrics: false, MetricsServer: genericconf.MetricsServerConfigDefault, @@ -146,7 +144,7 @@ var ConfigDefault = Config{ func ConfigAddOptions(f *flag.FlagSet) { genericconf.ConfConfigAddOptions("conf", f) L2ConfigAddOptions("chain", f) - f.Int("log-level", ConfigDefault.LogLevel, "log level") + f.String("log-level", ConfigDefault.LogLevel, "log level, valid values are CRIT, ERROR, WARN, INFO, DEBUG, TRACE") f.String("log-type", ConfigDefault.LogType, "log type") f.Bool("metrics", ConfigDefault.Metrics, "enable metrics") genericconf.MetricsServerAddOptions("metrics-server", f) diff --git a/scripts/split-val-entry.sh b/scripts/split-val-entry.sh new file mode 100755 index 0000000000..8e1be0f6cc --- /dev/null +++ b/scripts/split-val-entry.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +xxd -l 32 -ps -c 40 /dev/urandom > /tmp/nitro-val.jwt + +echo launching validation servers +# To add validation server: +# > launch them here with a different port and --validation.wasm.root-path +# add their port to wait loop +# edit validation-server-configs-list to include the other nodes +/usr/local/bin/nitro-val --file-logging.enable=false --auth.addr 127.0.0.10 --auth.origins 127.0.0.1 --auth.jwtsecret /tmp/nitro-val.jwt --auth.port 52000 & +/home/user/nitro-legacy/bin/nitro-val --file-logging.enable=false --auth.addr 127.0.0.10 --auth.origins 127.0.0.1 --auth.jwtsecret /tmp/nitro-val.jwt --auth.port 52001 --validation.wasm.root-path /home/user/nitro-legacy/machines & +for port in 52000 52001; do + while ! nc -w1 -z 127.0.0.10 $port; do + echo waiting for validation port $port + sleep 1 + done +done +echo launching nitro-node +/usr/local/bin/nitro --validation.wasm.allowed-wasm-module-roots /home/user/nitro-legacy/machines,/home/user/target/machines --node.block-validator.validation-server-configs-list='[{"jwtsecret":"/tmp/nitro-val.jwt","url":"http://127.0.0.10:52000"}, {"jwtsecret":"/tmp/nitro-val.jwt","url":"http://127.0.0.10:52001"}]' "$@" diff --git a/staker/block_validator.go b/staker/block_validator.go index 56cd5307d8..0fea05469f 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -8,14 +8,13 @@ import ( "encoding/json" "errors" "fmt" + "regexp" "runtime" "sync" "sync/atomic" "testing" "time" - flag "github.com/spf13/pflag" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -27,6 +26,8 @@ import ( "github.com/offchainlabs/nitro/util/rpcclient" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/client/redis" + "github.com/spf13/pflag" ) var ( @@ -74,6 +75,13 @@ type BlockValidator struct { sendRecordChan chan struct{} progressValidationsChan chan struct{} + chosenValidator map[common.Hash]validator.ValidationSpawner + + // wasmModuleRoot + moduleMutex sync.Mutex + currentWasmModuleRoot common.Hash + pendingWasmModuleRoot common.Hash + // for testing only testingProgressMadeChan chan struct{} @@ -84,8 +92,9 @@ type BlockValidator struct { type BlockValidatorConfig struct { Enable bool `koanf:"enable"` + RedisValidationClientConfig redis.ValidationClientConfig `koanf:"redis-validation-client-config"` ValidationServer rpcclient.ClientConfig `koanf:"validation-server" reload:"hot"` - ValidationServerConfigs []rpcclient.ClientConfig `koanf:"validation-server-configs" reload:"hot"` + ValidationServerConfigs []rpcclient.ClientConfig `koanf:"validation-server-configs"` ValidationPoll time.Duration `koanf:"validation-poll" reload:"hot"` PrerecordedBlocks uint64 `koanf:"prerecorded-blocks" reload:"hot"` ForwardBlocks uint64 `koanf:"forward-blocks" reload:"hot"` @@ -94,7 +103,7 @@ type BlockValidatorConfig struct { FailureIsFatal bool `koanf:"failure-is-fatal" reload:"hot"` Dangerous BlockValidatorDangerousConfig `koanf:"dangerous"` MemoryFreeLimit string `koanf:"memory-free-limit" reload:"hot"` - ValidationServerConfigsList string `koanf:"validation-server-configs-list" reload:"hot"` + ValidationServerConfigsList string `koanf:"validation-server-configs-list"` memoryFreeLimit int } @@ -109,23 +118,20 @@ func (c *BlockValidatorConfig) Validate() error { } c.memoryFreeLimit = limit } - if c.ValidationServerConfigs == nil { - if c.ValidationServerConfigsList == "default" { - c.ValidationServerConfigs = []rpcclient.ClientConfig{c.ValidationServer} - } else { - var validationServersConfigs []rpcclient.ClientConfig - if err := json.Unmarshal([]byte(c.ValidationServerConfigsList), &validationServersConfigs); err != nil { + streamsEnabled := c.RedisValidationClientConfig.Enabled() + if len(c.ValidationServerConfigs) == 0 { + c.ValidationServerConfigs = []rpcclient.ClientConfig{c.ValidationServer} + if c.ValidationServerConfigsList != "default" { + var executionServersConfigs []rpcclient.ClientConfig + if err := json.Unmarshal([]byte(c.ValidationServerConfigsList), &executionServersConfigs); err != nil && !streamsEnabled { return fmt.Errorf("failed to parse block-validator validation-server-configs-list string: %w", err) } - c.ValidationServerConfigs = validationServersConfigs + c.ValidationServerConfigs = executionServersConfigs } } - if len(c.ValidationServerConfigs) == 0 { - return fmt.Errorf("block-validator validation-server-configs is empty, need at least one validation server config") - } - for _, serverConfig := range c.ValidationServerConfigs { - if err := serverConfig.Validate(); err != nil { - return fmt.Errorf("failed to validate one of the block-validator validation-server-configs. url: %s, err: %w", serverConfig.URL, err) + for i := range c.ValidationServerConfigs { + if err := c.ValidationServerConfigs[i].Validate(); err != nil { + return fmt.Errorf("failed to validate one of the block-validator validation-server-configs. url: %s, err: %w", c.ValidationServerConfigs[i].URL, err) } } return nil @@ -137,10 +143,11 @@ type BlockValidatorDangerousConfig struct { type BlockValidatorConfigFetcher func() *BlockValidatorConfig -func BlockValidatorConfigAddOptions(prefix string, f *flag.FlagSet) { +func BlockValidatorConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".enable", DefaultBlockValidatorConfig.Enable, "enable block-by-block validation") rpcclient.RPCClientAddOptions(prefix+".validation-server", f, &DefaultBlockValidatorConfig.ValidationServer) - f.String(prefix+".validation-server-configs-list", DefaultBlockValidatorConfig.ValidationServerConfigsList, "array of validation rpc configs given as a json string. time duration should be supplied in number indicating nanoseconds") + redis.ValidationClientConfigAddOptions(prefix+".redis-validation-client-config", f) + f.String(prefix+".validation-server-configs-list", DefaultBlockValidatorConfig.ValidationServerConfigsList, "array of execution rpc configs given as a json string. time duration should be supplied in number indicating nanoseconds") f.Duration(prefix+".validation-poll", DefaultBlockValidatorConfig.ValidationPoll, "poll time to check validations") f.Uint64(prefix+".forward-blocks", DefaultBlockValidatorConfig.ForwardBlocks, "prepare entries for up to that many blocks ahead of validation (small footprint)") f.Uint64(prefix+".prerecorded-blocks", DefaultBlockValidatorConfig.PrerecordedBlocks, "record that many blocks ahead of validation (larger footprint)") @@ -151,7 +158,7 @@ func BlockValidatorConfigAddOptions(prefix string, f *flag.FlagSet) { f.String(prefix+".memory-free-limit", DefaultBlockValidatorConfig.MemoryFreeLimit, "minimum free-memory limit after reaching which the blockvalidator pauses validation. Enabled by default as 1GB, to disable provide empty string") } -func BlockValidatorDangerousConfigAddOptions(prefix string, f *flag.FlagSet) { +func BlockValidatorDangerousConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".reset-block-validation", DefaultBlockValidatorDangerousConfig.ResetBlockValidation, "resets block-by-block validation, starting again at genesis") } @@ -159,6 +166,7 @@ var DefaultBlockValidatorConfig = BlockValidatorConfig{ Enable: false, ValidationServerConfigsList: "default", ValidationServer: rpcclient.DefaultClientConfig, + RedisValidationClientConfig: redis.DefaultValidationClientConfig, ValidationPoll: time.Second, ForwardBlocks: 1024, PrerecordedBlocks: uint64(2 * runtime.NumCPU()), @@ -170,17 +178,18 @@ var DefaultBlockValidatorConfig = BlockValidatorConfig{ } var TestBlockValidatorConfig = BlockValidatorConfig{ - Enable: false, - ValidationServer: rpcclient.TestClientConfig, - ValidationServerConfigs: []rpcclient.ClientConfig{rpcclient.TestClientConfig}, - ValidationPoll: 100 * time.Millisecond, - ForwardBlocks: 128, - PrerecordedBlocks: uint64(2 * runtime.NumCPU()), - CurrentModuleRoot: "latest", - PendingUpgradeModuleRoot: "latest", - FailureIsFatal: true, - Dangerous: DefaultBlockValidatorDangerousConfig, - MemoryFreeLimit: "default", + Enable: false, + ValidationServer: rpcclient.TestClientConfig, + ValidationServerConfigs: []rpcclient.ClientConfig{rpcclient.TestClientConfig}, + RedisValidationClientConfig: redis.TestValidationClientConfig, + ValidationPoll: 100 * time.Millisecond, + ForwardBlocks: 128, + PrerecordedBlocks: uint64(2 * runtime.NumCPU()), + CurrentModuleRoot: "latest", + PendingUpgradeModuleRoot: "latest", + FailureIsFatal: true, + Dangerous: DefaultBlockValidatorDangerousConfig, + MemoryFreeLimit: "default", } var DefaultBlockValidatorDangerousConfig = BlockValidatorDangerousConfig{ @@ -321,6 +330,17 @@ func nonBlockingTrigger(channel chan struct{}) { } } +func (v *BlockValidator) GetModuleRootsToValidate() []common.Hash { + v.moduleMutex.Lock() + defer v.moduleMutex.Unlock() + + validatingModuleRoots := []common.Hash{v.currentWasmModuleRoot} + if v.currentWasmModuleRoot != v.pendingWasmModuleRoot && v.pendingWasmModuleRoot != (common.Hash{}) { + validatingModuleRoots = append(validatingModuleRoots, v.pendingWasmModuleRoot) + } + return validatingModuleRoots +} + // called from NewBlockValidator, doesn't need to catch locks func ReadLastValidatedInfo(db ethdb.Database) (*GlobalStateValidatedInfo, error) { exists, err := db.Has(lastGlobalStateValidatedInfoKey) @@ -449,8 +469,13 @@ func (v *BlockValidator) writeToFile(validationEntry *validationEntry, moduleRoo if err != nil { return err } - _, err = v.execSpawner.WriteToFile(input, validationEntry.End, moduleRoot).Await(v.GetContext()) - return err + for _, spawner := range v.execSpawners { + if validator.SpawnerSupportsModule(spawner, moduleRoot) { + _, err = spawner.WriteToFile(input, validationEntry.End, moduleRoot).Await(v.GetContext()) + return err + } + } + return errors.New("did not find exec spawner for wasmModuleRoot") } func (v *BlockValidator) SetCurrentWasmModuleRoot(hash common.Hash) error { @@ -549,7 +574,10 @@ func (v *BlockValidator) createNextValidationEntry(ctx context.Context) (bool, e } else { return false, fmt.Errorf("illegal batch msg count %d pos %d batch %d", v.nextCreateBatchMsgCount, pos, endGS.Batch) } - entry, err := newValidationEntry(pos, v.nextCreateStartGS, endGS, msg, v.nextCreateBatch, v.nextCreateBatchBlockHash, v.nextCreatePrevDelayed) + chainConfig := v.streamer.ChainConfig() + entry, err := newValidationEntry( + pos, v.nextCreateStartGS, endGS, msg, v.nextCreateBatch, v.nextCreateBatchBlockHash, v.nextCreatePrevDelayed, chainConfig, + ) if err != nil { return false, err } @@ -693,14 +721,6 @@ func (v *BlockValidator) advanceValidations(ctx context.Context) (*arbutil.Messa defer v.reorgMutex.RUnlock() wasmRoots := v.GetModuleRootsToValidate() - rooms := make([]int, len(v.validationSpawners)) - currentSpawnerIndex := 0 - for i, spawner := range v.validationSpawners { - here := spawner.Room() / len(wasmRoots) - if here > 0 { - rooms[i] = here - } - } pos := v.validated() - 1 // to reverse the first +1 in the loop validationsLoop: for { @@ -769,15 +789,16 @@ validationsLoop: log.Trace("result validated", "count", v.validated(), "blockHash", v.lastValidGS.BlockHash) continue } - for currentSpawnerIndex < len(rooms) { - if rooms[currentSpawnerIndex] > 0 { - break + for _, moduleRoot := range wasmRoots { + if v.chosenValidator[moduleRoot] == nil { + notFoundErr := fmt.Errorf("did not find spawner for moduleRoot :%v", moduleRoot) + v.possiblyFatal(notFoundErr) + return nil, notFoundErr + } + if v.chosenValidator[moduleRoot].Room() == 0 { + log.Trace("advanceValidations: no more room", "moduleRoot", moduleRoot) + return nil, nil } - currentSpawnerIndex++ - } - if currentSpawnerIndex == len(rooms) { - log.Trace("advanceValidations: no more room", "pos", pos) - return nil, nil } if v.isMemoryLimitExceeded() { log.Warn("advanceValidations: aborting due to running low on memory") @@ -797,8 +818,8 @@ validationsLoop: defer validatorPendingValidationsGauge.Dec(1) var runs []validator.ValidationRun for _, moduleRoot := range wasmRoots { - run := v.validationSpawners[currentSpawnerIndex].Launch(input, moduleRoot) - log.Trace("advanceValidations: launched", "pos", validationStatus.Entry.Pos, "moduleRoot", moduleRoot, "spawner", currentSpawnerIndex) + run := v.chosenValidator[moduleRoot].Launch(input, moduleRoot) + log.Trace("advanceValidations: launched", "pos", validationStatus.Entry.Pos, "moduleRoot", moduleRoot) runs = append(runs, run) } validationCtx, cancel := context.WithCancel(ctx) @@ -821,10 +842,6 @@ validationsLoop: } nonBlockingTrigger(v.progressValidationsChan) }) - rooms[currentSpawnerIndex]-- - if rooms[currentSpawnerIndex] == 0 { - currentSpawnerIndex++ - } } } } @@ -1030,10 +1047,11 @@ func (v *BlockValidator) Reorg(ctx context.Context, count arbutil.MessageIndex) // Initialize must be called after SetCurrentWasmModuleRoot sets the current one func (v *BlockValidator) Initialize(ctx context.Context) error { config := v.config() + currentModuleRoot := config.CurrentModuleRoot switch currentModuleRoot { case "latest": - latest, err := v.execSpawner.LatestWasmModuleRoot().Await(ctx) + latest, err := v.GetLatestWasmModuleRoot(ctx) if err != nil { return err } @@ -1048,7 +1066,52 @@ func (v *BlockValidator) Initialize(ctx context.Context) error { return errors.New("current-module-root config value illegal") } } + pendingModuleRoot := config.PendingUpgradeModuleRoot + if pendingModuleRoot != "" { + if pendingModuleRoot == "latest" { + latest, err := v.GetLatestWasmModuleRoot(ctx) + if err != nil { + return err + } + v.pendingWasmModuleRoot = latest + } else { + valid, _ := regexp.MatchString("(0x)?[0-9a-fA-F]{64}", pendingModuleRoot) + v.pendingWasmModuleRoot = common.HexToHash(pendingModuleRoot) + if (!valid || v.pendingWasmModuleRoot == common.Hash{}) { + return errors.New("pending-upgrade-module-root config value illegal") + } + } + } log.Info("BlockValidator initialized", "current", v.currentWasmModuleRoot, "pending", v.pendingWasmModuleRoot) + moduleRoots := []common.Hash{v.currentWasmModuleRoot} + if v.pendingWasmModuleRoot != v.currentWasmModuleRoot && v.pendingWasmModuleRoot != (common.Hash{}) { + moduleRoots = append(moduleRoots, v.pendingWasmModuleRoot) + } + // First spawner is always RedisValidationClient if RedisStreams are enabled. + if v.redisValidator != nil { + err := v.redisValidator.Initialize(ctx, moduleRoots) + if err != nil { + return err + } + } + v.chosenValidator = make(map[common.Hash]validator.ValidationSpawner) + for _, root := range moduleRoots { + if v.redisValidator != nil && validator.SpawnerSupportsModule(v.redisValidator, root) { + v.chosenValidator[root] = v.redisValidator + log.Info("validator chosen", "WasmModuleRoot", root, "chosen", "redis") + } else { + for _, spawner := range v.execSpawners { + if validator.SpawnerSupportsModule(spawner, root) { + v.chosenValidator[root] = spawner + log.Info("validator chosen", "WasmModuleRoot", root, "chosen", spawner.Name()) + break + } + } + if v.chosenValidator[root] == nil { + return fmt.Errorf("cannot validate WasmModuleRoot %v", root) + } + } + } return nil } diff --git a/staker/challenge_manager.go b/staker/challenge_manager.go index ac2ae8835a..22897e3c1d 100644 --- a/staker/challenge_manager.go +++ b/staker/challenge_manager.go @@ -478,9 +478,18 @@ func (m *ChallengeManager) createExecutionBackend(ctx context.Context, step uint } } input.BatchInfo = prunedBatches - execRun, err := m.validator.execSpawner.CreateExecutionRun(m.wasmModuleRoot, input).Await(ctx) - if err != nil { - return fmt.Errorf("error creating execution backend for msg %v: %w", initialCount, err) + var execRun validator.ExecutionRun + for _, spawner := range m.validator.execSpawners { + if validator.SpawnerSupportsModule(spawner, m.wasmModuleRoot) { + execRun, err = spawner.CreateExecutionRun(m.wasmModuleRoot, input).Await(ctx) + if err != nil { + return fmt.Errorf("error creating execution backend for msg %v: %w", initialCount, err) + } + break + } + } + if execRun == nil { + return fmt.Errorf("did not find valid execution backend") } backend, err := NewExecutionChallengeBackend(execRun) if err != nil { diff --git a/staker/challenge_test.go b/staker/challenge_test.go index c21ebcdecd..4534b04a25 100644 --- a/staker/challenge_test.go +++ b/staker/challenge_test.go @@ -5,6 +5,7 @@ package staker import ( "context" + "io" "math/big" "os" "path" @@ -116,9 +117,10 @@ func runChallengeTest( testTimeout bool, maxInboxMessage uint64, ) { - glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) - glogger.Verbosity(log.LvlDebug) - log.Root().SetHandler(glogger) + glogger := log.NewGlogHandler( + log.NewTerminalHandler(io.Writer(os.Stderr), false)) + glogger.Verbosity(log.LevelDebug) + log.SetDefault(log.NewLogger(glogger)) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -191,6 +193,7 @@ func runChallengeTest( for i := 0; i < 100; i++ { if testTimeout { + backend.Commit() err = backend.AdjustTime(time.Second * 40) } Require(t, err) @@ -247,7 +250,7 @@ func createBaseMachine(t *testing.T, wasmname string, wasmModules []string) *ser modulePaths = append(modulePaths, path.Join(wasmDir, moduleName)) } - machine, err := server_arb.LoadSimpleMachine(wasmPath, modulePaths) + machine, err := server_arb.LoadSimpleMachine(wasmPath, modulePaths, true) Require(t, err) return machine diff --git a/staker/l1_validator.go b/staker/l1_validator.go index 4e7aa22cbe..d68365ede0 100644 --- a/staker/l1_validator.go +++ b/staker/l1_validator.go @@ -10,9 +10,9 @@ import ( "math/big" "time" - "github.com/offchainlabs/nitro/arbstate" "github.com/offchainlabs/nitro/staker/txbuilder" "github.com/offchainlabs/nitro/util/arbmath" + "github.com/offchainlabs/nitro/util/headerreader" "github.com/offchainlabs/nitro/validator" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -50,7 +50,6 @@ type L1Validator struct { wallet ValidatorWalletInterface callOpts bind.CallOpts - das arbstate.DataAvailabilityReader inboxTracker InboxTrackerInterface txStreamer TransactionStreamerInterface blockValidator *BlockValidator @@ -62,7 +61,6 @@ func NewL1Validator( wallet ValidatorWalletInterface, validatorUtilsAddress common.Address, callOpts bind.CallOpts, - das arbstate.DataAvailabilityReader, inboxTracker InboxTrackerInterface, txStreamer TransactionStreamerInterface, blockValidator *BlockValidator, @@ -90,7 +88,6 @@ func NewL1Validator( builder: builder, wallet: wallet, callOpts: callOpts, - das: das, inboxTracker: inboxTracker, txStreamer: txStreamer, blockValidator: blockValidator, @@ -191,12 +188,16 @@ func (v *L1Validator) resolveNextNode(ctx context.Context, info *StakerInfo, lat func (v *L1Validator) isRequiredStakeElevated(ctx context.Context) (bool, error) { callOpts := v.getCallOpts(ctx) - requiredStake, err := v.rollup.CurrentRequiredStake(callOpts) + baseStake, err := v.rollup.BaseStake(callOpts) if err != nil { return false, err } - baseStake, err := v.rollup.BaseStake(callOpts) + requiredStake, err := v.rollup.CurrentRequiredStake(callOpts) if err != nil { + if headerreader.ExecutionRevertedRegexp.MatchString(err.Error()) { + log.Warn("execution reverted checking if required state is elevated; assuming elevated", "err", err) + return true, nil + } return false, err } return requiredStake.Cmp(baseStake) > 0, nil @@ -339,10 +340,14 @@ func (v *L1Validator) generateNodeAction( batchNum = localBatchCount - 1 validatedCount = messageCount } else { - batchNum, err = FindBatchContainingMessageIndex(v.inboxTracker, validatedCount-1, localBatchCount) + var found bool + batchNum, found, err = v.inboxTracker.FindInboxBatchContainingMessage(validatedCount - 1) if err != nil { return nil, false, err } + if !found { + return nil, false, errors.New("batch not found on L1") + } } execResult, err := v.txStreamer.ResultAtCount(validatedCount) if err != nil { diff --git a/staker/staker.go b/staker/staker.go index 2a95e9c9f7..da6413e122 100644 --- a/staker/staker.go +++ b/staker/staker.go @@ -291,7 +291,7 @@ func NewStaker( } client := l1Reader.Client() val, err := NewL1Validator(client, wallet, validatorUtilsAddress, callOpts, - statelessBlockValidator.daService, statelessBlockValidator.inboxTracker, statelessBlockValidator.streamer, blockValidator) + statelessBlockValidator.inboxTracker, statelessBlockValidator.streamer, blockValidator) if err != nil { return nil, err } diff --git a/staker/stateless_block_validator.go b/staker/stateless_block_validator.go index fcd1f247c2..1cf3d7a4c3 100644 --- a/staker/stateless_block_validator.go +++ b/staker/stateless_block_validator.go @@ -7,30 +7,31 @@ import ( "context" "errors" "fmt" - "regexp" - "sync" "testing" - "github.com/offchainlabs/nitro/execution" - "github.com/offchainlabs/nitro/util/rpcclient" - "github.com/offchainlabs/nitro/validator/server_api" - - "github.com/offchainlabs/nitro/arbutil" - "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbos/arbostypes" - "github.com/offchainlabs/nitro/arbstate" + "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/execution" + "github.com/offchainlabs/nitro/util/rpcclient" + "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/client/redis" + + validatorclient "github.com/offchainlabs/nitro/validator/client" ) type StatelessBlockValidator struct { config *BlockValidatorConfig - execSpawner validator.ExecutionSpawner - validationSpawners []validator.ValidationSpawner + execSpawners []validator.ExecutionSpawner + redisValidator *redis.ValidationClient recorder execution.ExecutionRecorder @@ -38,12 +39,7 @@ type StatelessBlockValidator struct { inboxTracker InboxTrackerInterface streamer TransactionStreamerInterface db ethdb.Database - daService arbstate.DataAvailabilityReader - blobReader arbstate.BlobReader - - moduleMutex sync.Mutex - currentWasmModuleRoot common.Hash - pendingWasmModuleRoot common.Hash + dapReaders []daprovider.Reader } type BlockValidatorRegistrer interface { @@ -56,6 +52,7 @@ type InboxTrackerInterface interface { GetBatchMessageCount(seqNum uint64) (arbutil.MessageIndex, error) GetBatchAcc(seqNum uint64) (common.Hash, error) GetBatchCount() (uint64, error) + FindInboxBatchContainingMessage(pos arbutil.MessageIndex) (uint64, bool, error) } type TransactionStreamerInterface interface { @@ -65,6 +62,7 @@ type TransactionStreamerInterface interface { ResultAtCount(count arbutil.MessageIndex) (*execution.MessageResult, error) PauseReorgs() ResumeReorgs() + ChainConfig() *params.ChainConfig } type InboxReaderInterface interface { @@ -108,39 +106,6 @@ func GlobalStatePositionsAtCount( return startPos, GlobalStatePosition{batch, posInBatch + 1}, nil } -func FindBatchContainingMessageIndex( - tracker InboxTrackerInterface, pos arbutil.MessageIndex, high uint64, -) (uint64, error) { - var low uint64 - // Iteration preconditions: - // - high >= low - // - msgCount(low - 1) <= pos implies low <= target - // - msgCount(high) > pos implies high >= target - // Therefore, if low == high, then low == high == target - for high > low { - // Due to integer rounding, mid >= low && mid < high - mid := (low + high) / 2 - count, err := tracker.GetBatchMessageCount(mid) - if err != nil { - return 0, err - } - if count < pos { - // Must narrow as mid >= low, therefore mid + 1 > low, therefore newLow > oldLow - // Keeps low precondition as msgCount(mid) < pos - low = mid + 1 - } else if count == pos { - return mid + 1, nil - } else if count == pos+1 || mid == low { // implied: count > pos - return mid, nil - } else { // implied: count > pos + 1 - // Must narrow as mid < high, therefore newHigh < lowHigh - // Keeps high precondition as msgCount(mid) > pos - high = mid - } - } - return low, nil -} - type ValidationEntryStage uint32 const ( @@ -157,12 +122,14 @@ type validationEntry struct { End validator.GoGlobalState HasDelayedMsg bool DelayedMsgNr uint64 + ChainConfig *params.ChainConfig // valid when created, removed after recording msg *arbostypes.MessageWithMetadata // Has batch when created - others could be added on record BatchInfo []validator.BatchInfo // Valid since Ready Preimages map[arbutil.PreimageType]map[common.Hash][]byte + UserWasms state.UserWasms DelayedMsg []byte } @@ -175,9 +142,11 @@ func (e *validationEntry) ToInput() (*validator.ValidationInput, error) { HasDelayedMsg: e.HasDelayedMsg, DelayedMsgNr: e.DelayedMsgNr, Preimages: e.Preimages, + UserWasms: e.UserWasms, BatchInfo: e.BatchInfo, DelayedMsg: e.DelayedMsg, StartState: e.Start, + DebugChain: e.ChainConfig.DebugMode(), }, nil } @@ -189,6 +158,7 @@ func newValidationEntry( batch []byte, batchBlockHash common.Hash, prevDelayed uint64, + chainConfig *params.ChainConfig, ) (*validationEntry, error) { batchInfo := validator.BatchInfo{ Number: start.Batch, @@ -212,6 +182,7 @@ func newValidationEntry( DelayedMsgNr: delayedNum, msg: msg, BatchInfo: []validator.BatchInfo{batchInfo}, + ChainConfig: chainConfig, }, nil } @@ -221,42 +192,42 @@ func NewStatelessBlockValidator( streamer TransactionStreamerInterface, recorder execution.ExecutionRecorder, arbdb ethdb.Database, - das arbstate.DataAvailabilityReader, - blobReader arbstate.BlobReader, + dapReaders []daprovider.Reader, config func() *BlockValidatorConfig, stack *node.Node, ) (*StatelessBlockValidator, error) { - validationSpawners := make([]validator.ValidationSpawner, len(config().ValidationServerConfigs)) - for i, serverConfig := range config().ValidationServerConfigs { - valConfFetcher := func() *rpcclient.ClientConfig { return &serverConfig } - validationSpawners[i] = server_api.NewValidationClient(valConfFetcher, stack) - } - valConfFetcher := func() *rpcclient.ClientConfig { return &config().ValidationServerConfigs[0] } - execClient := server_api.NewExecutionClient(valConfFetcher, stack) - validator := &StatelessBlockValidator{ - config: config(), - execSpawner: execClient, - recorder: recorder, - validationSpawners: validationSpawners, - inboxReader: inboxReader, - inboxTracker: inbox, - streamer: streamer, - db: arbdb, - daService: das, - blobReader: blobReader, - } - return validator, nil -} + var executionSpawners []validator.ExecutionSpawner + var redisValClient *redis.ValidationClient -func (v *StatelessBlockValidator) GetModuleRootsToValidate() []common.Hash { - v.moduleMutex.Lock() - defer v.moduleMutex.Unlock() + if config().RedisValidationClientConfig.Enabled() { + var err error + redisValClient, err = redis.NewValidationClient(&config().RedisValidationClientConfig) + if err != nil { + return nil, fmt.Errorf("creating new redis validation client: %w", err) + } + } + configs := config().ValidationServerConfigs + for i := range configs { + i := i + confFetcher := func() *rpcclient.ClientConfig { return &config().ValidationServerConfigs[i] } + executionSpawners = append(executionSpawners, validatorclient.NewExecutionClient(confFetcher, stack)) + } - validatingModuleRoots := []common.Hash{v.currentWasmModuleRoot} - if (v.currentWasmModuleRoot != v.pendingWasmModuleRoot && v.pendingWasmModuleRoot != common.Hash{}) { - validatingModuleRoots = append(validatingModuleRoots, v.pendingWasmModuleRoot) + if len(executionSpawners) == 0 { + return nil, errors.New("no enabled execution servers") } - return validatingModuleRoots + + return &StatelessBlockValidator{ + config: config(), + recorder: recorder, + redisValidator: redisValClient, + inboxReader: inboxReader, + inboxTracker: inbox, + streamer: streamer, + db: arbdb, + dapReaders: dapReaders, + execSpawners: executionSpawners, + }, nil } func (v *StatelessBlockValidator) ValidationEntryRecord(ctx context.Context, e *validationEntry) error { @@ -277,6 +248,7 @@ func (v *StatelessBlockValidator) ValidationEntryRecord(ctx context.Context, e * if recording.Preimages != nil { e.Preimages[arbutil.Keccak256PreimageType] = recording.Preimages } + e.UserWasms = recording.UserWasms } if e.HasDelayedMsg { delayedMsg, err := v.inboxTracker.GetDelayedMessageBytes(e.DelayedMsgNr) @@ -293,39 +265,27 @@ func (v *StatelessBlockValidator) ValidationEntryRecord(ctx context.Context, e * if len(batch.Data) <= 40 { continue } - if arbstate.IsBlobHashesHeaderByte(batch.Data[40]) { - payload := batch.Data[41:] - if len(payload)%len(common.Hash{}) != 0 { - return fmt.Errorf("blob batch data is not a list of hashes as expected") - } - versionedHashes := make([]common.Hash, len(payload)/len(common.Hash{})) - for i := 0; i*32 < len(payload); i += 1 { - copy(versionedHashes[i][:], payload[i*32:(i+1)*32]) - } - blobs, err := v.blobReader.GetBlobs(ctx, batch.BlockHash, versionedHashes) - if err != nil { - return fmt.Errorf("failed to get blobs: %w", err) - } - if e.Preimages[arbutil.EthVersionedHashPreimageType] == nil { - e.Preimages[arbutil.EthVersionedHashPreimageType] = make(map[common.Hash][]byte) - } - for i, blob := range blobs { - // Prevent aliasing `blob` when slicing it, as for range loops overwrite the same variable - // Won't be necessary after Go 1.22 with https://go.dev/blog/loopvar-preview - b := blob - e.Preimages[arbutil.EthVersionedHashPreimageType][versionedHashes[i]] = b[:] - } - } - if arbstate.IsDASMessageHeaderByte(batch.Data[40]) { - if v.daService == nil { - log.Warn("No DAS configured, but sequencer message found with DAS header") - } else { - _, err := arbstate.RecoverPayloadFromDasBatch( - ctx, batch.Number, batch.Data, v.daService, e.Preimages, arbstate.KeysetValidate, - ) + foundDA := false + for _, dapReader := range v.dapReaders { + if dapReader != nil && dapReader.IsValidHeaderByte(batch.Data[40]) { + preimageRecorder := daprovider.RecordPreimagesTo(e.Preimages) + _, err := dapReader.RecoverPayloadFromBatch(ctx, batch.Number, batch.BlockHash, batch.Data, preimageRecorder, true) if err != nil { - return err + // Matches the way keyset validation was done inside DAS readers i.e logging the error + // But other daproviders might just want to return the error + if errors.Is(err, daprovider.ErrSeqMsgValidation) && daprovider.IsDASMessageHeaderByte(batch.Data[40]) { + log.Error(err.Error()) + } else { + return err + } } + foundDA = true + break + } + } + if !foundDA { + if daprovider.IsDASMessageHeaderByte(batch.Data[40]) { + log.Error("No DAS Reader configured, but sequencer message found with DAS header") } } } @@ -352,13 +312,12 @@ func (v *StatelessBlockValidator) GlobalStatePositionsAtCount(count arbutil.Mess if count == 1 { return GlobalStatePosition{}, GlobalStatePosition{1, 0}, nil } - batchCount, err := v.inboxTracker.GetBatchCount() + batch, found, err := v.inboxTracker.FindInboxBatchContainingMessage(count - 1) if err != nil { return GlobalStatePosition{}, GlobalStatePosition{}, err } - batch, err := FindBatchContainingMessageIndex(v.inboxTracker, count-1, batchCount) - if err != nil { - return GlobalStatePosition{}, GlobalStatePosition{}, err + if !found { + return GlobalStatePosition{}, GlobalStatePosition{}, errors.New("batch not found on L1 yet") } return GlobalStatePositionsAtCount(v.inboxTracker, count, batch) } @@ -394,7 +353,7 @@ func (v *StatelessBlockValidator) CreateReadyValidationEntry(ctx context.Context if err != nil { return nil, err } - entry, err := newValidationEntry(pos, start, end, msg, seqMsg, batchBlockHash, prevDelayed) + entry, err := newValidationEntry(pos, start, end, msg, seqMsg, batchBlockHash, prevDelayed, v.streamer.ChainConfig()) if err != nil { return nil, err } @@ -417,30 +376,29 @@ func (v *StatelessBlockValidator) ValidateResult( if err != nil { return false, nil, err } - var spawners []validator.ValidationSpawner - if useExec { - spawners = append(spawners, v.execSpawner) - } else { - spawners = v.validationSpawners + var run validator.ValidationRun + if !useExec { + if v.redisValidator != nil { + if validator.SpawnerSupportsModule(v.redisValidator, moduleRoot) { + run = v.redisValidator.Launch(input, moduleRoot) + } + } } - if len(spawners) == 0 { - return false, &entry.End, errors.New("no validation defined") + if run == nil { + for _, spawner := range v.execSpawners { + if validator.SpawnerSupportsModule(spawner, moduleRoot) { + run = spawner.Launch(input, moduleRoot) + break + } + } } - var runs []validator.ValidationRun - for _, spawner := range spawners { - run := spawner.Launch(input, moduleRoot) - runs = append(runs, run) + if run == nil { + return false, nil, fmt.Errorf("validation with WasmModuleRoot %v not supported by node", moduleRoot) } - defer func() { - for _, run := range runs { - run.Cancel() - } - }() - for _, run := range runs { - gsEnd, err := run.Await(ctx) - if err != nil || gsEnd != entry.End { - return false, &gsEnd, err - } + defer run.Cancel() + gsEnd, err := run.Await(ctx) + if err != nil || gsEnd != entry.End { + return false, &gsEnd, err } return true, &entry.End, nil } @@ -449,37 +407,40 @@ func (v *StatelessBlockValidator) OverrideRecorder(t *testing.T, recorder execut v.recorder = recorder } +func (v *StatelessBlockValidator) GetLatestWasmModuleRoot(ctx context.Context) (common.Hash, error) { + var lastErr error + for _, spawner := range v.execSpawners { + var latest common.Hash + latest, lastErr = spawner.LatestWasmModuleRoot().Await(ctx) + if latest != (common.Hash{}) && lastErr == nil { + return latest, nil + } + if ctx.Err() != nil { + return common.Hash{}, ctx.Err() + } + } + return common.Hash{}, fmt.Errorf("couldn't detect latest WasmModuleRoot: %w", lastErr) +} + func (v *StatelessBlockValidator) Start(ctx_in context.Context) error { - err := v.execSpawner.Start(ctx_in) - if err != nil { - return err + if v.redisValidator != nil { + if err := v.redisValidator.Start(ctx_in); err != nil { + return fmt.Errorf("starting execution spawner: %w", err) + } } - for _, spawner := range v.validationSpawners { + for _, spawner := range v.execSpawners { if err := spawner.Start(ctx_in); err != nil { return err } } - if v.config.PendingUpgradeModuleRoot != "" { - if v.config.PendingUpgradeModuleRoot == "latest" { - latest, err := v.execSpawner.LatestWasmModuleRoot().Await(ctx_in) - if err != nil { - return err - } - v.pendingWasmModuleRoot = latest - } else { - valid, _ := regexp.MatchString("(0x)?[0-9a-fA-F]{64}", v.config.PendingUpgradeModuleRoot) - v.pendingWasmModuleRoot = common.HexToHash(v.config.PendingUpgradeModuleRoot) - if (!valid || v.pendingWasmModuleRoot == common.Hash{}) { - return errors.New("pending-upgrade-module-root config value illegal") - } - } - } return nil } func (v *StatelessBlockValidator) Stop() { - v.execSpawner.Stop() - for _, spawner := range v.validationSpawners { + for _, spawner := range v.execSpawners { spawner.Stop() } + if v.redisValidator != nil { + v.redisValidator.Stop() + } } diff --git a/util/arbmath/bips.go b/util/arbmath/bips.go index 83c7a61ec2..8b7c47d82b 100644 --- a/util/arbmath/bips.go +++ b/util/arbmath/bips.go @@ -1,4 +1,4 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package arbmath @@ -6,8 +6,10 @@ package arbmath import "math/big" type Bips int64 +type UBips uint64 const OneInBips Bips = 10000 +const OneInUBips UBips = 10000 func NaturalToBips(natural int64) Bips { return Bips(SaturatingMul(natural, int64(OneInBips))) @@ -34,7 +36,15 @@ func UintMulByBips(value uint64, bips Bips) uint64 { } func SaturatingCastToBips(value uint64) Bips { - return Bips(SaturatingCast(value)) + return Bips(SaturatingCast[int64](value)) +} + +func (bips UBips) Uint64() uint64 { + return uint64(bips) +} + +func (bips Bips) Uint64() uint64 { + return uint64(bips) } // BigDivToBips returns dividend/divisor as bips, saturating if out of bounds diff --git a/util/arbmath/bits.go b/util/arbmath/bits.go index 89ce89e08a..1b91e2755a 100644 --- a/util/arbmath/bits.go +++ b/util/arbmath/bits.go @@ -7,6 +7,7 @@ import ( "encoding/binary" "github.com/ethereum/go-ethereum/common" + "github.com/holiman/uint256" ) type bytes32 = common.Hash @@ -44,3 +45,96 @@ func Uint32ToBytes(value uint32) []byte { binary.BigEndian.PutUint32(result, value) return result } + +// Uint16ToBytes casts a uint16 to its big-endian representation +func Uint16ToBytes(value uint16) []byte { + result := make([]byte, 2) + binary.BigEndian.PutUint16(result, value) + return result +} + +// casts a uint8 to its big-endian representation +func Uint8ToBytes(value uint8) []byte { + return []byte{value} +} + +// casts a bool to its big-endian representation +func BoolToBytes(value bool) []byte { + if value { + return Uint8ToBytes(1) + } + return Uint8ToBytes(0) +} + +// BytesToUint creates a uint64 from its big-endian representation +func BytesToUint(value []byte) uint64 { + return binary.BigEndian.Uint64(value) +} + +// BytesToUint32 creates a uint32 from its big-endian representation +func BytesToUint32(value []byte) uint32 { + return binary.BigEndian.Uint32(value) +} + +// BytesToUint16 creates a uint16 from its big-endian representation +func BytesToUint16(value []byte) uint16 { + return binary.BigEndian.Uint16(value) +} + +// creates a uint8 from its big-endian representation +func BytesToUint8(value []byte) uint8 { + return value[0] +} + +// creates a uint256 from its big-endian representation +func BytesToUint256(value []byte) *uint256.Int { + int := &uint256.Int{} + int.SetBytes(value) + return int +} + +// creates a bool from its big-endian representation +func BytesToBool(value []byte) bool { + return value[0] != 0 +} + +// BoolToUint8 assigns a nonzero value when true +func BoolToUint8(value bool) uint8 { + if value { + return 1 + } + return 0 +} + +// BoolToUint32 assigns a nonzero value when true +func BoolToUint32(value bool) uint32 { + if value { + return 1 + } + return 0 +} + +// BoolToUint32 assigns a nonzero value when true +func UintToBool[T Unsigned](value T) bool { + return value != 0 +} + +// Ensures a slice is non-nil +func NonNilSlice[T any](slice []T) []T { + if slice == nil { + return []T{} + } + return slice +} + +// Equivalent to slice[start:offset], but truncates when out of bounds rather than panicking. +func SliceWithRunoff[S any, I Integer](slice []S, start, end I) []S { + len := I(len(slice)) + start = MaxInt(start, 0) + end = MaxInt(start, end) + + if slice == nil || start >= len { + return []S{} + } + return slice[start:MinInt(end, len)] +} diff --git a/util/arbmath/math.go b/util/arbmath/math.go index eaac79bfad..7413955409 100644 --- a/util/arbmath/math.go +++ b/util/arbmath/math.go @@ -1,5 +1,5 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE package arbmath @@ -7,6 +7,7 @@ import ( "math" "math/big" "math/bits" + "unsafe" eth_math "github.com/ethereum/go-ethereum/common/math" "github.com/ethereum/go-ethereum/params" @@ -61,12 +62,28 @@ func MinInt[T Number](value, ceiling T) T { return value } -// MaxInt the maximum of two ints -func MaxInt[T Number](value, floor T) T { - if value < floor { - return floor +// MaxInt the maximum of one or more ints +func MaxInt[T Number](values ...T) T { + max := values[0] + for i := 1; i < len(values); i++ { + value := values[i] + if value > max { + max = value + } } - return value + return max +} + +// Checks if two ints are sufficiently close to one another +func Within[T Unsigned](a, b, bound T) bool { + min := MinInt(a, b) + max := MaxInt(a, b) + return max-min <= bound +} + +// Checks if an int belongs to [a, b] +func WithinRange[T Unsigned](value, a, b T) bool { + return a <= value && value <= b } // UintToBig casts an int to a huge @@ -133,6 +150,11 @@ func BigGreaterThan(first, second *big.Int) bool { return first.Cmp(second) > 0 } +// BigGreaterThanOrEqual check if a huge is greater than or equal to another +func BigGreaterThanOrEqual(first, second *big.Int) bool { + return first.Cmp(second) >= 0 +} + // BigMin returns a clone of the minimum of two big integers func BigMin(first, second *big.Int) *big.Int { if BigLessThan(first, second) { @@ -237,76 +259,104 @@ func BigFloatMulByUint(multiplicand *big.Float, multiplier uint64) *big.Float { return new(big.Float).Mul(multiplicand, UintToBigFloat(multiplier)) } -// SaturatingAdd add two int64's without overflow -func SaturatingAdd(augend, addend int64) int64 { - sum := augend + addend - if addend > 0 && sum < augend { - sum = math.MaxInt64 +func MaxSignedValue[T Signed]() T { + return T((uint64(1) << (8*unsafe.Sizeof(T(0)) - 1)) - 1) +} + +func MinSignedValue[T Signed]() T { + return T(uint64(1) << ((8 * unsafe.Sizeof(T(0))) - 1)) +} + +// SaturatingAdd add two integers without overflow +func SaturatingAdd[T Signed](a, b T) T { + sum := a + b + if b > 0 && sum < a { + sum = MaxSignedValue[T]() } - if addend < 0 && sum > augend { - sum = math.MinInt64 + if b < 0 && sum > a { + sum = MinSignedValue[T]() } return sum } -// SaturatingUAdd add two uint64's without overflow -func SaturatingUAdd(augend uint64, addend uint64) uint64 { - sum := augend + addend - if sum < augend || sum < addend { - sum = math.MaxUint64 +// SaturatingUAdd add two integers without overflow +func SaturatingUAdd[T Unsigned](a, b T) T { + sum := a + b + if sum < a || sum < b { + sum = ^T(0) } return sum } // SaturatingSub subtract an int64 from another without overflow func SaturatingSub(minuend, subtrahend int64) int64 { - return SaturatingAdd(minuend, -subtrahend) + if subtrahend == math.MinInt64 { + // The absolute value of MinInt64 is one greater than MaxInt64 + return SaturatingAdd(SaturatingAdd(minuend, math.MaxInt64), 1) + } + return SaturatingAdd(minuend, SaturatingNeg(subtrahend)) } -// SaturatingUSub subtract a uint64 from another without underflow -func SaturatingUSub(minuend uint64, subtrahend uint64) uint64 { - if subtrahend >= minuend { +// SaturatingUSub subtract an integer from another without underflow +func SaturatingUSub[T Unsigned](a, b T) T { + if b >= a { return 0 } - return minuend - subtrahend + return a - b } -// SaturatingUMul multiply two uint64's without overflow -func SaturatingUMul(multiplicand uint64, multiplier uint64) uint64 { - product := multiplicand * multiplier - if multiplier != 0 && product/multiplier != multiplicand { - product = math.MaxUint64 +// SaturatingUMul multiply two integers without over/underflow +func SaturatingUMul[T Unsigned](a, b T) T { + product := a * b + if b != 0 && product/b != a { + product = ^T(0) } return product } -// SaturatingMul multiply two int64's without over/underflow -func SaturatingMul(multiplicand int64, multiplier int64) int64 { - product := multiplicand * multiplier - if multiplier != 0 && product/multiplier != multiplicand { - if (multiplicand > 0 && multiplier > 0) || (multiplicand < 0 && multiplier < 0) { - product = math.MaxInt64 +// SaturatingMul multiply two integers without over/underflow +func SaturatingMul[T Signed](a, b T) T { + product := a * b + if b != 0 && product/b != a { + if (a > 0 && b > 0) || (a < 0 && b < 0) { + product = MaxSignedValue[T]() } else { - product = math.MinInt64 + product = MinSignedValue[T]() } } return product } -// SaturatingCast cast a uint64 to an int64, clipping to [0, 2^63-1] -func SaturatingCast(value uint64) int64 { - if value > math.MaxInt64 { - return math.MaxInt64 +// SaturatingCast cast an unsigned integer to a signed one, clipping to [0, S::MAX] +func SaturatingCast[S Signed, T Unsigned](value T) S { + tBig := unsafe.Sizeof(T(0)) >= unsafe.Sizeof(S(0)) + bits := uint64(8 * unsafe.Sizeof(S(0))) + sMax := T(1<> 1 + if tBig && value > sMax { + return S(sMax) } - return int64(value) + return S(value) } -// SaturatingUCast cast an int64 to a uint64, clipping to [0, 2^63-1] -func SaturatingUCast(value int64) uint64 { - if value < 0 { +// SaturatingUCast cast a signed integer to an unsigned one, clipping to [0, T::MAX] +func SaturatingUCast[T Unsigned, S Signed](value S) T { + if value <= 0 { return 0 } - return uint64(value) + tSmall := unsafe.Sizeof(T(0)) < unsafe.Sizeof(S(0)) + if tSmall && value >= S(^T(0)) { + return ^T(0) + } + return T(value) +} + +// SaturatingUUCast cast an unsigned integer to another, clipping to [0, U::MAX] +func SaturatingUUCast[U, T Unsigned](value T) U { + tBig := unsafe.Sizeof(T(0)) > unsafe.Sizeof(U(0)) + if tBig && value > T(^U(0)) { + return ^U(0) + } + return U(value) } func SaturatingCastToUint(value *big.Int) uint64 { @@ -319,25 +369,42 @@ func SaturatingCastToUint(value *big.Int) uint64 { return value.Uint64() } +// Negates an int without underflow +func SaturatingNeg[T Signed](value T) T { + if value < 0 && value == MinSignedValue[T]() { + return MaxSignedValue[T]() + } + return -value +} + +// Integer division but rounding up +func DivCeil[T Unsigned](value, divisor T) T { + if value%divisor == 0 { + return value / divisor + } + return value/divisor + 1 +} + // ApproxExpBasisPoints return the Maclaurin series approximation of e^x, where x is denominated in basis points. -// This quartic polynomial will underestimate e^x by about 5% as x approaches 20000 bips. -func ApproxExpBasisPoints(value Bips) Bips { +// The quartic polynomial will underestimate e^x by about 5% as x approaches 20000 bips. +func ApproxExpBasisPoints(value Bips, degree uint64) Bips { input := value negative := value < 0 if negative { input = -value } x := uint64(input) - bips := uint64(OneInBips) - res := bips + x/4 - res = bips + SaturatingUMul(res, x)/(3*bips) - res = bips + SaturatingUMul(res, x)/(2*bips) - res = bips + SaturatingUMul(res, x)/(1*bips) + + res := bips + x/degree + for i := uint64(1); i < degree; i++ { + res = bips + SaturatingUMul(res, x)/((degree-i)*bips) + } + if negative { - return Bips(SaturatingCast(bips * bips / res)) + return Bips(SaturatingCast[int64](bips * bips / res)) } else { - return Bips(SaturatingCast(res)) + return Bips(SaturatingCast[int64](res)) } } diff --git a/util/arbmath/math_fuzz_test.go b/util/arbmath/math_fuzz_test.go new file mode 100644 index 0000000000..591d699de0 --- /dev/null +++ b/util/arbmath/math_fuzz_test.go @@ -0,0 +1,112 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package arbmath + +import ( + "math/big" + "testing" +) + +func toBig[T Signed](a T) *big.Int { + return big.NewInt(int64(a)) +} + +func saturatingBigToInt[T Signed](a *big.Int) T { + // MinSignedValue and MaxSignedValue are already separately tested + if a.Cmp(toBig(MaxSignedValue[T]())) > 0 { + return MaxSignedValue[T]() + } + if a.Cmp(toBig(MinSignedValue[T]())) < 0 { + return MinSignedValue[T]() + } + return T(a.Int64()) +} + +func fuzzSaturatingAdd[T Signed](f *testing.F) { + f.Fuzz(func(t *testing.T, a, b T) { + got := SaturatingAdd(a, b) + expected := saturatingBigToInt[T](new(big.Int).Add(toBig(a), toBig(b))) + if got != expected { + t.Errorf("SaturatingAdd(%v, %v) = %v, expected %v", a, b, got, expected) + } + }) +} + +func fuzzSaturatingMul[T Signed](f *testing.F) { + f.Fuzz(func(t *testing.T, a, b T) { + got := SaturatingMul(a, b) + expected := saturatingBigToInt[T](new(big.Int).Mul(toBig(a), toBig(b))) + if got != expected { + t.Errorf("SaturatingMul(%v, %v) = %v, expected %v", a, b, got, expected) + } + }) +} + +func fuzzSaturatingNeg[T Signed](f *testing.F) { + f.Fuzz(func(t *testing.T, a T) { + got := SaturatingNeg(a) + expected := saturatingBigToInt[T](new(big.Int).Neg(toBig(a))) + if got != expected { + t.Errorf("SaturatingNeg(%v) = %v, expected %v", a, got, expected) + } + }) +} + +func FuzzSaturatingAddInt8(f *testing.F) { + fuzzSaturatingAdd[int8](f) +} + +func FuzzSaturatingAddInt16(f *testing.F) { + fuzzSaturatingAdd[int16](f) +} + +func FuzzSaturatingAddInt32(f *testing.F) { + fuzzSaturatingAdd[int32](f) +} + +func FuzzSaturatingAddInt64(f *testing.F) { + fuzzSaturatingAdd[int64](f) +} + +func FuzzSaturatingSub(f *testing.F) { + f.Fuzz(func(t *testing.T, a, b int64) { + got := SaturatingSub(a, b) + expected := saturatingBigToInt[int64](new(big.Int).Sub(toBig(a), toBig(b))) + if got != expected { + t.Errorf("SaturatingSub(%v, %v) = %v, expected %v", a, b, got, expected) + } + }) +} + +func FuzzSaturatingMulInt8(f *testing.F) { + fuzzSaturatingMul[int8](f) +} + +func FuzzSaturatingMulInt16(f *testing.F) { + fuzzSaturatingMul[int16](f) +} + +func FuzzSaturatingMulInt32(f *testing.F) { + fuzzSaturatingMul[int32](f) +} + +func FuzzSaturatingMulInt64(f *testing.F) { + fuzzSaturatingMul[int64](f) +} + +func FuzzSaturatingNegInt8(f *testing.F) { + fuzzSaturatingNeg[int8](f) +} + +func FuzzSaturatingNegInt16(f *testing.F) { + fuzzSaturatingNeg[int16](f) +} + +func FuzzSaturatingNegInt32(f *testing.F) { + fuzzSaturatingNeg[int32](f) +} + +func FuzzSaturatingNegInt64(f *testing.F) { + fuzzSaturatingNeg[int64](f) +} diff --git a/util/arbmath/math_test.go b/util/arbmath/math_test.go index 7bb643f916..1be60dc58b 100644 --- a/util/arbmath/math_test.go +++ b/util/arbmath/math_test.go @@ -1,13 +1,16 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package arbmath import ( + "bytes" + "fmt" "math" "math/rand" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/offchainlabs/nitro/util/testhelpers" ) @@ -59,6 +62,167 @@ func TestMath(t *testing.T) { Fail(t, "incorrect", "2^", i, diff, approx, correct) } } + + assert := func(cond bool) { + t.Helper() + if !cond { + Fail(t) + } + } + assert(uint64(math.MaxInt64) == SaturatingUCast[uint64](int64(math.MaxInt64))) + assert(uint64(math.MaxInt64-1) == SaturatingUCast[uint64](int64(math.MaxInt64-1))) + assert(uint64(math.MaxInt64-1) == SaturatingUCast[uint64](math.MaxInt64-1)) + assert(uint64(math.MaxInt64) == SaturatingUCast[uint64](math.MaxInt64)) + assert(uint32(math.MaxUint32) == SaturatingUCast[uint32](math.MaxInt64-1)) + assert(uint16(math.MaxUint16) == SaturatingUCast[uint16](math.MaxInt32)) + assert(uint16(math.MaxUint16) == SaturatingUCast[uint16](math.MaxInt32-1)) + assert(uint16(math.MaxUint16) == SaturatingUCast[uint16](math.MaxInt-1)) + assert(uint8(math.MaxUint8) == SaturatingUCast[uint8](math.MaxInt-1)) + assert(uint(math.MaxInt-1) == SaturatingUCast[uint](math.MaxInt-1)) + assert(uint(math.MaxInt-1) == SaturatingUCast[uint](int64(math.MaxInt-1))) + + assert(int64(math.MaxInt64) == SaturatingCast[int64, uint64](math.MaxUint64)) + assert(int64(math.MaxInt64) == SaturatingCast[int64, uint64](math.MaxUint64-1)) + assert(int32(math.MaxInt32) == SaturatingCast[int32, uint64](math.MaxUint64)) + assert(int32(math.MaxInt32) == SaturatingCast[int32, uint64](math.MaxUint64-1)) + assert(int8(math.MaxInt8) == SaturatingCast[int8, uint16](math.MaxUint16)) + assert(int8(32) == SaturatingCast[int8, uint16](32)) + assert(int16(0) == SaturatingCast[int16, uint32](0)) + assert(int16(math.MaxInt16) == SaturatingCast[int16, uint32](math.MaxInt16)) + assert(int16(math.MaxInt16) == SaturatingCast[int16, uint16](math.MaxInt16)) + assert(int16(math.MaxInt8) == SaturatingCast[int16, uint8](math.MaxInt8)) + + assert(uint32(math.MaxUint32) == SaturatingUUCast[uint32, uint64](math.MaxUint64)) + assert(uint32(math.MaxUint16) == SaturatingUUCast[uint32, uint64](math.MaxUint16)) + assert(uint32(math.MaxUint16) == SaturatingUUCast[uint32, uint16](math.MaxUint16)) + assert(uint16(math.MaxUint16) == SaturatingUUCast[uint16, uint16](math.MaxUint16)) +} + +func TestSlices(t *testing.T) { + assert_eq := func(left, right []uint8) { + t.Helper() + if !bytes.Equal(left, right) { + Fail(t, common.Bytes2Hex(left), " ", common.Bytes2Hex(right)) + } + } + + data := []uint8{0, 1, 2, 3} + assert_eq(SliceWithRunoff(data, 4, 4), data[0:0]) + assert_eq(SliceWithRunoff(data, 1, 0), data[0:0]) + assert_eq(SliceWithRunoff(data, 0, 0), data[0:0]) + assert_eq(SliceWithRunoff(data, 0, 1), data[0:1]) + assert_eq(SliceWithRunoff(data, 1, 3), data[1:3]) + assert_eq(SliceWithRunoff(data, 0, 4), data[0:4]) + assert_eq(SliceWithRunoff(data, 0, 5), data[0:4]) + assert_eq(SliceWithRunoff(data, 2, math.MaxUint8), data[2:4]) + + assert_eq(SliceWithRunoff(data, -1, -2), []uint8{}) + assert_eq(SliceWithRunoff(data, 5, 3), []uint8{}) + assert_eq(SliceWithRunoff(data, 7, 8), []uint8{}) +} + +func testMinMaxSignedValues[T Signed](t *testing.T, min T, max T) { + gotMin := MinSignedValue[T]() + if gotMin != min { + Fail(t, "expected min", min, "but got", gotMin) + } + gotMax := MaxSignedValue[T]() + if gotMax != max { + Fail(t, "expected max", max, "but got", gotMax) + } +} + +func TestMinMaxSignedValues(t *testing.T) { + testMinMaxSignedValues[int8](t, math.MinInt8, math.MaxInt8) + testMinMaxSignedValues[int16](t, math.MinInt16, math.MaxInt16) + testMinMaxSignedValues[int32](t, math.MinInt32, math.MaxInt32) + testMinMaxSignedValues[int64](t, math.MinInt64, math.MaxInt64) +} + +func TestSaturatingAdd(t *testing.T) { + tests := []struct { + a, b, expected int64 + }{ + {2, 3, 5}, + {-1, -2, -3}, + {math.MaxInt64, 1, math.MaxInt64}, + {math.MaxInt64, math.MaxInt64, math.MaxInt64}, + {math.MinInt64, -1, math.MinInt64}, + {math.MinInt64, math.MinInt64, math.MinInt64}, + } + + for _, tc := range tests { + t.Run(fmt.Sprintf("%v + %v = %v", tc.a, tc.b, tc.expected), func(t *testing.T) { + sum := SaturatingAdd(int64(tc.a), int64(tc.b)) + if sum != tc.expected { + t.Errorf("SaturatingAdd(%v, %v) = %v; want %v", tc.a, tc.b, sum, tc.expected) + } + }) + } +} + +func TestSaturatingSub(t *testing.T) { + tests := []struct { + a, b, expected int64 + }{ + {5, 3, 2}, + {-3, -2, -1}, + {math.MinInt64, 1, math.MinInt64}, + {math.MinInt64, -1, math.MinInt64 + 1}, + {math.MinInt64, math.MinInt64, 0}, + {0, math.MinInt64, math.MaxInt64}, + } + + for _, tc := range tests { + t.Run("", func(t *testing.T) { + sum := SaturatingSub(int64(tc.a), int64(tc.b)) + if sum != tc.expected { + t.Errorf("SaturatingSub(%v, %v) = %v; want %v", tc.a, tc.b, sum, tc.expected) + } + }) + } +} + +func TestSaturatingMul(t *testing.T) { + tests := []struct { + a, b, expected int64 + }{ + {5, 3, 15}, + {-3, -2, 6}, + {math.MaxInt64, 2, math.MaxInt64}, + {math.MinInt64, 2, math.MinInt64}, + } + + for _, tc := range tests { + t.Run(fmt.Sprintf("%v - %v = %v", tc.a, tc.b, tc.expected), func(t *testing.T) { + sum := SaturatingMul(int64(tc.a), int64(tc.b)) + if sum != tc.expected { + t.Errorf("SaturatingMul(%v, %v) = %v; want %v", tc.a, tc.b, sum, tc.expected) + } + }) + } +} + +func TestSaturatingNeg(t *testing.T) { + tests := []struct { + value int64 + expected int64 + }{ + {0, 0}, + {5, -5}, + {-5, 5}, + {math.MinInt64, math.MaxInt64}, + {math.MaxInt64, math.MinInt64 + 1}, + } + + for _, tc := range tests { + t.Run(fmt.Sprintf("-%v = %v", tc.value, tc.expected), func(t *testing.T) { + result := SaturatingNeg(tc.value) + if result != tc.expected { + t.Errorf("SaturatingNeg(%v) = %v: expected %v", tc.value, result, tc.expected) + } + }) + } } func Fail(t *testing.T, printables ...interface{}) { diff --git a/util/arbmath/time.go b/util/arbmath/time.go new file mode 100644 index 0000000000..af7d9ae84a --- /dev/null +++ b/util/arbmath/time.go @@ -0,0 +1,8 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package arbmath + +func DaysToSeconds[T Unsigned](days T) uint64 { + return uint64(days) * 24 * 60 * 60 +} diff --git a/util/arbmath/uint24.go b/util/arbmath/uint24.go new file mode 100644 index 0000000000..818f871a23 --- /dev/null +++ b/util/arbmath/uint24.go @@ -0,0 +1,57 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package arbmath + +import ( + "encoding/binary" + "errors" + "math/big" +) + +const MaxUint24 = 1<<24 - 1 // 16777215 + +type Uint24 uint32 + +func (value Uint24) ToBig() *big.Int { + return UintToBig(uint64(value)) +} + +func (value Uint24) ToUint32() uint32 { + return uint32(value) +} + +func (value Uint24) ToUint64() uint64 { + return uint64(value) +} + +func IntToUint24[T uint32 | uint64](value T) (Uint24, error) { + if value > T(MaxUint24) { + return Uint24(MaxUint24), errors.New("value out of range") + } + return Uint24(value), nil +} + +// Casts a huge to a uint24, panicking if out of bounds +func BigToUint24OrPanic(value *big.Int) Uint24 { + if value.Sign() < 0 { + panic("big.Int value is less than 0") + } + if !value.IsUint64() || value.Uint64() > MaxUint24 { + panic("big.Int value exceeds the max Uint24") + } + return Uint24(value.Uint64()) +} + +// creates a uint24 from its big-endian representation +func BytesToUint24(value []byte) Uint24 { + value32 := ConcatByteSlices([]byte{0}, value) + return Uint24(binary.BigEndian.Uint32(value32)) +} + +// casts a uint24 to its big-endian representation +func Uint24ToBytes(value Uint24) []byte { + result := make([]byte, 4) + binary.BigEndian.PutUint32(result, value.ToUint32()) + return result[1:] +} diff --git a/util/colors/colors.go b/util/colors/colors.go index 56d8b51d16..c652d80ca9 100644 --- a/util/colors/colors.go +++ b/util/colors/colors.go @@ -3,7 +3,10 @@ package colors -import "fmt" +import ( + "fmt" + "regexp" +) var Red = "\033[31;1m" var Blue = "\033[34;1m" @@ -48,3 +51,17 @@ func PrintYellow(args ...interface{}) { fmt.Print(args...) println(Clear) } + +func PrintPink(args ...interface{}) { + print(Pink) + fmt.Print(args...) + println(Clear) +} + +func Uncolor(text string) string { + uncolor := regexp.MustCompile("\x1b\\[([0-9]+;)*[0-9]+m") + unwhite := regexp.MustCompile(`\s+`) + + text = uncolor.ReplaceAllString(text, "") + return unwhite.ReplaceAllString(text, " ") +} diff --git a/util/headerreader/blob_client.go b/util/headerreader/blob_client.go index 664dbb5e30..73849d0d3a 100644 --- a/util/headerreader/blob_client.go +++ b/util/headerreader/blob_client.go @@ -13,6 +13,7 @@ import ( "net/url" "os" "path" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" @@ -188,8 +189,14 @@ const trailingCharsOfResponse = 25 func (b *BlobClient) blobSidecars(ctx context.Context, slot uint64, versionedHashes []common.Hash) ([]kzg4844.Blob, error) { rawData, err := beaconRequest[json.RawMessage](b, ctx, fmt.Sprintf("/eth/v1/beacon/blob_sidecars/%d", slot)) - if err != nil { - return nil, fmt.Errorf("error calling beacon client in blobSidecars: %w", err) + if err != nil || len(rawData) == 0 { + // blobs are pruned after 4096 epochs (1 epoch = 32 slots), we determine if the requested slot were to be pruned by a non-archive endpoint + roughAgeOfSlot := uint64(time.Now().Unix()) - (b.genesisTime + slot*b.secondsPerSlot) + if roughAgeOfSlot > b.secondsPerSlot*32*4096 { + return nil, fmt.Errorf("beacon client in blobSidecars got error or empty response fetching older blobs in slot: %d, an archive endpoint is required, please refer to https://docs.arbitrum.io/run-arbitrum-node/l1-ethereum-beacon-chain-rpc-providers, err: %w", slot, err) + } else { + return nil, fmt.Errorf("beacon client in blobSidecars got error or empty response fetching non-expired blobs in slot: %d, if using a prysm endpoint, try --enable-experimental-backfill flag, err: %w", slot, err) + } } var response []blobResponseItem if err := json.Unmarshal(rawData, &response); err != nil { diff --git a/util/rpcclient/rpcclient.go b/util/rpcclient/rpcclient.go index 02b41cf15d..56aebef396 100644 --- a/util/rpcclient/rpcclient.go +++ b/util/rpcclient/rpcclient.go @@ -21,14 +21,15 @@ import ( ) type ClientConfig struct { - URL string `json:"url,omitempty" koanf:"url"` - JWTSecret string `json:"jwtsecret,omitempty" koanf:"jwtsecret"` - Timeout time.Duration `json:"timeout,omitempty" koanf:"timeout" reload:"hot"` - Retries uint `json:"retries,omitempty" koanf:"retries" reload:"hot"` - ConnectionWait time.Duration `json:"connection-wait,omitempty" koanf:"connection-wait"` - ArgLogLimit uint `json:"arg-log-limit,omitempty" koanf:"arg-log-limit" reload:"hot"` - RetryErrors string `json:"retry-errors,omitempty" koanf:"retry-errors" reload:"hot"` - RetryDelay time.Duration `json:"retry-delay,omitempty" koanf:"retry-delay"` + URL string `json:"url,omitempty" koanf:"url"` + JWTSecret string `json:"jwtsecret,omitempty" koanf:"jwtsecret"` + Timeout time.Duration `json:"timeout,omitempty" koanf:"timeout" reload:"hot"` + Retries uint `json:"retries,omitempty" koanf:"retries" reload:"hot"` + ConnectionWait time.Duration `json:"connection-wait,omitempty" koanf:"connection-wait"` + ArgLogLimit uint `json:"arg-log-limit,omitempty" koanf:"arg-log-limit" reload:"hot"` + RetryErrors string `json:"retry-errors,omitempty" koanf:"retry-errors" reload:"hot"` + RetryDelay time.Duration `json:"retry-delay,omitempty" koanf:"retry-delay"` + WebsocketMessageSizeLimit int64 `json:"websocket-message-size-limit,omitempty" koanf:"websocket-message-size-limit"` retryErrors *regexp.Regexp } @@ -46,16 +47,18 @@ func (c *ClientConfig) Validate() error { type ClientConfigFetcher func() *ClientConfig var TestClientConfig = ClientConfig{ - URL: "self", - JWTSecret: "", + URL: "self", + JWTSecret: "", + WebsocketMessageSizeLimit: 256 * 1024 * 1024, } var DefaultClientConfig = ClientConfig{ - URL: "self-auth", - JWTSecret: "", - Retries: 3, - RetryErrors: "websocket: close.*|dial tcp .*|.*i/o timeout|.*connection reset by peer|.*connection refused", - ArgLogLimit: 2048, + URL: "self-auth", + JWTSecret: "", + Retries: 3, + RetryErrors: "websocket: close.*|dial tcp .*|.*i/o timeout|.*connection reset by peer|.*connection refused", + ArgLogLimit: 2048, + WebsocketMessageSizeLimit: 256 * 1024 * 1024, } func RPCClientAddOptions(prefix string, f *flag.FlagSet, defaultConfig *ClientConfig) { @@ -67,6 +70,7 @@ func RPCClientAddOptions(prefix string, f *flag.FlagSet, defaultConfig *ClientCo f.Uint(prefix+".retries", defaultConfig.Retries, "number of retries in case of failure(0 mean one attempt)") f.String(prefix+".retry-errors", defaultConfig.RetryErrors, "Errors matching this regular expression are automatically retried") f.Duration(prefix+".retry-delay", defaultConfig.RetryDelay, "delay between retries") + f.Int64(prefix+".websocket-message-size-limit", defaultConfig.WebsocketMessageSizeLimit, "websocket message size limit used by the RPC client. 0 means no limit") } type RpcClient struct { @@ -256,9 +260,9 @@ func (c *RpcClient) Start(ctx_in context.Context) error { var err error var client *rpc.Client if jwt == nil { - client, err = rpc.DialContext(ctx, url) + client, err = rpc.DialOptions(ctx, url, rpc.WithWebsocketMessageSizeLimit(c.config().WebsocketMessageSizeLimit)) } else { - client, err = rpc.DialOptions(ctx, url, rpc.WithHTTPAuth(node.NewJWTAuth([32]byte(*jwt)))) + client, err = rpc.DialOptions(ctx, url, rpc.WithHTTPAuth(node.NewJWTAuth([32]byte(*jwt))), rpc.WithWebsocketMessageSizeLimit(c.config().WebsocketMessageSizeLimit)) } cancelCtx() if err == nil { diff --git a/util/testhelpers/testhelpers.go b/util/testhelpers/testhelpers.go index bccc269171..071429879e 100644 --- a/util/testhelpers/testhelpers.go +++ b/util/testhelpers/testhelpers.go @@ -1,10 +1,14 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package testhelpers import ( - "crypto/rand" + "context" + crypto "crypto/rand" + "io" + "math/big" + "math/rand" "os" "regexp" "sync" @@ -13,6 +17,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/util/colors" + "golang.org/x/exp/slog" ) // Fail a test should an error occur @@ -29,33 +34,71 @@ func FailImpl(t *testing.T, printables ...interface{}) { } func RandomizeSlice(slice []byte) []byte { - _, err := rand.Read(slice) + _, err := crypto.Read(slice) if err != nil { panic(err) } return slice } +func RandomSlice(size uint64) []byte { + return RandomizeSlice(make([]byte, size)) +} + +func RandomHash() common.Hash { + var hash common.Hash + RandomizeSlice(hash[:]) + return hash +} + func RandomAddress() common.Address { var address common.Address RandomizeSlice(address[:]) return address } +func RandomCallValue(limit int64) *big.Int { + return big.NewInt(rand.Int63n(limit)) +} + +// Computes a psuedo-random uint64 on the interval [min, max] +func RandomUint32(min, max uint32) uint32 { + return uint32(RandomUint64(uint64(min), uint64(max))) +} + +// Computes a psuedo-random uint64 on the interval [min, max] +func RandomUint64(min, max uint64) uint64 { + return uint64(rand.Uint64()%(max-min+1) + min) +} + +func RandomBool() bool { + return rand.Int31n(2) == 0 +} + type LogHandler struct { - mutex sync.Mutex - t *testing.T - records []log.Record - streamHandler log.Handler + mutex sync.Mutex + t *testing.T + records []slog.Record + terminalHandler *log.TerminalHandler +} + +func (h *LogHandler) Enabled(_ context.Context, level slog.Level) bool { + return h.terminalHandler.Enabled(context.Background(), level) +} +func (h *LogHandler) WithGroup(name string) slog.Handler { + return h.terminalHandler.WithGroup(name) +} +func (h *LogHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + return h.terminalHandler.WithAttrs(attrs) } -func (h *LogHandler) Log(record *log.Record) error { - if err := h.streamHandler.Log(record); err != nil { +func (h *LogHandler) Handle(_ context.Context, record slog.Record) error { + if err := h.terminalHandler.Handle(context.Background(), record); err != nil { return err } h.mutex.Lock() defer h.mutex.Unlock() - h.records = append(h.records, *record) + h.records = append(h.records, record) return nil } @@ -65,7 +108,7 @@ func (h *LogHandler) WasLogged(pattern string) bool { h.mutex.Lock() defer h.mutex.Unlock() for _, record := range h.records { - if re.MatchString(record.Msg) { + if re.MatchString(record.Message) { return true } } @@ -74,16 +117,16 @@ func (h *LogHandler) WasLogged(pattern string) bool { func newLogHandler(t *testing.T) *LogHandler { return &LogHandler{ - t: t, - records: make([]log.Record, 0), - streamHandler: log.StreamHandler(os.Stderr, log.TerminalFormat(false)), + t: t, + records: make([]slog.Record, 0), + terminalHandler: log.NewTerminalHandler(io.Writer(os.Stderr), false), } } -func InitTestLog(t *testing.T, level log.Lvl) *LogHandler { +func InitTestLog(t *testing.T, level slog.Level) *LogHandler { handler := newLogHandler(t) glogger := log.NewGlogHandler(handler) glogger.Verbosity(level) - log.Root().SetHandler(glogger) + log.SetDefault(log.NewLogger(glogger)) return handler } diff --git a/validator/client/redis/producer.go b/validator/client/redis/producer.go new file mode 100644 index 0000000000..41ae100954 --- /dev/null +++ b/validator/client/redis/producer.go @@ -0,0 +1,155 @@ +package redis + +import ( + "context" + "fmt" + "sync/atomic" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/go-redis/redis/v8" + "github.com/offchainlabs/nitro/pubsub" + "github.com/offchainlabs/nitro/util/containers" + "github.com/offchainlabs/nitro/util/redisutil" + "github.com/offchainlabs/nitro/util/stopwaiter" + "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/server_api" + "github.com/offchainlabs/nitro/validator/server_common" + "github.com/spf13/pflag" +) + +type ValidationClientConfig struct { + Name string `koanf:"name"` + Room int32 `koanf:"room"` + RedisURL string `koanf:"redis-url"` + ProducerConfig pubsub.ProducerConfig `koanf:"producer-config"` + CreateStreams bool `koanf:"create-streams"` +} + +func (c ValidationClientConfig) Enabled() bool { + return c.RedisURL != "" +} + +var DefaultValidationClientConfig = ValidationClientConfig{ + Name: "redis validation client", + Room: 2, + RedisURL: "", + ProducerConfig: pubsub.DefaultProducerConfig, + CreateStreams: true, +} + +var TestValidationClientConfig = ValidationClientConfig{ + Name: "test redis validation client", + Room: 2, + RedisURL: "", + ProducerConfig: pubsub.TestProducerConfig, + CreateStreams: false, +} + +func ValidationClientConfigAddOptions(prefix string, f *pflag.FlagSet) { + f.String(prefix+".name", DefaultValidationClientConfig.Name, "validation client name") + f.Int32(prefix+".room", DefaultValidationClientConfig.Room, "validation client room") + pubsub.ProducerAddConfigAddOptions(prefix+".producer-config", f) + f.Bool(prefix+".create-streams", DefaultValidationClientConfig.CreateStreams, "create redis streams if it does not exist") +} + +// ValidationClient implements validation client through redis streams. +type ValidationClient struct { + stopwaiter.StopWaiter + name string + room int32 + // producers stores moduleRoot to producer mapping. + producers map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState] + producerConfig pubsub.ProducerConfig + redisClient redis.UniversalClient + moduleRoots []common.Hash + createStreams bool +} + +func NewValidationClient(cfg *ValidationClientConfig) (*ValidationClient, error) { + if cfg.RedisURL == "" { + return nil, fmt.Errorf("redis url cannot be empty") + } + redisClient, err := redisutil.RedisClientFromURL(cfg.RedisURL) + if err != nil { + return nil, err + } + return &ValidationClient{ + name: cfg.Name, + room: cfg.Room, + producers: make(map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState]), + producerConfig: cfg.ProducerConfig, + redisClient: redisClient, + createStreams: cfg.CreateStreams, + }, nil +} + +func (c *ValidationClient) Initialize(ctx context.Context, moduleRoots []common.Hash) error { + for _, mr := range moduleRoots { + if c.createStreams { + if err := pubsub.CreateStream(ctx, server_api.RedisStreamForRoot(mr), c.redisClient); err != nil { + return fmt.Errorf("creating redis stream: %w", err) + } + } + if _, exists := c.producers[mr]; exists { + log.Warn("Producer already existsw for module root", "hash", mr) + continue + } + p, err := pubsub.NewProducer[*validator.ValidationInput, validator.GoGlobalState]( + c.redisClient, server_api.RedisStreamForRoot(mr), &c.producerConfig) + if err != nil { + log.Warn("failed init redis for %v: %w", mr, err) + continue + } + p.Start(c.GetContext()) + c.producers[mr] = p + c.moduleRoots = append(c.moduleRoots, mr) + } + return nil +} + +func (c *ValidationClient) WasmModuleRoots() ([]common.Hash, error) { + return c.moduleRoots, nil +} + +func (c *ValidationClient) Launch(entry *validator.ValidationInput, moduleRoot common.Hash) validator.ValidationRun { + atomic.AddInt32(&c.room, -1) + defer atomic.AddInt32(&c.room, 1) + producer, found := c.producers[moduleRoot] + if !found { + errPromise := containers.NewReadyPromise(validator.GoGlobalState{}, fmt.Errorf("no validation is configured for wasm root %v", moduleRoot)) + return server_common.NewValRun(errPromise, moduleRoot) + } + promise, err := producer.Produce(c.GetContext(), entry) + if err != nil { + errPromise := containers.NewReadyPromise(validator.GoGlobalState{}, fmt.Errorf("error producing input: %w", err)) + return server_common.NewValRun(errPromise, moduleRoot) + } + return server_common.NewValRun(promise, moduleRoot) +} + +func (c *ValidationClient) Start(ctx_in context.Context) error { + for _, p := range c.producers { + p.Start(ctx_in) + } + c.StopWaiter.Start(ctx_in, c) + return nil +} + +func (c *ValidationClient) Stop() { + for _, p := range c.producers { + p.StopAndWait() + } + c.StopWaiter.StopAndWait() +} + +func (c *ValidationClient) Name() string { + if c.Started() { + return c.name + } + return "(not started)" +} + +func (c *ValidationClient) Room() int { + return int(c.room) +} diff --git a/validator/server_api/validation_client.go b/validator/client/validation_client.go similarity index 69% rename from validator/server_api/validation_client.go rename to validator/client/validation_client.go index d6143ca917..fa6b9000f2 100644 --- a/validator/server_api/validation_client.go +++ b/validator/client/validation_client.go @@ -1,9 +1,13 @@ -package server_api +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package client import ( "context" "encoding/base64" "errors" + "fmt" "sync/atomic" "time" @@ -13,6 +17,7 @@ import ( "github.com/offchainlabs/nitro/util/rpcclient" "github.com/offchainlabs/nitro/util/stopwaiter" + "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/server_common" "github.com/ethereum/go-ethereum/common" @@ -22,9 +27,10 @@ import ( type ValidationClient struct { stopwaiter.StopWaiter - client *rpcclient.RpcClient - name string - room int32 + client *rpcclient.RpcClient + name string + room int32 + wasmModuleRoots []common.Hash } func NewValidationClient(config rpcclient.ClientConfigFetcher, stack *node.Node) *ValidationClient { @@ -36,9 +42,9 @@ func NewValidationClient(config rpcclient.ClientConfigFetcher, stack *node.Node) func (c *ValidationClient) Launch(entry *validator.ValidationInput, moduleRoot common.Hash) validator.ValidationRun { atomic.AddInt32(&c.room, -1) promise := stopwaiter.LaunchPromiseThread[validator.GoGlobalState](c, func(ctx context.Context) (validator.GoGlobalState, error) { - input := ValidationInputToJson(entry) + input := server_api.ValidationInputToJson(entry) var res validator.GoGlobalState - err := c.client.CallContext(ctx, &res, Namespace+"_validate", input, moduleRoot) + err := c.client.CallContext(ctx, &res, server_api.Namespace+"_validate", input, moduleRoot) atomic.AddInt32(&c.room, 1) return res, err }) @@ -48,21 +54,25 @@ func (c *ValidationClient) Launch(entry *validator.ValidationInput, moduleRoot c func (c *ValidationClient) Start(ctx_in context.Context) error { c.StopWaiter.Start(ctx_in, c) ctx := c.GetContext() - err := c.client.Start(ctx) - if err != nil { + if err := c.client.Start(ctx); err != nil { return err } var name string - err = c.client.CallContext(ctx, &name, Namespace+"_name") - if err != nil { + if err := c.client.CallContext(ctx, &name, server_api.Namespace+"_name"); err != nil { return err } if len(name) == 0 { return errors.New("couldn't read name from server") } + var moduleRoots []common.Hash + if err := c.client.CallContext(c.GetContext(), &moduleRoots, server_api.Namespace+"_wasmModuleRoots"); err != nil { + return err + } + if len(moduleRoots) == 0 { + return fmt.Errorf("server reported no wasmModuleRoots") + } var room int - err = c.client.CallContext(c.GetContext(), &room, Namespace+"_room") - if err != nil { + if err := c.client.CallContext(c.GetContext(), &room, server_api.Namespace+"_room"); err != nil { return err } if room < 2 { @@ -72,10 +82,18 @@ func (c *ValidationClient) Start(ctx_in context.Context) error { log.Info("connected to validation server", "name", name, "room", room) } atomic.StoreInt32(&c.room, int32(room)) + c.wasmModuleRoots = moduleRoots c.name = name return nil } +func (c *ValidationClient) WasmModuleRoots() ([]common.Hash, error) { + if c.Started() { + return c.wasmModuleRoots, nil + } + return nil, errors.New("not started") +} + func (c *ValidationClient) Stop() { c.StopWaiter.StopOnly() if c.client != nil { @@ -84,10 +102,7 @@ func (c *ValidationClient) Stop() { } func (c *ValidationClient) Name() string { - if c.Started() { - return c.name - } - return "(not started)" + return c.name } func (c *ValidationClient) Room() int { @@ -111,7 +126,7 @@ func NewExecutionClient(config rpcclient.ClientConfigFetcher, stack *node.Node) func (c *ExecutionClient) CreateExecutionRun(wasmModuleRoot common.Hash, input *validator.ValidationInput) containers.PromiseInterface[validator.ExecutionRun] { return stopwaiter.LaunchPromiseThread[validator.ExecutionRun](c, func(ctx context.Context) (validator.ExecutionRun, error) { var res uint64 - err := c.client.CallContext(ctx, &res, Namespace+"_createExecutionRun", wasmModuleRoot, ValidationInputToJson(input)) + err := c.client.CallContext(ctx, &res, server_api.Namespace+"_createExecutionRun", wasmModuleRoot, server_api.ValidationInputToJson(input)) if err != nil { return nil, err } @@ -133,7 +148,7 @@ type ExecutionClientRun struct { func (c *ExecutionClient) LatestWasmModuleRoot() containers.PromiseInterface[common.Hash] { return stopwaiter.LaunchPromiseThread[common.Hash](c, func(ctx context.Context) (common.Hash, error) { var res common.Hash - err := c.client.CallContext(ctx, &res, Namespace+"_latestWasmModuleRoot") + err := c.client.CallContext(ctx, &res, server_api.Namespace+"_latestWasmModuleRoot") if err != nil { return common.Hash{}, err } @@ -142,15 +157,20 @@ func (c *ExecutionClient) LatestWasmModuleRoot() containers.PromiseInterface[com } func (c *ExecutionClient) WriteToFile(input *validator.ValidationInput, expOut validator.GoGlobalState, moduleRoot common.Hash) containers.PromiseInterface[struct{}] { - jsonInput := ValidationInputToJson(input) + jsonInput := server_api.ValidationInputToJson(input) + if err := jsonInput.WriteToFile(); err != nil { + return stopwaiter.LaunchPromiseThread[struct{}](c, func(ctx context.Context) (struct{}, error) { + return struct{}{}, err + }) + } return stopwaiter.LaunchPromiseThread[struct{}](c, func(ctx context.Context) (struct{}, error) { - err := c.client.CallContext(ctx, nil, Namespace+"_writeToFile", jsonInput, expOut, moduleRoot) + err := c.client.CallContext(ctx, nil, server_api.Namespace+"_writeToFile", jsonInput, expOut, moduleRoot) return struct{}{}, err }) } func (r *ExecutionClientRun) SendKeepAlive(ctx context.Context) time.Duration { - err := r.client.client.CallContext(ctx, nil, Namespace+"_execKeepAlive", r.id) + err := r.client.client.CallContext(ctx, nil, server_api.Namespace+"_execKeepAlive", r.id) if err != nil { log.Error("execution run keepalive failed", "err", err) } @@ -164,12 +184,12 @@ func (r *ExecutionClientRun) Start(ctx_in context.Context) { func (r *ExecutionClientRun) GetStepAt(pos uint64) containers.PromiseInterface[*validator.MachineStepResult] { return stopwaiter.LaunchPromiseThread[*validator.MachineStepResult](r, func(ctx context.Context) (*validator.MachineStepResult, error) { - var resJson MachineStepResultJson - err := r.client.client.CallContext(ctx, &resJson, Namespace+"_getStepAt", r.id, pos) + var resJson server_api.MachineStepResultJson + err := r.client.client.CallContext(ctx, &resJson, server_api.Namespace+"_getStepAt", r.id, pos) if err != nil { return nil, err } - res, err := MachineStepResultFromJson(&resJson) + res, err := server_api.MachineStepResultFromJson(&resJson) if err != nil { return nil, err } @@ -180,7 +200,7 @@ func (r *ExecutionClientRun) GetStepAt(pos uint64) containers.PromiseInterface[* func (r *ExecutionClientRun) GetProofAt(pos uint64) containers.PromiseInterface[[]byte] { return stopwaiter.LaunchPromiseThread[[]byte](r, func(ctx context.Context) ([]byte, error) { var resString string - err := r.client.client.CallContext(ctx, &resString, Namespace+"_getProofAt", r.id, pos) + err := r.client.client.CallContext(ctx, &resString, server_api.Namespace+"_getProofAt", r.id, pos) if err != nil { return nil, err } @@ -194,7 +214,7 @@ func (r *ExecutionClientRun) GetLastStep() containers.PromiseInterface[*validato func (r *ExecutionClientRun) PrepareRange(start, end uint64) containers.PromiseInterface[struct{}] { return stopwaiter.LaunchPromiseThread[struct{}](r, func(ctx context.Context) (struct{}, error) { - err := r.client.client.CallContext(ctx, nil, Namespace+"_prepareRange", r.id, start, end) + err := r.client.client.CallContext(ctx, nil, server_api.Namespace+"_prepareRange", r.id, start, end) if err != nil && ctx.Err() == nil { log.Warn("prepare execution got error", "err", err) } @@ -205,7 +225,7 @@ func (r *ExecutionClientRun) PrepareRange(start, end uint64) containers.PromiseI func (r *ExecutionClientRun) Close() { r.StopOnly() r.LaunchUntrackedThread(func() { - err := r.client.client.CallContext(r.GetParentContext(), nil, Namespace+"_closeExec", r.id) + err := r.client.client.CallContext(r.GetParentContext(), nil, server_api.Namespace+"_closeExec", r.id) if err != nil { log.Warn("closing execution client run got error", "err", err, "client", r.client.Name(), "id", r.id) } diff --git a/validator/interface.go b/validator/interface.go index 5785ac4de1..0324b996ed 100644 --- a/validator/interface.go +++ b/validator/interface.go @@ -9,6 +9,7 @@ import ( type ValidationSpawner interface { Launch(entry *ValidationInput, moduleRoot common.Hash) ValidationRun + WasmModuleRoots() ([]common.Hash, error) Start(context.Context) error Stop() Name() string diff --git a/validator/server_api/json.go b/validator/server_api/json.go index 2029741989..3dd817d5ae 100644 --- a/validator/server_api/json.go +++ b/validator/server_api/json.go @@ -5,19 +5,56 @@ package server_api import ( "encoding/base64" + "encoding/json" + "fmt" + "os" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/util/jsonapi" "github.com/offchainlabs/nitro/validator" ) -type BatchInfoJson struct { - Number uint64 - DataB64 string +const Namespace string = "validation" + +type MachineStepResultJson struct { + Hash common.Hash + Position uint64 + Status uint8 + GlobalState validator.GoGlobalState } -type ValidationInputJson struct { +func MachineStepResultToJson(result *validator.MachineStepResult) *MachineStepResultJson { + return &MachineStepResultJson{ + Hash: result.Hash, + Position: result.Position, + Status: uint8(result.Status), + GlobalState: result.GlobalState, + } +} + +func MachineStepResultFromJson(resultJson *MachineStepResultJson) (*validator.MachineStepResult, error) { + + return &validator.MachineStepResult{ + Hash: resultJson.Hash, + Position: resultJson.Position, + Status: validator.MachineStatus(resultJson.Status), + GlobalState: resultJson.GlobalState, + }, nil +} + +func RedisStreamForRoot(moduleRoot common.Hash) string { + return fmt.Sprintf("stream:%s", moduleRoot.Hex()) +} + +type Request struct { + Input *InputJSON + ModuleRoot common.Hash +} + +type InputJSON struct { Id uint64 HasDelayedMsg bool DelayedMsgNr uint64 @@ -25,29 +62,61 @@ type ValidationInputJson struct { BatchInfo []BatchInfoJson DelayedMsgB64 string StartState validator.GoGlobalState + UserWasms map[common.Hash]UserWasmJson + DebugChain bool +} + +func (i *InputJSON) WriteToFile() error { + contents, err := json.MarshalIndent(i, "", " ") + if err != nil { + return err + } + if err = os.WriteFile(fmt.Sprintf("block_inputs_%d.json", i.Id), contents, 0600); err != nil { + return err + } + return nil } -func ValidationInputToJson(entry *validator.ValidationInput) *ValidationInputJson { +type UserWasmJson struct { + Module string + Asm string +} + +type BatchInfoJson struct { + Number uint64 + DataB64 string +} + +func ValidationInputToJson(entry *validator.ValidationInput) *InputJSON { jsonPreimagesMap := make(map[arbutil.PreimageType]*jsonapi.PreimagesMapJson) for ty, preimages := range entry.Preimages { jsonPreimagesMap[ty] = jsonapi.NewPreimagesMapJson(preimages) } - res := &ValidationInputJson{ + res := &InputJSON{ Id: entry.Id, HasDelayedMsg: entry.HasDelayedMsg, DelayedMsgNr: entry.DelayedMsgNr, DelayedMsgB64: base64.StdEncoding.EncodeToString(entry.DelayedMsg), StartState: entry.StartState, PreimagesB64: jsonPreimagesMap, + UserWasms: make(map[common.Hash]UserWasmJson), + DebugChain: entry.DebugChain, } for _, binfo := range entry.BatchInfo { encData := base64.StdEncoding.EncodeToString(binfo.Data) - res.BatchInfo = append(res.BatchInfo, BatchInfoJson{binfo.Number, encData}) + res.BatchInfo = append(res.BatchInfo, BatchInfoJson{Number: binfo.Number, DataB64: encData}) + } + for moduleHash, info := range entry.UserWasms { + encWasm := UserWasmJson{ + Asm: base64.StdEncoding.EncodeToString(info.Asm), + Module: base64.StdEncoding.EncodeToString(info.Module), + } + res.UserWasms[moduleHash] = encWasm } return res } -func ValidationInputFromJson(entry *ValidationInputJson) (*validator.ValidationInput, error) { +func ValidationInputFromJson(entry *InputJSON) (*validator.ValidationInput, error) { preimages := make(map[arbutil.PreimageType]map[common.Hash][]byte) for ty, jsonPreimages := range entry.PreimagesB64 { preimages[ty] = jsonPreimages.Map @@ -58,6 +127,8 @@ func ValidationInputFromJson(entry *ValidationInputJson) (*validator.ValidationI DelayedMsgNr: entry.DelayedMsgNr, StartState: entry.StartState, Preimages: preimages, + UserWasms: make(state.UserWasms), + DebugChain: entry.DebugChain, } delayed, err := base64.StdEncoding.DecodeString(entry.DelayedMsgB64) if err != nil { @@ -75,31 +146,20 @@ func ValidationInputFromJson(entry *ValidationInputJson) (*validator.ValidationI } valInput.BatchInfo = append(valInput.BatchInfo, decInfo) } - return valInput, nil -} - -type MachineStepResultJson struct { - Hash common.Hash - Position uint64 - Status uint8 - GlobalState validator.GoGlobalState -} - -func MachineStepResultToJson(result *validator.MachineStepResult) *MachineStepResultJson { - return &MachineStepResultJson{ - Hash: result.Hash, - Position: result.Position, - Status: uint8(result.Status), - GlobalState: result.GlobalState, + for moduleHash, info := range entry.UserWasms { + asm, err := base64.StdEncoding.DecodeString(info.Asm) + if err != nil { + return nil, err + } + module, err := base64.StdEncoding.DecodeString(info.Module) + if err != nil { + return nil, err + } + decInfo := state.ActivatedWasm{ + Asm: asm, + Module: module, + } + valInput.UserWasms[moduleHash] = decInfo } -} - -func MachineStepResultFromJson(resultJson *MachineStepResultJson) (*validator.MachineStepResult, error) { - - return &validator.MachineStepResult{ - Hash: resultJson.Hash, - Position: resultJson.Position, - Status: validator.MachineStatus(resultJson.Status), - GlobalState: resultJson.GlobalState, - }, nil + return valInput, nil } diff --git a/validator/server_arb/machine.go b/validator/server_arb/machine.go index e59659c7af..cffd3db0ee 100644 --- a/validator/server_arb/machine.go +++ b/validator/server_arb/machine.go @@ -1,5 +1,5 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE +// Copyright 2021-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE package server_arb @@ -22,10 +22,17 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" "github.com/offchainlabs/nitro/arbutil" + "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/containers" "github.com/offchainlabs/nitro/validator" ) +type u8 = C.uint8_t +type u16 = C.uint16_t +type u32 = C.uint32_t +type u64 = C.uint64_t +type usize = C.size_t + type MachineInterface interface { CloneMachineInterface() MachineInterface GetStepCount() uint64 @@ -81,10 +88,11 @@ func machineFromPointer(ptr *C.struct_Machine) *ArbitratorMachine { return mach } -func LoadSimpleMachine(wasm string, libraries []string) (*ArbitratorMachine, error) { +func LoadSimpleMachine(wasm string, libraries []string, debugChain bool) (*ArbitratorMachine, error) { cWasm := C.CString(wasm) cLibraries := CreateCStringList(libraries) - mach := C.arbitrator_load_machine(cWasm, cLibraries, C.long(len(libraries))) + debug := usize(arbmath.BoolToUint32(debugChain)) + mach := C.arbitrator_load_machine(cWasm, cLibraries, C.long(len(libraries)), debug) C.free(unsafe.Pointer(cWasm)) FreeCStringList(cLibraries, len(libraries)) if mach == nil { @@ -171,8 +179,8 @@ func (m *ArbitratorMachine) ValidForStep(requestedStep uint64) bool { } } -func manageConditionByte(ctx context.Context) (*C.uint8_t, func()) { - var zero C.uint8_t +func manageConditionByte(ctx context.Context) (*u8, func()) { + var zero u8 conditionByte := &zero doneEarlyChan := make(chan struct{}) @@ -205,11 +213,10 @@ func (m *ArbitratorMachine) Step(ctx context.Context, count uint64) error { conditionByte, cancel := manageConditionByte(ctx) defer cancel() - err := C.arbitrator_step(m.ptr, C.uint64_t(count), conditionByte) + err := C.arbitrator_step(m.ptr, u64(count), conditionByte) + defer C.free(unsafe.Pointer(err)) if err != nil { - errString := C.GoString(err) - C.free(unsafe.Pointer(err)) - return errors.New(errString) + return errors.New(C.GoString(err)) } return ctx.Err() @@ -226,7 +233,11 @@ func (m *ArbitratorMachine) StepUntilHostIo(ctx context.Context) error { conditionByte, cancel := manageConditionByte(ctx) defer cancel() - C.arbitrator_step_until_host_io(m.ptr, conditionByte) + err := C.arbitrator_step_until_host_io(m.ptr, conditionByte) + defer C.free(unsafe.Pointer(err)) + if err != nil { + return errors.New(C.GoString(err)) + } return ctx.Err() } @@ -252,6 +263,7 @@ func (m *ArbitratorMachine) GetModuleRoot() (hash common.Hash) { } return } + func (m *ArbitratorMachine) ProveNextStep() []byte { defer runtime.KeepAlive(m) m.mutex.Lock() @@ -309,7 +321,7 @@ func (m *ArbitratorMachine) AddSequencerInboxMessage(index uint64, data []byte) return errors.New("machine frozen") } cbyte := CreateCByteArray(data) - status := C.arbitrator_add_inbox_message(m.ptr, C.uint64_t(0), C.uint64_t(index), cbyte) + status := C.arbitrator_add_inbox_message(m.ptr, u64(0), u64(index), cbyte) DestroyCByteArray(cbyte) if status != 0 { return errors.New("failed to add sequencer inbox message") @@ -328,7 +340,7 @@ func (m *ArbitratorMachine) AddDelayedInboxMessage(index uint64, data []byte) er } cbyte := CreateCByteArray(data) - status := C.arbitrator_add_inbox_message(m.ptr, C.uint64_t(1), C.uint64_t(index), cbyte) + status := C.arbitrator_add_inbox_message(m.ptr, u64(1), u64(index), cbyte) DestroyCByteArray(cbyte) if status != 0 { return errors.New("failed to add sequencer inbox message") @@ -358,7 +370,7 @@ func preimageResolver(context C.size_t, ty C.uint8_t, ptr unsafe.Pointer) C.Reso } } return C.ResolvedPreimage{ - ptr: (*C.uint8_t)(C.CBytes(preimage)), + ptr: (*u8)(C.CBytes(preimage)), len: (C.ptrdiff_t)(len(preimage)), } } @@ -374,6 +386,24 @@ func (m *ArbitratorMachine) SetPreimageResolver(resolver GoPreimageResolver) err preimageResolvers.Store(id, resolver) m.contextId = &id runtime.SetFinalizer(m.contextId, freeContextId) - C.arbitrator_set_context(m.ptr, C.uint64_t(id)) + C.arbitrator_set_context(m.ptr, u64(id)) + return nil +} + +func (m *ArbitratorMachine) AddUserWasm(moduleHash common.Hash, module []byte) error { + defer runtime.KeepAlive(m) + if m.frozen { + return errors.New("machine frozen") + } + hashBytes := [32]u8{} + for index, byte := range moduleHash.Bytes() { + hashBytes[index] = u8(byte) + } + C.arbitrator_add_user_wasm( + m.ptr, + (*u8)(arbutil.SliceToPointer(module)), + usize(len(module)), + &C.struct_Bytes32{hashBytes}, + ) return nil } diff --git a/validator/server_arb/nitro_machine.go b/validator/server_arb/nitro_machine.go index acaf3b10e6..2b2cb230b6 100644 --- a/validator/server_arb/nitro_machine.go +++ b/validator/server_arb/nitro_machine.go @@ -1,5 +1,5 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE +// Copyright 2021-2023, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE package server_arb diff --git a/validator/server_arb/prover_interface.go b/validator/server_arb/prover_interface.go index 0cc1d0be86..bdd81ed588 100644 --- a/validator/server_arb/prover_interface.go +++ b/validator/server_arb/prover_interface.go @@ -1,11 +1,11 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2023, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE package server_arb /* -#cgo CFLAGS: -g -Wall -I../../target/include/ -#cgo LDFLAGS: ${SRCDIR}/../../target/lib/libprover.a -ldl -lm +#cgo CFLAGS: -g -Wall -I../target/include/ +#cgo LDFLAGS: ${SRCDIR}/../../target/lib/libstylus.a -ldl -lm #include "arbitrator.h" #include diff --git a/validator/server_arb/validator_spawner.go b/validator/server_arb/validator_spawner.go index 67aa5477eb..dca15c369e 100644 --- a/validator/server_arb/validator_spawner.go +++ b/validator/server_arb/validator_spawner.go @@ -11,13 +11,14 @@ import ( "sync/atomic" "time" - flag "github.com/spf13/pflag" + "github.com/spf13/pflag" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/containers" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" "github.com/offchainlabs/nitro/validator/server_common" + "github.com/offchainlabs/nitro/validator/valnode/redis" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" @@ -27,26 +28,29 @@ import ( var arbitratorValidationSteps = metrics.NewRegisteredHistogram("arbitrator/validation/steps", nil, metrics.NewBoundedHistogramSample()) type ArbitratorSpawnerConfig struct { - Workers int `koanf:"workers" reload:"hot"` - OutputPath string `koanf:"output-path" reload:"hot"` - Execution MachineCacheConfig `koanf:"execution" reload:"hot"` // hot reloading for new executions only - ExecutionRunTimeout time.Duration `koanf:"execution-run-timeout" reload:"hot"` + Workers int `koanf:"workers" reload:"hot"` + OutputPath string `koanf:"output-path" reload:"hot"` + Execution MachineCacheConfig `koanf:"execution" reload:"hot"` // hot reloading for new executions only + ExecutionRunTimeout time.Duration `koanf:"execution-run-timeout" reload:"hot"` + RedisValidationServerConfig redis.ValidationServerConfig `koanf:"redis-validation-server-config"` } type ArbitratorSpawnerConfigFecher func() *ArbitratorSpawnerConfig var DefaultArbitratorSpawnerConfig = ArbitratorSpawnerConfig{ - Workers: 0, - OutputPath: "./target/output", - Execution: DefaultMachineCacheConfig, - ExecutionRunTimeout: time.Minute * 15, + Workers: 0, + OutputPath: "./target/output", + Execution: DefaultMachineCacheConfig, + ExecutionRunTimeout: time.Minute * 15, + RedisValidationServerConfig: redis.DefaultValidationServerConfig, } -func ArbitratorSpawnerConfigAddOptions(prefix string, f *flag.FlagSet) { +func ArbitratorSpawnerConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Int(prefix+".workers", DefaultArbitratorSpawnerConfig.Workers, "number of concurrent validation threads") f.Duration(prefix+".execution-run-timeout", DefaultArbitratorSpawnerConfig.ExecutionRunTimeout, "timeout before discarding execution run") f.String(prefix+".output-path", DefaultArbitratorSpawnerConfig.OutputPath, "path to write machines to") MachineCacheConfigConfigAddOptions(prefix+".execution", f) + redis.ValidationServerConfigAddOptions(prefix+".redis-validation-server-config", f) } func DefaultArbitratorSpawnerConfigFetcher() *ArbitratorSpawnerConfig { @@ -80,6 +84,10 @@ func (s *ArbitratorSpawner) LatestWasmModuleRoot() containers.PromiseInterface[c return containers.NewReadyPromise(s.locator.LatestWasmModuleRoot(), nil) } +func (s *ArbitratorSpawner) WasmModuleRoots() ([]common.Hash, error) { + return s.locator.ModuleRoots(), nil +} + func (s *ArbitratorSpawner) Name() string { return "arbitrator" } @@ -110,6 +118,16 @@ func (v *ArbitratorSpawner) loadEntryToMachine(ctx context.Context, entry *valid return fmt.Errorf("error while trying to add sequencer msg for proving: %w", err) } } + for moduleHash, info := range entry.UserWasms { + err = mach.AddUserWasm(moduleHash, info.Module) + if err != nil { + log.Error( + "error adding user wasm for proving", + "err", err, "moduleHash", moduleHash, "blockNr", entry.Id, + ) + return fmt.Errorf("error adding user wasm for proving: %w", err) + } + } if entry.HasDelayedMsg { err = mach.AddDelayedInboxMessage(entry.DelayedMsgNr, entry.DelayedMsg) if err != nil { diff --git a/validator/server_common/machine_locator.go b/validator/server_common/machine_locator.go index 4c25448dda..71f6af60b6 100644 --- a/validator/server_common/machine_locator.go +++ b/validator/server_common/machine_locator.go @@ -8,21 +8,20 @@ import ( "strings" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" ) type MachineLocator struct { - rootPath string - latest common.Hash + rootPath string + latest common.Hash + moduleRoots []common.Hash } var ErrMachineNotFound = errors.New("machine not found") func NewMachineLocator(rootPath string) (*MachineLocator, error) { - var places []string - - if rootPath != "" { - places = append(places, rootPath) - } else { + dirs := []string{rootPath} + if rootPath == "" { // Check the project dir: /arbnode/node.go => ../../target/machines _, thisFile, _, ok := runtime.Caller(0) if !ok { @@ -30,7 +29,7 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { } projectDir := filepath.Dir(filepath.Dir(filepath.Dir(thisFile))) projectPath := filepath.Join(filepath.Join(projectDir, "target"), "machines") - places = append(places, projectPath) + dirs = append(dirs, projectPath) // Check the working directory: ./machines and ./target/machines workDir, err := os.Getwd() @@ -39,8 +38,8 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { } workPath1 := filepath.Join(workDir, "machines") workPath2 := filepath.Join(filepath.Join(workDir, "target"), "machines") - places = append(places, workPath1) - places = append(places, workPath2) + dirs = append(dirs, workPath1) + dirs = append(dirs, workPath2) // Check above the executable: => ../../machines execfile, err := os.Executable() @@ -48,22 +47,62 @@ func NewMachineLocator(rootPath string) (*MachineLocator, error) { return nil, err } execPath := filepath.Join(filepath.Dir(filepath.Dir(execfile)), "machines") - places = append(places, execPath) + dirs = append(dirs, execPath) } - for _, place := range places { - if _, err := os.Stat(place); err == nil { - var latestModuleRoot common.Hash - latestModuleRootPath := filepath.Join(place, "latest", "module-root.txt") - fileBytes, err := os.ReadFile(latestModuleRootPath) - if err == nil { - s := strings.TrimSpace(string(fileBytes)) - latestModuleRoot = common.HexToHash(s) + var ( + moduleRoots = make(map[common.Hash]bool) + latestModuleRoot common.Hash + ) + + for _, dir := range dirs { + fInfo, err := os.Stat(dir) + if err != nil { + log.Warn("Getting file info", "dir", dir, "error", err) + continue + } + if !fInfo.IsDir() { + // Skip files that are not directories. + continue + } + files, err := os.ReadDir(dir) + if err != nil { + log.Warn("Reading directory", "dir", dir, "error", err) + } + for _, file := range files { + mrFile := filepath.Join(dir, file.Name(), "module-root.txt") + if _, err := os.Stat(mrFile); err != nil { + // Skip if module-roots file does not exist. + continue } - return &MachineLocator{place, latestModuleRoot}, nil + mrContent, err := os.ReadFile(mrFile) + if err != nil { + log.Warn("Reading module roots file", "file path", mrFile, "error", err) + continue + } + moduleRoot := common.HexToHash(strings.TrimSpace(string(mrContent))) + if file.Name() != "latest" && file.Name() != moduleRoot.Hex() { + continue + } + moduleRoots[moduleRoot] = true + if file.Name() == "latest" { + latestModuleRoot = moduleRoot + } + rootPath = dir + } + if rootPath != "" { + break } } - return nil, ErrMachineNotFound + var roots []common.Hash + for k := range moduleRoots { + roots = append(roots, k) + } + return &MachineLocator{ + rootPath: rootPath, + latest: latestModuleRoot, + moduleRoots: roots, + }, nil } func (l MachineLocator) GetMachinePath(moduleRoot common.Hash) string { @@ -81,3 +120,7 @@ func (l MachineLocator) LatestWasmModuleRoot() common.Hash { func (l MachineLocator) RootPath() string { return l.rootPath } + +func (l MachineLocator) ModuleRoots() []common.Hash { + return l.moduleRoots +} diff --git a/validator/server_common/machine_locator_test.go b/validator/server_common/machine_locator_test.go new file mode 100644 index 0000000000..ac664fe660 --- /dev/null +++ b/validator/server_common/machine_locator_test.go @@ -0,0 +1,39 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package server_common + +import ( + "sort" + "testing" + + "github.com/google/go-cmp/cmp" +) + +var ( + wantLatestModuleRoot = "0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a" + wantModuleRoots = []string{ + "0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4", + "0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4", + "0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a", + } +) + +func TestNewMachineLocator(t *testing.T) { + ml, err := NewMachineLocator("testdata") + if err != nil { + t.Fatalf("Error creating new machine locator: %v", err) + } + if ml.latest.Hex() != wantLatestModuleRoot { + t.Errorf("NewMachineLocator() got latestModuleRoot: %v, want: %v", ml.latest, wantLatestModuleRoot) + } + var got []string + for _, s := range ml.ModuleRoots() { + got = append(got, s.Hex()) + } + sort.Strings(got) + sort.Strings(wantModuleRoots) + if diff := cmp.Diff(got, wantModuleRoots); diff != "" { + t.Errorf("NewMachineLocator() unexpected diff (-want +got):\n%s", diff) + } +} diff --git a/validator/server_common/testdata/0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4/module-root.txt b/validator/server_common/testdata/0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4/module-root.txt new file mode 100644 index 0000000000..067f2db9f5 --- /dev/null +++ b/validator/server_common/testdata/0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4/module-root.txt @@ -0,0 +1 @@ +0x68e4fe5023f792d4ef584796c84d710303a5e12ea02d6e37e2b5e9c4332507c4 diff --git a/validator/server_common/testdata/0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4/module-root.txt b/validator/server_common/testdata/0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4/module-root.txt new file mode 100644 index 0000000000..ad3a905ab7 --- /dev/null +++ b/validator/server_common/testdata/0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4/module-root.txt @@ -0,0 +1 @@ +0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4 diff --git a/validator/server_common/testdata/0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a/module-root.txt b/validator/server_common/testdata/0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a/module-root.txt new file mode 100644 index 0000000000..1a359ae1cd --- /dev/null +++ b/validator/server_common/testdata/0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a/module-root.txt @@ -0,0 +1 @@ +0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a diff --git a/validator/server_common/testdata/latest b/validator/server_common/testdata/latest new file mode 120000 index 0000000000..42d98792a0 --- /dev/null +++ b/validator/server_common/testdata/latest @@ -0,0 +1 @@ +0xf4389b835497a910d7ba3ebfb77aa93da985634f3c052de1290360635be40c4a \ No newline at end of file diff --git a/validator/server_jit/jit_machine.go b/validator/server_jit/jit_machine.go index a41e249cdb..1a3ccfa340 100644 --- a/validator/server_jit/jit_machine.go +++ b/validator/server_jit/jit_machine.go @@ -98,7 +98,7 @@ func (machine *JitMachine) prove( // Wait for the forked process to connect conn, err := tcp.Accept() if err != nil { - return state, err + return state, fmt.Errorf("error waiting for jit machine to connect back to validator: %w", err) } go func() { <-ctx.Done() @@ -121,6 +121,9 @@ func (machine *JitMachine) prove( writeUint8 := func(data uint8) error { return writeExact([]byte{data}) } + writeUint32 := func(data uint32) error { + return writeExact(arbmath.Uint32ToBytes(data)) + } writeUint64 := func(data uint64) error { return writeExact(arbmath.UintToBytes(data)) } @@ -188,14 +191,14 @@ func (machine *JitMachine) prove( // send known preimages preimageTypes := entry.Preimages - if err := writeUint64(uint64(len(preimageTypes))); err != nil { + if err := writeUint32(uint32(len(preimageTypes))); err != nil { return state, err } for ty, preimages := range preimageTypes { if err := writeUint8(uint8(ty)); err != nil { return state, err } - if err := writeUint64(uint64(len(preimages))); err != nil { + if err := writeUint32(uint32(len(preimages))); err != nil { return state, err } for hash, preimage := range preimages { @@ -208,6 +211,20 @@ func (machine *JitMachine) prove( } } + // send user wasms + userWasms := entry.UserWasms + if err := writeUint32(uint32(len(userWasms))); err != nil { + return state, err + } + for moduleHash, info := range userWasms { + if err := writeExact(moduleHash[:]); err != nil { + return state, err + } + if err := writeBytes(info.Asm); err != nil { + return state, err + } + } + // signal that we are done sending global state if err := writeExact(ready); err != nil { return state, err diff --git a/validator/server_jit/machine_loader.go b/validator/server_jit/machine_loader.go index 3a831928b7..b2bdb65322 100644 --- a/validator/server_jit/machine_loader.go +++ b/validator/server_jit/machine_loader.go @@ -27,13 +27,16 @@ var DefaultJitMachineConfig = JitMachineConfig{ func getJitPath() (string, error) { var jitBinary string executable, err := os.Executable() + println("executable: ", executable) if err == nil { - if strings.Contains(filepath.Base(executable), "test") { + if strings.Contains(filepath.Base(executable), "test") || strings.Contains(filepath.Dir(executable), "system_tests") { _, thisfile, _, _ := runtime.Caller(0) projectDir := filepath.Dir(filepath.Dir(filepath.Dir(thisfile))) + println("projectDir: ", projectDir) jitBinary = filepath.Join(projectDir, "target", "bin", "jit") } else { jitBinary = filepath.Join(filepath.Dir(executable), "jit") + println("inside else: ", jitBinary) } _, err = os.Stat(jitBinary) } diff --git a/validator/server_jit/spawner.go b/validator/server_jit/spawner.go index 6489821b5b..703e761af5 100644 --- a/validator/server_jit/spawner.go +++ b/validator/server_jit/spawner.go @@ -67,6 +67,10 @@ func (v *JitSpawner) Start(ctx_in context.Context) error { return nil } +func (v *JitSpawner) WasmModuleRoots() ([]common.Hash, error) { + return v.locator.ModuleRoots(), nil +} + func (v *JitSpawner) execute( ctx context.Context, entry *validator.ValidationInput, moduleRoot common.Hash, ) (validator.GoGlobalState, error) { diff --git a/validator/utils.go b/validator/utils.go new file mode 100644 index 0000000000..4c8ae65d08 --- /dev/null +++ b/validator/utils.go @@ -0,0 +1,20 @@ +package validator + +import ( + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" +) + +func SpawnerSupportsModule(spawner ValidationSpawner, requested common.Hash) bool { + supported, err := spawner.WasmModuleRoots() + if err != nil { + log.Warn("WasmModuleRoots returned error", "err", err) + return false + } + for _, root := range supported { + if root == requested { + return true + } + } + return false +} diff --git a/validator/validation_entry.go b/validator/validation_entry.go index 8bb021335e..446f84ca62 100644 --- a/validator/validation_entry.go +++ b/validator/validation_entry.go @@ -2,6 +2,7 @@ package validator import ( "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/state" "github.com/offchainlabs/nitro/arbutil" ) @@ -16,7 +17,9 @@ type ValidationInput struct { HasDelayedMsg bool DelayedMsgNr uint64 Preimages map[arbutil.PreimageType]map[common.Hash][]byte + UserWasms state.UserWasms BatchInfo []BatchInfo DelayedMsg []byte StartState GoGlobalState + DebugChain bool } diff --git a/validator/valnode/redis/consumer.go b/validator/valnode/redis/consumer.go new file mode 100644 index 0000000000..016f30bd61 --- /dev/null +++ b/validator/valnode/redis/consumer.go @@ -0,0 +1,157 @@ +package redis + +import ( + "context" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/pubsub" + "github.com/offchainlabs/nitro/util/redisutil" + "github.com/offchainlabs/nitro/util/stopwaiter" + "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/server_api" + "github.com/spf13/pflag" +) + +// ValidationServer implements consumer for the requests originated from +// RedisValidationClient producers. +type ValidationServer struct { + stopwaiter.StopWaiter + spawner validator.ValidationSpawner + + // consumers stores moduleRoot to consumer mapping. + consumers map[common.Hash]*pubsub.Consumer[*validator.ValidationInput, validator.GoGlobalState] + streamTimeout time.Duration +} + +func NewValidationServer(cfg *ValidationServerConfig, spawner validator.ValidationSpawner) (*ValidationServer, error) { + if cfg.RedisURL == "" { + return nil, fmt.Errorf("redis url cannot be empty") + } + redisClient, err := redisutil.RedisClientFromURL(cfg.RedisURL) + if err != nil { + return nil, err + } + consumers := make(map[common.Hash]*pubsub.Consumer[*validator.ValidationInput, validator.GoGlobalState]) + for _, hash := range cfg.ModuleRoots { + mr := common.HexToHash(hash) + c, err := pubsub.NewConsumer[*validator.ValidationInput, validator.GoGlobalState](redisClient, server_api.RedisStreamForRoot(mr), &cfg.ConsumerConfig) + if err != nil { + return nil, fmt.Errorf("creating consumer for validation: %w", err) + } + consumers[mr] = c + } + return &ValidationServer{ + consumers: consumers, + spawner: spawner, + streamTimeout: cfg.StreamTimeout, + }, nil +} + +func (s *ValidationServer) Start(ctx_in context.Context) { + s.StopWaiter.Start(ctx_in, s) + // Channel that all consumers use to indicate their readiness. + readyStreams := make(chan struct{}, len(s.consumers)) + for moduleRoot, c := range s.consumers { + c := c + moduleRoot := moduleRoot + c.Start(ctx_in) + // Channel for single consumer, once readiness is indicated in this, + // consumer will start consuming iteratively. + ready := make(chan struct{}, 1) + s.StopWaiter.LaunchThread(func(ctx context.Context) { + for { + if pubsub.StreamExists(ctx, c.StreamName(), c.RedisClient()) { + ready <- struct{}{} + readyStreams <- struct{}{} + return + } + select { + case <-ctx.Done(): + log.Info("Context done while checking redis stream existance", "error", ctx.Err().Error()) + return + case <-time.After(time.Millisecond * 100): + } + } + }) + s.StopWaiter.LaunchThread(func(ctx context.Context) { + select { + case <-ctx.Done(): + log.Info("Context done while waiting a redis stream to be ready", "error", ctx.Err().Error()) + return + case <-ready: // Wait until the stream exists and start consuming iteratively. + } + s.StopWaiter.CallIteratively(func(ctx context.Context) time.Duration { + req, err := c.Consume(ctx) + if err != nil { + log.Error("Consuming request", "error", err) + return 0 + } + if req == nil { + // There's nothing in the queue. + return time.Second + } + valRun := s.spawner.Launch(req.Value, moduleRoot) + res, err := valRun.Await(ctx) + if err != nil { + log.Error("Error validating", "request value", req.Value, "error", err) + return 0 + } + if err := c.SetResult(ctx, req.ID, res); err != nil { + log.Error("Error setting result for request", "id", req.ID, "result", res, "error", err) + return 0 + } + return time.Second + }) + }) + } + s.StopWaiter.LaunchThread(func(ctx context.Context) { + for { + select { + case <-readyStreams: + log.Trace("At least one stream is ready") + return // Don't block Start if at least one of the stream is ready. + case <-time.After(s.streamTimeout): + log.Error("Waiting for redis streams timed out") + case <-ctx.Done(): + log.Info("Context done while waiting redis streams to be ready, failed to start") + return + } + } + }) +} + +type ValidationServerConfig struct { + RedisURL string `koanf:"redis-url"` + ConsumerConfig pubsub.ConsumerConfig `koanf:"consumer-config"` + // Supported wasm module roots. + ModuleRoots []string `koanf:"module-roots"` + // Timeout on polling for existence of each redis stream. + StreamTimeout time.Duration `koanf:"stream-timeout"` +} + +var DefaultValidationServerConfig = ValidationServerConfig{ + RedisURL: "", + ConsumerConfig: pubsub.DefaultConsumerConfig, + ModuleRoots: []string{}, + StreamTimeout: 10 * time.Minute, +} + +var TestValidationServerConfig = ValidationServerConfig{ + RedisURL: "", + ConsumerConfig: pubsub.TestConsumerConfig, + ModuleRoots: []string{}, + StreamTimeout: time.Minute, +} + +func ValidationServerConfigAddOptions(prefix string, f *pflag.FlagSet) { + pubsub.ConsumerConfigAddOptions(prefix+".consumer-config", f) + f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") + f.Duration(prefix+".stream-timeout", DefaultValidationServerConfig.StreamTimeout, "Timeout on polling for existence of redis streams") +} + +func (cfg *ValidationServerConfig) Enabled() bool { + return cfg.RedisURL != "" +} diff --git a/validator/valnode/redis/consumer_test.go b/validator/valnode/redis/consumer_test.go new file mode 100644 index 0000000000..0ebd697f16 --- /dev/null +++ b/validator/valnode/redis/consumer_test.go @@ -0,0 +1,30 @@ +package redis + +import ( + "context" + "testing" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/util/redisutil" + "github.com/offchainlabs/nitro/util/testhelpers" +) + +func TestTimeout(t *testing.T) { + handler := testhelpers.InitTestLog(t, log.LevelInfo) + ctx, cancel := context.WithCancel(context.Background()) + redisURL := redisutil.CreateTestRedis(ctx, t) + TestValidationServerConfig.RedisURL = redisURL + TestValidationServerConfig.ModuleRoots = []string{"0x123"} + TestValidationServerConfig.StreamTimeout = 100 * time.Millisecond + vs, err := NewValidationServer(&TestValidationServerConfig, nil) + if err != nil { + t.Fatalf("NewValidationSever() unexpected error: %v", err) + } + vs.Start(ctx) + time.Sleep(time.Second) + if !handler.WasLogged("Waiting for redis streams timed out") { + t.Error("Expected message about stream time-outs was not logged") + } + cancel() +} diff --git a/validator/server_api/validation_api.go b/validator/valnode/validation_api.go similarity index 82% rename from validator/server_api/validation_api.go rename to validator/valnode/validation_api.go index ca5aafcee2..a67299b1a1 100644 --- a/validator/server_api/validation_api.go +++ b/validator/valnode/validation_api.go @@ -1,4 +1,7 @@ -package server_api +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package valnode import ( "context" @@ -12,11 +15,10 @@ import ( "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" + "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/server_arb" ) -const Namespace string = "validation" - type ValidationServerAPI struct { spawner validator.ValidationSpawner } @@ -29,8 +31,8 @@ func (a *ValidationServerAPI) Room() int { return a.spawner.Room() } -func (a *ValidationServerAPI) Validate(ctx context.Context, entry *ValidationInputJson, moduleRoot common.Hash) (validator.GoGlobalState, error) { - valInput, err := ValidationInputFromJson(entry) +func (a *ValidationServerAPI) Validate(ctx context.Context, entry *server_api.InputJSON, moduleRoot common.Hash) (validator.GoGlobalState, error) { + valInput, err := server_api.ValidationInputFromJson(entry) if err != nil { return validator.GoGlobalState{}, err } @@ -38,6 +40,10 @@ func (a *ValidationServerAPI) Validate(ctx context.Context, entry *ValidationInp return valRun.Await(ctx) } +func (a *ValidationServerAPI) WasmModuleRoots() ([]common.Hash, error) { + return a.spawner.WasmModuleRoots() +} + func NewValidationServerAPI(spawner validator.ValidationSpawner) *ValidationServerAPI { return &ValidationServerAPI{spawner} } @@ -69,8 +75,8 @@ func NewExecutionServerAPI(valSpawner validator.ValidationSpawner, execution val } } -func (a *ExecServerAPI) CreateExecutionRun(ctx context.Context, wasmModuleRoot common.Hash, jsonInput *ValidationInputJson) (uint64, error) { - input, err := ValidationInputFromJson(jsonInput) +func (a *ExecServerAPI) CreateExecutionRun(ctx context.Context, wasmModuleRoot common.Hash, jsonInput *server_api.InputJSON) (uint64, error) { + input, err := server_api.ValidationInputFromJson(jsonInput) if err != nil { return 0, err } @@ -107,8 +113,8 @@ func (a *ExecServerAPI) Start(ctx_in context.Context) { a.CallIteratively(a.removeOldRuns) } -func (a *ExecServerAPI) WriteToFile(ctx context.Context, jsonInput *ValidationInputJson, expOut validator.GoGlobalState, moduleRoot common.Hash) error { - input, err := ValidationInputFromJson(jsonInput) +func (a *ExecServerAPI) WriteToFile(ctx context.Context, jsonInput *server_api.InputJSON, expOut validator.GoGlobalState, moduleRoot common.Hash) error { + input, err := server_api.ValidationInputFromJson(jsonInput) if err != nil { return err } @@ -129,7 +135,7 @@ func (a *ExecServerAPI) getRun(id uint64) (validator.ExecutionRun, error) { return entry.run, nil } -func (a *ExecServerAPI) GetStepAt(ctx context.Context, execid uint64, position uint64) (*MachineStepResultJson, error) { +func (a *ExecServerAPI) GetStepAt(ctx context.Context, execid uint64, position uint64) (*server_api.MachineStepResultJson, error) { run, err := a.getRun(execid) if err != nil { return nil, err @@ -139,7 +145,7 @@ func (a *ExecServerAPI) GetStepAt(ctx context.Context, execid uint64, position u if err != nil { return nil, err } - return MachineStepResultToJson(res), nil + return server_api.MachineStepResultToJson(res), nil } func (a *ExecServerAPI) GetProofAt(ctx context.Context, execid uint64, position uint64) (string, error) { diff --git a/validator/valnode/valnode.go b/validator/valnode/valnode.go index ca954094ff..972e11189d 100644 --- a/validator/valnode/valnode.go +++ b/validator/valnode/valnode.go @@ -5,14 +5,15 @@ import ( "github.com/offchainlabs/nitro/validator" + "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" - flag "github.com/spf13/pflag" - "github.com/offchainlabs/nitro/validator/server_api" "github.com/offchainlabs/nitro/validator/server_arb" "github.com/offchainlabs/nitro/validator/server_common" "github.com/offchainlabs/nitro/validator/server_jit" + "github.com/offchainlabs/nitro/validator/valnode/redis" + "github.com/spf13/pflag" ) type WasmConfig struct { @@ -21,10 +22,10 @@ type WasmConfig struct { AllowedWasmModuleRoots []string `koanf:"allowed-wasm-module-roots"` } -func WasmConfigAddOptions(prefix string, f *flag.FlagSet) { +func WasmConfigAddOptions(prefix string, f *pflag.FlagSet) { f.String(prefix+".root-path", DefaultWasmConfig.RootPath, "path to machine folders, each containing wasm files (machine.wavm.br, replay.wasm)") f.Bool(prefix+".enable-wasmroots-check", DefaultWasmConfig.EnableWasmrootsCheck, "enable check for compatibility of on-chain WASM module root with node") - f.StringSlice(prefix+".allowed-wasm-module-roots", DefaultWasmConfig.AllowedWasmModuleRoots, "list of WASM module roots to check if the on-chain WASM module root belongs to on node startup") + f.StringSlice(prefix+".allowed-wasm-module-roots", DefaultWasmConfig.AllowedWasmModuleRoots, "list of WASM module roots or mahcine base paths to match against on-chain WasmModuleRoot") } var DefaultWasmConfig = WasmConfig{ @@ -62,7 +63,7 @@ var TestValidationConfig = Config{ Wasm: DefaultWasmConfig, } -func ValidationConfigAddOptions(prefix string, f *flag.FlagSet) { +func ValidationConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".use-jit", DefaultValidationConfig.UseJit, "use jit for validation") f.Bool(prefix+".api-auth", DefaultValidationConfig.ApiAuth, "validate is an authenticated API") f.Bool(prefix+".api-public", DefaultValidationConfig.ApiPublic, "validate is a public API") @@ -75,6 +76,8 @@ type ValidationNode struct { config ValidationConfigFetcher arbSpawner *server_arb.ArbitratorSpawner jitSpawner *server_jit.JitSpawner + + redisConsumer *redis.ValidationServer } func EnsureValidationExposedViaAuthRPC(stackConf *node.Config) { @@ -103,7 +106,7 @@ func CreateValidationNode(configFetcher ValidationConfigFetcher, stack *node.Nod if err != nil { return nil, err } - var serverAPI *server_api.ExecServerAPI + var serverAPI *ExecServerAPI var jitSpawner *server_jit.JitSpawner if config.UseJit { jitConfigFetcher := func() *server_jit.JitSpawnerConfig { return &configFetcher().Jit } @@ -112,9 +115,17 @@ func CreateValidationNode(configFetcher ValidationConfigFetcher, stack *node.Nod if err != nil { return nil, err } - serverAPI = server_api.NewExecutionServerAPI(jitSpawner, arbSpawner, arbConfigFetcher) + serverAPI = NewExecutionServerAPI(jitSpawner, arbSpawner, arbConfigFetcher) } else { - serverAPI = server_api.NewExecutionServerAPI(arbSpawner, arbSpawner, arbConfigFetcher) + serverAPI = NewExecutionServerAPI(arbSpawner, arbSpawner, arbConfigFetcher) + } + var redisConsumer *redis.ValidationServer + redisValidationConfig := arbConfigFetcher().RedisValidationServerConfig + if redisValidationConfig.Enabled() { + redisConsumer, err = redis.NewValidationServer(&redisValidationConfig, arbSpawner) + if err != nil { + log.Error("Creating new redis validation server", "error", err) + } } valAPIs := []rpc.API{{ Namespace: server_api.Namespace, @@ -125,7 +136,7 @@ func CreateValidationNode(configFetcher ValidationConfigFetcher, stack *node.Nod }} stack.RegisterAPIs(valAPIs) - return &ValidationNode{configFetcher, arbSpawner, jitSpawner}, nil + return &ValidationNode{configFetcher, arbSpawner, jitSpawner, redisConsumer}, nil } func (v *ValidationNode) Start(ctx context.Context) error { @@ -137,6 +148,9 @@ func (v *ValidationNode) Start(ctx context.Context) error { return err } } + if v.redisConsumer != nil { + v.redisConsumer.Start(ctx) + } return nil } diff --git a/wavmio/higher.go b/wavmio/higher.go index 81fa4a5e3e..0fb5516c1a 100644 --- a/wavmio/higher.go +++ b/wavmio/higher.go @@ -1,12 +1,14 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -//go:build js -// +build js +//go:build wasm +// +build wasm package wavmio import ( + "unsafe" + "github.com/ethereum/go-ethereum/common" "github.com/offchainlabs/nitro/arbutil" ) @@ -22,14 +24,14 @@ const IDX_SEND_ROOT = 1 const IDX_INBOX_POSITION = 0 const IDX_POSITION_WITHIN_MESSAGE = 1 -func readBuffer(f func(uint32, []byte) uint32) []byte { +func readBuffer(f func(uint32, unsafe.Pointer) uint32) []byte { buf := make([]byte, 0, INITIAL_CAPACITY) offset := 0 for { if len(buf) < offset+QUERY_SIZE { buf = append(buf, make([]byte, offset+QUERY_SIZE-len(buf))...) } - read := f(uint32(offset), buf[offset:(offset+QUERY_SIZE)]) + read := f(uint32(offset), unsafe.Pointer(&buf[offset])) offset += int(read) if read < QUERY_SIZE { buf = buf[:offset] @@ -43,18 +45,19 @@ func StubInit() {} func StubFinal() {} func GetLastBlockHash() (hash common.Hash) { - getGlobalStateBytes32(IDX_LAST_BLOCKHASH, hash[:]) + hashUnsafe := unsafe.Pointer(&hash[0]) + getGlobalStateBytes32(IDX_LAST_BLOCKHASH, hashUnsafe) return } func ReadInboxMessage(msgNum uint64) []byte { - return readBuffer(func(offset uint32, buf []byte) uint32 { + return readBuffer(func(offset uint32, buf unsafe.Pointer) uint32 { return readInboxMessage(msgNum, offset, buf) }) } func ReadDelayedInboxMessage(seqNum uint64) []byte { - return readBuffer(func(offset uint32, buf []byte) uint32 { + return readBuffer(func(offset uint32, buf unsafe.Pointer) uint32 { return readDelayedInboxMessage(seqNum, offset, buf) }) } @@ -65,18 +68,21 @@ func AdvanceInboxMessage() { } func ResolveTypedPreimage(ty arbutil.PreimageType, hash common.Hash) ([]byte, error) { - return readBuffer(func(offset uint32, buf []byte) uint32 { - return resolveTypedPreimage(uint8(ty), hash[:], offset, buf) + return readBuffer(func(offset uint32, buf unsafe.Pointer) uint32 { + hashUnsafe := unsafe.Pointer(&hash[0]) + return resolveTypedPreimage(uint32(ty), hashUnsafe, offset, buf) }), nil } func SetLastBlockHash(hash [32]byte) { - setGlobalStateBytes32(IDX_LAST_BLOCKHASH, hash[:]) + hashUnsafe := unsafe.Pointer(&hash[0]) + setGlobalStateBytes32(IDX_LAST_BLOCKHASH, hashUnsafe) } // Note: if a GetSendRoot is ever modified, the validator will need to fill in the previous send root, which it currently does not. func SetSendRoot(hash [32]byte) { - setGlobalStateBytes32(IDX_SEND_ROOT, hash[:]) + hashUnsafe := unsafe.Pointer(&hash[0]) + setGlobalStateBytes32(IDX_SEND_ROOT, hashUnsafe) } func GetPositionWithinMessage() uint64 { diff --git a/wavmio/raw.go b/wavmio/raw.go index f0462cbbe3..c09543f84f 100644 --- a/wavmio/raw.go +++ b/wavmio/raw.go @@ -1,15 +1,30 @@ -// Copyright 2021-2022, Offchain Labs, Inc. +// Copyright 2021-2024, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -//go:build js -// +build js +//go:build wasm +// +build wasm package wavmio -func getGlobalStateBytes32(idx uint64, output []byte) -func setGlobalStateBytes32(idx uint64, val []byte) -func getGlobalStateU64(idx uint64) uint64 -func setGlobalStateU64(idx uint64, val uint64) -func readInboxMessage(msgNum uint64, offset uint32, output []byte) uint32 -func readDelayedInboxMessage(seqNum uint64, offset uint32, output []byte) uint32 -func resolveTypedPreimage(ty uint8, hash []byte, offset uint32, output []byte) uint32 +import "unsafe" + +//go:wasmimport wavmio getGlobalStateBytes32 +func getGlobalStateBytes32(idx uint32, output unsafe.Pointer) + +//go:wasmimport wavmio setGlobalStateBytes32 +func setGlobalStateBytes32(idx uint32, val unsafe.Pointer) + +//go:wasmimport wavmio getGlobalStateU64 +func getGlobalStateU64(idx uint32) uint64 + +//go:wasmimport wavmio setGlobalStateU64 +func setGlobalStateU64(idx uint32, val uint64) + +//go:wasmimport wavmio readInboxMessage +func readInboxMessage(msgNum uint64, offset uint32, output unsafe.Pointer) uint32 + +//go:wasmimport wavmio readDelayedInboxMessage +func readDelayedInboxMessage(seqNum uint64, offset uint32, output unsafe.Pointer) uint32 + +//go:wasmimport wavmio resolveTypedPreimage +func resolveTypedPreimage(ty uint32, hash unsafe.Pointer, offset uint32, output unsafe.Pointer) uint32 diff --git a/wavmio/raw.s b/wavmio/raw.s deleted file mode 100644 index 7347d13394..0000000000 --- a/wavmio/raw.s +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2021-2022, Offchain Labs, Inc. -// For license information, see https://github.com/nitro/blob/master/LICENSE - -//go:build js -// +build js - -#include "textflag.h" - -TEXT ·getGlobalStateBytes32(SB), NOSPLIT, $0 - CallImport - RET - -TEXT ·setGlobalStateBytes32(SB), NOSPLIT, $0 - CallImport - RET - -TEXT ·getGlobalStateU64(SB), NOSPLIT, $0 - CallImport - RET - -TEXT ·setGlobalStateU64(SB), NOSPLIT, $0 - CallImport - RET - -TEXT ·readInboxMessage(SB), NOSPLIT, $0 - CallImport - RET - -TEXT ·readDelayedInboxMessage(SB), NOSPLIT, $0 - CallImport - RET - -TEXT ·resolveTypedPreimage(SB), NOSPLIT, $0 - CallImport - RET diff --git a/wavmio/stub.go b/wavmio/stub.go index 0893f35250..7fd29e2062 100644 --- a/wavmio/stub.go +++ b/wavmio/stub.go @@ -1,8 +1,8 @@ // Copyright 2021-2022, Offchain Labs, Inc. // For license information, see https://github.com/nitro/blob/master/LICENSE -//go:build !js -// +build !js +//go:build !wasm +// +build !wasm package wavmio diff --git a/wsbroadcastserver/clientconnection.go b/wsbroadcastserver/clientconnection.go index 6f5bf54e4d..ba70756c98 100644 --- a/wsbroadcastserver/clientconnection.go +++ b/wsbroadcastserver/clientconnection.go @@ -302,7 +302,7 @@ func (cc *ClientConnection) Receive(ctx context.Context, timeout time.Duration) return msg, op, err } -// readRequests reads json-rpc request from connection. +// readRequest reads json-rpc request from connection. func (cc *ClientConnection) readRequest(ctx context.Context, timeout time.Duration) ([]byte, ws.OpCode, error) { cc.ioMutex.Lock() defer cc.ioMutex.Unlock()