diff --git a/.dockerignore b/.dockerignore index e142afd073..51424900e8 100644 --- a/.dockerignore +++ b/.dockerignore @@ -9,6 +9,7 @@ go-ethereum/tests **/*.yml contracts/build contracts/cache/ +safe-smart-account/build/ solgen/go **/node_modules diff --git a/.github/workflows/arbitrator-ci.yml b/.github/workflows/arbitrator-ci.yml index 3ec3327392..392eb876c0 100644 --- a/.github/workflows/arbitrator-ci.yml +++ b/.github/workflows/arbitrator-ci.yml @@ -71,17 +71,20 @@ jobs: - name: Install rust stable uses: dtolnay/rust-toolchain@stable with: - toolchain: "1.76" + toolchain: 'stable' 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" + toolchain: 'nightly-2024-08-06' targets: 'wasm32-wasi, wasm32-unknown-unknown' components: 'rust-src, rustfmt, clippy' + - name: Set STYLUS_NIGHTLY_VER environment variable + run: echo "STYLUS_NIGHTLY_VER=+$(rustup toolchain list | grep '^nightly' | head -n1 | cut -d' ' -f1)" >> "$GITHUB_ENV" + - name: Cache Rust intermediate build products uses: actions/cache@v3 with: @@ -156,28 +159,19 @@ jobs: run: echo "$HOME/wabt-prefix/bin" >> "$GITHUB_PATH" - name: Make arbitrator libraries - run: make -j wasm-ci-build STYLUS_NIGHTLY_VER="+nightly-2024-02-04" + run: make -j wasm-ci-build - name: Clippy check run: cargo clippy --all --manifest-path arbitrator/Cargo.toml -- -D warnings - name: Run rust tests - uses: actions-rs/cargo@v1 - with: - command: test - args: -p arbutil -p prover -p jit -p stylus --release --manifest-path arbitrator/prover/Cargo.toml + run: cargo test -p arbutil -p prover -p jit -p stylus --release --manifest-path arbitrator/prover/Cargo.toml - name: Rustfmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: -p arbutil -p prover -p jit -p stylus --manifest-path arbitrator/Cargo.toml -- --check + run: cargo fmt -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 + run: cargo fmt --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 cc5cd68a90..acd6295b7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,25 +56,23 @@ jobs: - name: Install rust stable uses: dtolnay/rust-toolchain@stable with: - targets: 'wasm32-unknown-unknown, wasm32-wasi' - - - name: Install Foundry - uses: foundry-rs/foundry-toolchain@v1 + toolchain: 'stable' + targets: 'wasm32-wasi, wasm32-unknown-unknown' + components: 'llvm-tools-preview, rustfmt, clippy' - name: Install rust nightly - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@nightly id: install-rust-nightly with: - profile: minimal - toolchain: "nightly" + toolchain: 'nightly-2024-08-06' + targets: 'wasm32-wasi, wasm32-unknown-unknown' + components: 'rust-src, rustfmt, clippy' - - name: Install rust wasm targets - run: rustup target add wasm32-wasi wasm32-unknown-unknown + - name: Set STYLUS_NIGHTLY_VER environment variable + run: echo "STYLUS_NIGHTLY_VER=+$(rustup toolchain list | grep '^nightly' | head -n1 | cut -d' ' -f1)" >> "$GITHUB_ENV" - - name: Install nightly wasm targets - run: | - rustup component add rust-src --toolchain nightly - rustup target add wasm32-unknown-unknown --toolchain nightly + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 - name: Cache Build Products uses: actions/cache@v3 @@ -140,18 +138,59 @@ jobs: echo "TMPDIR=$(pwd)/target/tmp/deadbeefbee" >> "$GITHUB_ENV" echo "GOMEMLIMIT=6GiB" >> "$GITHUB_ENV" echo "GOGC=80" >> "$GITHUB_ENV" + echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> "$GITHUB_ENV" - - name: run tests without race detection + - name: run tests without race detection and path state scheme if: matrix.test-mode == 'defaults' + env: + TEST_STATE_SCHEME: path run: | packages=`go list ./...` - 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 + for package in $packages; do + echo running tests for $package + if ! stdbuf -oL gotestsum --format short-verbose --packages="$package" --rerun-fails=2 --no-color=false -- -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -timeout 20m -tags=cionly > >(stdbuf -oL tee -a full.log | grep -vE "INFO|seal"); then + exit 1 + fi + done + + - name: run tests without race detection and hash state scheme + if: matrix.test-mode == 'defaults' + env: + TEST_STATE_SCHEME: hash + run: | + packages=`go list ./...` + for package in $packages; do + echo running tests for $package + if ! stdbuf -oL gotestsum --format short-verbose --packages="$package" --rerun-fails=2 --no-color=false -- -timeout 20m -tags=cionly; then + exit 1 + fi + done + + - name: run tests with race detection and path state scheme if: matrix.test-mode == 'race' + env: + TEST_STATE_SCHEME: path run: | packages=`go list ./...` - 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") + for package in $packages; do + echo running tests for $package + if ! stdbuf -oL gotestsum --format short-verbose --packages="$package" --rerun-fails=2 --no-color=false -- -race -timeout 30m > >(stdbuf -oL tee -a full.log | grep -vE "INFO|seal"); then + exit 1 + fi + done + + - name: run tests with race detection and hash state scheme + if: matrix.test-mode == 'race' + env: + TEST_STATE_SCHEME: hash + run: | + packages=`go list ./...` + for package in $packages; do + echo running tests for $package + if ! stdbuf -oL gotestsum --format short-verbose --packages="$package" --rerun-fails=2 --no-color=false -- -race -timeout 30m; then + exit 1 + fi + done - name: run redis tests if: matrix.test-mode == 'defaults' @@ -161,19 +200,34 @@ jobs: if: matrix.test-mode == 'challenge' run: | packages=`go list ./...` - 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") + for package in $packages; do + echo running tests for $package + if ! stdbuf -oL gotestsum --format short-verbose --packages="$package" --rerun-fails=2 --no-color=false -- -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -tags=challengetest -run=TestChallenge > >(stdbuf -oL tee -a full.log | grep -vE "INFO|seal"); then + exit 1 + fi + done - 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") + for package in $packages; do + echo running tests for $package + if ! stdbuf -oL gotestsum --format short-verbose --packages="$package" --rerun-fails=2 --no-color=false -- -timeout 60m -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -tags=stylustest -run="TestProgramArbitrator" > >(stdbuf -oL tee -a full.log | grep -vE "INFO|seal"); then + exit 1 + fi + done - 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") + for package in $packages; do + echo running tests for $package + if ! stdbuf -oL gotestsum --format short-verbose --packages="$package" --rerun-fails=2 --no-color=false -- -timeout 60m -coverprofile=coverage.txt -covermode=atomic -coverpkg=./...,./go-ethereum/... -tags=stylustest -run="TestProgramLong" > >(stdbuf -oL tee -a full.log | grep -vE "INFO|seal"); then + exit 1 + fi + done - name: Archive detailed run log uses: actions/upload-artifact@v3 @@ -189,4 +243,3 @@ jobs: files: ./coverage.txt,./coverage-redis.txt verbose: false token: ${{ secrets.CODECOV_TOKEN }} - diff --git a/.github/workflows/merge-checks.yml b/.github/workflows/merge-checks.yml index 6561c429e2..c9f7957389 100644 --- a/.github/workflows/merge-checks.yml +++ b/.github/workflows/merge-checks.yml @@ -17,7 +17,7 @@ jobs: run: | set -x pipefail status_state="pending" - if ${{ contains(github.event.*.labels.*.name, 'design-approved') }}; then + if ${{ contains(github.event.pull_request.labels.*.name, 'design-approved') && !contains(github.event.pull_request.labels.*.name, 'after-next-version') }}; then status_state="success" else resp="$(curl -sSL --fail-with-body \ diff --git a/.gitmodules b/.gitmodules index 075c986df6..4804393139 100644 --- a/.gitmodules +++ b/.gitmodules @@ -32,3 +32,6 @@ [submodule "arbitrator/langs/bf"] path = arbitrator/langs/bf url = https://github.com/OffchainLabs/stylus-sdk-bf.git +[submodule "safe-smart-account"] + path = safe-smart-account + url = https://github.com/safe-global/safe-smart-account.git diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000000..3c032078a4 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18 diff --git a/Dockerfile b/Dockerfile index 73c950afaf..cfde0ba918 100644 --- a/Dockerfile +++ b/Dockerfile @@ -32,6 +32,8 @@ WORKDIR /workspace COPY contracts/package.json contracts/yarn.lock contracts/ RUN cd contracts && yarn install COPY contracts contracts/ +COPY safe-smart-account safe-smart-account/ +RUN cd safe-smart-account && yarn install COPY Makefile . RUN . ~/.bashrc && NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-solidity @@ -43,8 +45,8 @@ FROM wasm-base AS wasm-libs-builder # clang / lld used by soft-float wasm 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 + # pinned rust 1.80.1 +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.80.1 --target x86_64-unknown-linux-gnu wasm32-unknown-unknown wasm32-wasi COPY ./Makefile ./ COPY arbitrator/Cargo.* arbitrator/ COPY arbitrator/arbutil arbitrator/arbutil @@ -82,6 +84,7 @@ COPY ./wavmio ./wavmio COPY ./zeroheavy ./zeroheavy COPY ./contracts/src/precompiles/ ./contracts/src/precompiles/ COPY ./contracts/package.json ./contracts/yarn.lock ./contracts/ +COPY ./safe-smart-account ./safe-smart-account COPY ./solgen/gen.go ./solgen/ COPY ./fastcache ./fastcache COPY ./go-ethereum ./go-ethereum @@ -91,7 +94,7 @@ 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.75-slim-bookworm AS prover-header-builder +FROM rust:1.80.1-slim-bookworm AS prover-header-builder WORKDIR /workspace RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -100,6 +103,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ COPY arbitrator/Cargo.* arbitrator/ COPY ./Makefile ./ COPY arbitrator/arbutil arbitrator/arbutil +COPY arbitrator/bench arbitrator/bench COPY arbitrator/brotli arbitrator/brotli COPY arbitrator/caller-env arbitrator/caller-env COPY arbitrator/prover arbitrator/prover @@ -116,7 +120,7 @@ RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-prover-header FROM scratch AS prover-header-export COPY --from=prover-header-builder /workspace/target/ / -FROM rust:1.75-slim-bookworm AS prover-builder +FROM rust:1.80.1-slim-bookworm AS prover-builder WORKDIR /workspace RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -128,9 +132,12 @@ RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \ COPY --from=brotli-library-export / target/ COPY arbitrator/Cargo.* arbitrator/ COPY arbitrator/arbutil arbitrator/arbutil +COPY arbitrator/bench arbitrator/bench COPY arbitrator/brotli arbitrator/brotli COPY arbitrator/caller-env arbitrator/caller-env COPY arbitrator/prover/Cargo.toml arbitrator/prover/ +COPY arbitrator/prover/benches arbitrator/prover/benches +COPY arbitrator/bench/Cargo.toml arbitrator/bench/ COPY arbitrator/jit/Cargo.toml arbitrator/jit/ COPY arbitrator/stylus/Cargo.toml arbitrator/stylus/ COPY arbitrator/tools/wasmer arbitrator/tools/wasmer @@ -138,11 +145,15 @@ COPY arbitrator/wasm-libraries/user-host-trait/Cargo.toml arbitrator/wasm-librar 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/bench/src/lib.rs && \ + echo "fn test() {}" > arbitrator/prover/benches/merkle_bench.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/prover/src/lib.rs arbitrator/jit/src/lib.rs arbitrator/stylus/src/lib.rs && \ - rm arbitrator/wasm-libraries/user-host-trait/src/lib.rs + rm arbitrator/wasm-libraries/user-host-trait/src/lib.rs && \ + rm arbitrator/prover/benches/merkle_bench.rs && \ + rm arbitrator/bench/src/lib.rs COPY ./Makefile ./ COPY arbitrator/prover arbitrator/prover COPY arbitrator/wasm-libraries arbitrator/wasm-libraries @@ -179,6 +190,7 @@ COPY ./Makefile ./ COPY ./arbitrator ./arbitrator COPY ./solgen ./solgen COPY ./contracts ./contracts +COPY ./safe-smart-account ./safe-smart-account RUN NITRO_BUILD_IGNORE_TIMESTAMPS=1 make build-replay-env FROM debian:bookworm-slim AS machine-versions @@ -226,6 +238,7 @@ COPY . ./ COPY --from=contracts-builder workspace/contracts/build/ contracts/build/ COPY --from=contracts-builder workspace/contracts/out/ contracts/out/ COPY --from=contracts-builder workspace/contracts/node_modules/@offchainlabs/upgrade-executor/build/contracts/src/UpgradeExecutor.sol/UpgradeExecutor.json contracts/node_modules/@offchainlabs/upgrade-executor/build/contracts/src/UpgradeExecutor.sol/ +COPY --from=contracts-builder workspace/safe-smart-account/build/ safe-smart-account/build/ COPY --from=contracts-builder workspace/.make/ .make/ COPY --from=prover-header-export / target/ COPY --from=brotli-library-export / target/ @@ -250,7 +263,12 @@ COPY --from=node-builder /workspace/target/bin/nitro /usr/local/bin/ COPY --from=node-builder /workspace/target/bin/relay /usr/local/bin/ COPY --from=node-builder /workspace/target/bin/nitro-val /usr/local/bin/ COPY --from=node-builder /workspace/target/bin/seq-coordinator-manager /usr/local/bin/ +COPY --from=node-builder /workspace/target/bin/prover /usr/local/bin/ +COPY --from=node-builder /workspace/target/bin/dbconv /usr/local/bin/ +COPY ./scripts/convert-databases.bash /usr/local/bin/ COPY --from=machine-versions /workspace/machines /home/user/target/machines +COPY ./scripts/validate-wasm-module-root.sh . +RUN ./validate-wasm-module-root.sh /home/user/target/machines /usr/local/bin/prover USER root RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ diff --git a/Makefile b/Makefile index 11ebc82aa4..fb1f9c4011 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,8 @@ 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) 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_cargo = $(wasm_lib)/.cargo/config.toml +wasm_lib_deps = $(wildcard $(wasm_lib)/$(1)/*.toml $(wasm_lib)/$(1)/src/*.rs $(wasm_lib)/$(1)/*.rs) $(wasm_lib_cargo) $(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) @@ -146,18 +147,24 @@ stylus_benchmarks = $(wildcard $(stylus_dir)/*.toml $(stylus_dir)/src/*.rs) $(st # user targets +.PHONY: push push: lint test-go .make/fmt @printf "%bdone building %s%b\n" $(color_pink) $$(expr $$(echo $? | wc -w) - 1) $(color_reset) @printf "%bready for push!%b\n" $(color_pink) $(color_reset) +.PHONY: all all: build build-replay-env test-gen-proofs @touch .make/all +.PHONY: build build: $(patsubst %,$(output_root)/bin/%, nitro relay daserver datool seq-coordinator-invalidate nitro-val seq-coordinator-manager) +# build: $(patsubst %,$(output_root)/bin/%, nitro deploy relay daserver datool seq-coordinator-invalidate nitro-val seq-coordinator-manager dbconv) @printf $(done) +.PHONY: build-node-deps build-node-deps: $(go_source) build-prover-header build-prover-lib build-jit .make/solgen .make/cbrotli-lib +.PHONY: test-go-deps test-go-deps: \ build-replay-env \ $(stylus_test_wasms) \ @@ -165,59 +172,95 @@ test-go-deps: \ $(arbitrator_generated_header) \ $(patsubst %,$(arbitrator_cases)/%.wasm, global-state read-inboxmsg-10 global-state-wrapper const) +.PHONY: build-prover-header build-prover-header: $(arbitrator_generated_header) +.PHONY: build-prover-lib build-prover-lib: $(arbitrator_stylus_lib) +.PHONY: build-prover-bin build-prover-bin: $(prover_bin) +.PHONY: build-jit build-jit: $(arbitrator_jit) +.PHONY: build-replay-env build-replay-env: $(prover_bin) $(arbitrator_jit) $(arbitrator_wasm_libs) $(replay_wasm) $(output_latest)/machine.wavm.br +.PHONY: build-wasm-libs build-wasm-libs: $(arbitrator_wasm_libs) +.PHONY: build-wasm-bin build-wasm-bin: $(replay_wasm) +.PHONY: build-solidity build-solidity: .make/solidity +.PHONY: contracts contracts: .make/solgen @printf $(done) +.PHONY: format fmt format fmt: .make/fmt @printf $(done) +.PHONY: lint lint: .make/lint @printf $(done) +.PHONY: stylus-benchmarks stylus-benchmarks: $(stylus_benchmarks) cargo test --manifest-path $< --release --features benchmark benchmark_ -- --nocapture @printf $(done) +.PHONY: test-go test-go: .make/test-go @printf $(done) +.PHONY: test-go-challenge test-go-challenge: test-go-deps - go test -v -timeout 120m ./system_tests/... -run TestChallenge -tags challengetest + gotestsum --format short-verbose --no-color=false -- -timeout 120m ./system_tests/... -run TestChallenge -tags challengetest @printf $(done) +.PHONY: test-go-stylus test-go-stylus: test-go-deps - go test -v -timeout 120m ./system_tests/... -run TestProgramArbitrator -tags stylustest + gotestsum --format short-verbose --no-color=false -- -timeout 120m ./system_tests/... -run TestProgramArbitrator -tags stylustest @printf $(done) +.PHONY: test-go-redis test-go-redis: test-go-deps - TEST_REDIS=redis://localhost:6379/0 go test -p 1 -run TestRedis ./system_tests/... ./arbnode/... + TEST_REDIS=redis://localhost:6379/0 gotestsum --format short-verbose --no-color=false -- -p 1 -run TestRedis ./system_tests/... ./arbnode/... @printf $(done) +.PHONY: test-gen-proofs 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 + @printf $(done) + +.PHONY: test-rust +test-rust: .make/test-rust + @printf $(done) + +# Runs the fastest and most reliable and high-value tests. +.PHONY: tests +tests: test-go test-rust + @printf $(done) +# Runs all tests, including slow and unreliable tests. +# Currently, NOT including: +# - test-go-redis (These testts require additional setup and are not as reliable) +.PHONY: tests-all +tests-all: tests test-go-challenge test-go-stylus test-gen-proofs + @printf $(done) + +.PHONY: wasm-ci-build wasm-ci-build: $(arbitrator_wasm_libs) $(arbitrator_test_wasms) $(stylus_test_wasms) $(output_latest)/user_test.wasm @printf $(done) +.PHONY: clean clean: go clean -testcache rm -rf $(arbitrator_cases)/rust/target @@ -236,6 +279,7 @@ clean: @rm -rf contracts/build contracts/cache solgen/go/ @rm -f .make/* +.PHONY: docker docker: docker build -t nitro-node-slim --target nitro-node-slim . docker build -t nitro-node --target nitro-node . @@ -264,6 +308,9 @@ $(output_root)/bin/nitro-val: $(DEP_PREDICATE) build-node-deps $(output_root)/bin/seq-coordinator-manager: $(DEP_PREDICATE) build-node-deps go build $(GOLANG_PARAMS) -o $@ "$(CURDIR)/cmd/seq-coordinator-manager" +$(output_root)/bin/dbconv: $(DEP_PREDICATE) build-node-deps + go build $(GOLANG_PARAMS) -o $@ "$(CURDIR)/cmd/dbconv" + # recompile wasm, but don't change timestamp unless files differ $(replay_wasm): $(DEP_PREDICATE) $(go_source) .make/solgen mkdir -p `dirname $(replay_wasm)` @@ -284,8 +331,8 @@ $(arbitrator_jit): $(DEP_PREDICATE) $(jit_files) cargo build --manifest-path arbitrator/Cargo.toml --release -p jit ${CARGOFLAGS} install arbitrator/target/release/jit $@ -$(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)/rust/$(wasm32_wasi)/%.wasm: $(arbitrator_cases)/rust/src/bin/%.rs $(arbitrator_cases)/rust/src/lib.rs $(arbitrator_cases)/rust/.cargo/config.toml + cargo build --manifest-path $(arbitrator_cases)/rust/Cargo.toml --release --target wasm32-wasi --config $(arbitrator_cases)/rust/.cargo/config.toml --bin $(patsubst $(arbitrator_cases)/rust/$(wasm32_wasi)/%.wasm,%, $@) $(arbitrator_cases)/go/testcase.wasm: $(arbitrator_cases)/go/*.go .make/solgen cd $(arbitrator_cases)/go && GOOS=wasip1 GOARCH=wasm go build -o testcase.wasm @@ -297,7 +344,7 @@ $(arbitrator_generated_header): $(DEP_PREDICATE) $(stylus_files) @touch -c $@ # cargo might decide to not rebuild the header $(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 + cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-unknown-unknown --config $(wasm_lib_cargo) --package wasi-stub install arbitrator/wasm-libraries/$(wasm32_unknown)/wasi_stub.wasm $@ arbitrator/wasm-libraries/soft-float/SoftFloat/build/Wasm-Clang/softfloat.a: $(DEP_PREDICATE) \ @@ -339,23 +386,23 @@ $(output_latest)/soft-float.wasm: $(DEP_PREDICATE) \ --export wavm__f64_promote_f32 $(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 + cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --config $(wasm_lib_cargo) --package host-io 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 + cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --config $(wasm_lib_cargo) --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 + cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --config $(wasm_lib_cargo) --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 + cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --config $(wasm_lib_cargo) --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 + cargo build --manifest-path arbitrator/wasm-libraries/Cargo.toml --release --target wasm32-wasi --config $(wasm_lib_cargo) --package arbcompress install arbitrator/wasm-libraries/$(wasm32_wasi)/arbcompress.wasm $@ $(output_latest)/forward.wasm: $(DEP_PREDICATE) $(wasm_lib_forward) .make/machines @@ -425,10 +472,10 @@ $(stylus_test_erc20_wasm): $(stylus_test_erc20_src) @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 + $(prover_bin) $< -l $(output_latest)/soft-float.wasm -o $@ -b --allow-hostapi --require-success 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 + $(prover_bin) $< -o $@ --allow-hostapi --require-success target/testdata/preimages.bin: mkdir -p `dirname $@` @@ -452,19 +499,19 @@ 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)) + $(prover_bin) $< -o $@ --allow-hostapi $(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 + $(prover_bin) $< -o $@ --allow-hostapi --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 + $(prover_bin) $< -o $@ --allow-hostapi --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 + $(prover_bin) $< -o $@ --allow-hostapi --stylus-modules $(arbitrator_cases)/user.wasm -b contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(prover_bin) - $(prover_bin) $< -o $@ --allow-hostapi --always-merkleize + $(prover_bin) $< -o $@ --allow-hostapi # strategic rules to minimize dependency building @@ -486,17 +533,23 @@ contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(prover_bin) gotestsum --format short-verbose --no-color=false @touch $@ +.make/test-rust: $(DEP_PREDICATE) wasm-ci-build $(ORDER_ONLY_PREDICATE) .make + cargo test --manifest-path arbitrator/Cargo.toml --release + @touch $@ + .make/solgen: $(DEP_PREDICATE) solgen/gen.go .make/solidity $(ORDER_ONLY_PREDICATE) .make mkdir -p solgen/go/ go run solgen/gen.go @touch $@ -.make/solidity: $(DEP_PREDICATE) contracts/src/*/*.sol .make/yarndeps $(ORDER_ONLY_PREDICATE) .make +.make/solidity: $(DEP_PREDICATE) safe-smart-account/contracts/*/*.sol safe-smart-account/contracts/*.sol contracts/src/*/*.sol .make/yarndeps $(ORDER_ONLY_PREDICATE) .make + yarn --cwd safe-smart-account build yarn --cwd contracts build yarn --cwd contracts build:forge:yul @touch $@ .make/yarndeps: $(DEP_PREDICATE) contracts/package.json contracts/yarn.lock $(ORDER_ONLY_PREDICATE) .make + yarn --cwd safe-smart-account install yarn --cwd contracts install @touch $@ @@ -531,4 +584,3 @@ contracts/test/prover/proofs/%.json: $(arbitrator_cases)/%.wasm $(prover_bin) 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 stylus-benchmarks test-go test-gen-proofs push clean docker diff --git a/README.md b/README.md index a07772628b..1f0e4ac81c 100644 --- a/README.md +++ b/README.md @@ -17,26 +17,26 @@ Logo Nitro is the latest iteration of the Arbitrum technology. It is a fully integrated, complete -layer 2 optimistic rollup system, including fraud proofs, the sequencer, the token bridges, +layer 2 optimistic rollup system, including fraud proofs, the sequencer, the token bridges, advanced calldata compression, and more. See the live docs-site [here](https://developer.arbitrum.io/) (or [here](https://github.com/OffchainLabs/arbitrum-docs) for markdown docs source.) -See [here](./audits) for security audit reports. +See [here](https://docs.arbitrum.io/audit-reports) for security audit reports. -The Nitro stack is built on several innovations. At its core is a new prover, which can do Arbitrum’s classic -interactive fraud proofs over WASM code. That means the L2 Arbitrum engine can be written and compiled using +The Nitro stack is built on several innovations. At its core is a new prover, which can do Arbitrum’s classic +interactive fraud proofs over WASM code. That means the L2 Arbitrum engine can be written and compiled using standard languages and tools, replacing the custom-designed language and compiler used in previous Arbitrum -versions. In normal execution, -validators and nodes run the Nitro engine compiled to native code, switching to WASM if a fraud proof is needed. -We compile the core of Geth, the EVM engine that practically defines the Ethereum standard, right into Arbitrum. +versions. In normal execution, +validators and nodes run the Nitro engine compiled to native code, switching to WASM if a fraud proof is needed. +We compile the core of Geth, the EVM engine that practically defines the Ethereum standard, right into Arbitrum. So the previous custom-built EVM emulator is replaced by Geth, the most popular and well-supported Ethereum client. -The last piece of the stack is a slimmed-down version of our ArbOS component, rewritten in Go, which provides the -rest of what’s needed to run an L2 chain: things like cross-chain communication, and a new and improved batching +The last piece of the stack is a slimmed-down version of our ArbOS component, rewritten in Go, which provides the +rest of what’s needed to run an L2 chain: things like cross-chain communication, and a new and improved batching and compression system to minimize L1 costs. -Essentially, Nitro runs Geth at layer 2 on top of Ethereum, and can prove fraud over the core engine of Geth +Essentially, Nitro runs Geth at layer 2 on top of Ethereum, and can prove fraud over the core engine of Geth compiled to WASM. Arbitrum One successfully migrated from the Classic Arbitrum stack onto Nitro on 8/31/22. (See [state migration](https://developer.arbitrum.io/migration/state-migration) and [dapp migration](https://developer.arbitrum.io/migration/dapp_migration) for more info). @@ -45,14 +45,12 @@ Arbitrum One successfully migrated from the Classic Arbitrum stack onto Nitro on 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. +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. -For those that prefer to deploy the Nitro software either directly on Ethereum (i.e. an L2) or have it settle to another Layer-2 on top of Ethereum, the [Arbitrum Expansion Program (the "AEP")](https://docs.arbitrum.foundation/assets/files/Arbitrum%20Expansion%20Program%20Jan182024-4f08b0c2cb476a55dc153380fa3e64b0.pdf) was recently established. The AEP allows for the permissionless deployment in the aforementioned fashion provided that 10% of net revenue (as more fully described in the AEP) is contributed back to the Arbitrum community in accordance with the requirements of the AEP. +For those that prefer to deploy the Nitro software either directly on Ethereum (i.e. an L2) or have it settle to another Layer-2 on top of Ethereum, the [Arbitrum Expansion Program (the "AEP")](https://docs.arbitrum.foundation/assets/files/Arbitrum%20Expansion%20Program%20Jan182024-4f08b0c2cb476a55dc153380fa3e64b0.pdf) was recently established. The AEP allows for the permissionless deployment in the aforementioned fashion provided that 10% of net revenue (as more fully described in the AEP) is contributed back to the Arbitrum community in accordance with the requirements of the AEP. ## Contact Discord - [Arbitrum](https://discord.com/invite/5KE54JwyTs) Twitter: [Arbitrum](https://twitter.com/arbitrum) - - diff --git a/arbcompress/native.go b/arbcompress/native.go index 4624d6222e..8244010979 100644 --- a/arbcompress/native.go +++ b/arbcompress/native.go @@ -12,7 +12,10 @@ package arbcompress #include "arbitrator.h" */ import "C" -import "fmt" +import ( + "errors" + "fmt" +) type u8 = C.uint8_t type u32 = C.uint32_t @@ -44,6 +47,8 @@ func Compress(input []byte, level uint32, dictionary Dictionary) ([]byte, error) return output, nil } +var ErrOutputWontFit = errors.New("output won't fit in maxsize") + func Decompress(input []byte, maxSize int) ([]byte, error) { return DecompressWithDictionary(input, maxSize, EmptyDictionary) } @@ -54,6 +59,9 @@ func DecompressWithDictionary(input []byte, maxSize int, dictionary Dictionary) inbuf := sliceToBuffer(input) status := C.brotli_decompress(inbuf, outbuf, C.Dictionary(dictionary)) + if status == C.BrotliStatus_NeedsMoreOutput { + return nil, ErrOutputWontFit + } if status != C.BrotliStatus_Success { return nil, fmt.Errorf("failed decompression: %d", status) } diff --git a/arbitrator/Cargo.lock b/arbitrator/Cargo.lock index a89dc5e97e..79a9117a31 100644 --- a/arbitrator/Cargo.lock +++ b/arbitrator/Cargo.lock @@ -37,7 +37,7 @@ dependencies = [ "cfg-if 1.0.0", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -55,6 +55,27 @@ version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "ansi_term" version = "0.12.1" @@ -64,6 +85,55 @@ dependencies = [ "winapi", ] +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "arbitrary" version = "1.3.2" @@ -114,19 +184,40 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.72" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17c6a35df3749d2e8bb1b7b21a976d82b15548788d2735b9d82f329268f71a11" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", "cfg-if 1.0.0", "libc", "miniz_oxide", - "object 0.35.0", + "object 0.36.2", "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bench" +version = "0.1.0" +dependencies = [ + "arbutil", + "clap 4.5.13", + "eyre", + "gperftools", + "hex", + "prover", + "serde", + "serde_json", + "serde_with 3.9.0", +] + [[package]] name = "bincode" version = "1.3.3" @@ -144,9 +235,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -156,6 +247,7 @@ checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", + "serde", "tap", "wyz", ] @@ -187,9 +279,9 @@ checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] name = "blst" -version = "0.3.11" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94087b935a822949d3291a9989ad2b2051ea141eda0fd4e478a75f6aa3e604b" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" dependencies = [ "cc", "glob", @@ -252,9 +344,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9" [[package]] name = "c-kzg" @@ -281,15 +373,20 @@ dependencies = [ "wasmer", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" -version = "1.0.98" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" dependencies = [ "jobserver", "libc", - "once_cell", ] [[package]] @@ -304,6 +401,46 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "serde", + "windows-targets", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "clap" version = "2.34.0" @@ -319,12 +456,64 @@ dependencies = [ "vec_map", ] +[[package]] +name = "clap" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_derive" +version = "4.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + [[package]] name = "convert_case" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "corosensei" version = "0.1.4" @@ -430,6 +619,42 @@ version = "0.91.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "393bc73c451830ff8dbb3a07f61843d6cb41a084f9996319917c0b291ed785bb" +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap 4.5.13", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + [[package]] name = "crossbeam-deque" version = "0.8.5" @@ -492,12 +717,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b2eb4d90d12bdda5ed17de686c2acb4c57914f8f921b8da7e112b5a36f3fe1" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.9", - "darling_macro 0.20.9", + "darling_core 0.20.10", + "darling_macro 0.20.10", ] [[package]] @@ -516,15 +741,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622687fe0bac72a04e5599029151f5796111b90f1baaa9b544d807a5e31cd120" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 2.0.66", + "strsim 0.11.1", + "syn 2.0.72", ] [[package]] @@ -540,13 +766,13 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "733cabb43482b1a1b53eee8583c2b9e8684d592215ea83efd305dd31bc2f0178" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.20.9", + "darling_core 0.20.10", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -562,6 +788,16 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + [[package]] name = "derivative" version = "2.2.0" @@ -575,15 +811,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -633,9 +869,9 @@ dependencies = [ [[package]] name = "either" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "enum-iterator" @@ -643,7 +879,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" dependencies = [ - "enum-iterator-derive", + "enum-iterator-derive 0.7.0", +] + +[[package]] +name = "enum-iterator" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c280b9e6b3ae19e152d8e31cf47f18389781e119d4013a2a2bb0180e5facc635" +dependencies = [ + "enum-iterator-derive 1.4.0", ] [[package]] @@ -657,25 +902,36 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "enum-iterator-derive" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "enumset" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.8.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" dependencies = [ - "darling 0.20.9", + "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -684,6 +940,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "backtrace", + "version_check", +] + [[package]] name = "eyre" version = "0.6.12" @@ -765,6 +1031,27 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gperftools" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20a3fc5818b1223ec628fc6998c8900486208b577f78c07500d4b52f983ebc9d" +dependencies = [ + "error-chain", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if 1.0.0", + "crunchy", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -793,6 +1080,12 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -813,6 +1106,32 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] [[package]] name = "ident_case" @@ -834,16 +1153,18 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown 0.14.5", + "serde", ] [[package]] @@ -871,6 +1192,23 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "is-terminal" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itertools" version = "0.10.5" @@ -912,9 +1250,9 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" dependencies = [ "libc", ] @@ -939,9 +1277,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "leb128" @@ -968,9 +1306,9 @@ dependencies = [ [[package]] name = "llvm-sys" -version = "150.1.3" +version = "150.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd60e740af945d99c2446a52e3ab8cdba2f740a40a16c51f6871bdea2abc687" +checksum = "88d6891afbe90a8be244f769dfe1db0b3c4880b8c44b12a6d0f7ab848d89b26d" dependencies = [ "cc", "lazy_static", @@ -991,15 +1329,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ "hashbrown 0.14.5", ] @@ -1024,9 +1362,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -1069,9 +1407,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -1119,9 +1457,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -1136,6 +1474,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.4.2" @@ -1144,7 +1488,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -1199,23 +1543,23 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -1229,9 +1573,9 @@ dependencies = [ [[package]] name = "object" -version = "0.35.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8ec7ab813848ba4522158d5517a6093db1ded27575b070f4177b8d12b41db5e" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" dependencies = [ "memchr", ] @@ -1242,6 +1586,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + [[package]] name = "opaque-debug" version = "0.3.1" @@ -1277,11 +1627,54 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +[[package]] +name = "pkg-config" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" + +[[package]] +name = "plotters" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" + +[[package]] +name = "plotters-svg" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" +dependencies = [ + "zerocopy 0.6.6", +] [[package]] name = "proc-macro-crate" @@ -1318,9 +1711,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -1331,10 +1724,13 @@ version = "0.1.0" dependencies = [ "arbutil", "bincode", + "bitvec", "brotli", "c-kzg", + "criterion", "derivative", "digest 0.9.0", + "enum-iterator 2.1.0", "eyre", "fnv", "hex", @@ -1349,11 +1745,12 @@ dependencies = [ "num-traits", "once_cell", "parking_lot", + "rand", "rayon", "rustc-demangle", "serde", "serde_json", - "serde_with", + "serde_with 1.14.0", "sha2 0.9.9", "sha3 0.9.1", "smallvec", @@ -1462,11 +1859,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", ] [[package]] @@ -1483,9 +1880,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", @@ -1495,9 +1892,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", @@ -1506,9 +1903,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "region" @@ -1600,6 +1997,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1626,9 +2032,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -1646,22 +2052,23 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.203" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1673,7 +2080,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ "serde", - "serde_with_macros", + "serde_with_macros 1.5.2", +] + +[[package]] +name = "serde_with" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.3.0", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros 3.9.0", + "time", ] [[package]] @@ -1688,6 +2113,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "serde_with_macros" +version = "3.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350" +dependencies = [ + "darling 0.20.10", + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "sha2" version = "0.9.9" @@ -1795,13 +2232,19 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "structopt" version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ - "clap", + "clap 2.34.0", "lazy_static", "structopt-derive", ] @@ -1812,7 +2255,7 @@ version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro-error", "proc-macro2", "quote", @@ -1861,9 +2304,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -1878,9 +2321,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.14" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "textwrap" @@ -1893,22 +2336,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -1920,6 +2363,37 @@ dependencies = [ "num_cpus", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tiny-keccak" version = "2.0.2" @@ -1929,11 +2403,21 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -1946,9 +2430,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml_datetime" -version = "0.6.6" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" @@ -1956,7 +2440,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.6", + "indexmap 2.3.0", "toml_datetime", "winnow", ] @@ -1980,7 +2464,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -2012,9 +2496,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "user-host-trait" @@ -2027,11 +2511,17 @@ dependencies = [ "ruint2", ] +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "uuid" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" [[package]] name = "vec_map" @@ -2041,9 +2531,19 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] [[package]] name = "wasi" @@ -2072,7 +2572,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -2094,7 +2594,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2148,7 +2648,7 @@ dependencies = [ "backtrace", "bytes", "cfg-if 1.0.0", - "enum-iterator", + "enum-iterator 0.7.0", "enumset", "lazy_static", "leb128", @@ -2237,7 +2737,7 @@ name = "wasmer-types" version = "4.2.8" dependencies = [ "bytecheck", - "enum-iterator", + "enum-iterator 0.7.0", "enumset", "indexmap 1.9.3", "more-asserts", @@ -2257,7 +2757,7 @@ dependencies = [ "crossbeam-queue", "dashmap", "derivative", - "enum-iterator", + "enum-iterator 0.7.0", "fnv", "indexmap 1.9.3", "lazy_static", @@ -2278,8 +2778,8 @@ version = "0.121.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" dependencies = [ - "bitflags 2.5.0", - "indexmap 2.2.6", + "bitflags 2.6.0", + "indexmap 2.3.0", "semver", ] @@ -2304,6 +2804,16 @@ dependencies = [ "wast", ] +[[package]] +name = "web-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "wee_alloc" version = "0.4.5" @@ -2332,12 +2842,30 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys 0.52.0", +] + [[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-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-sys" version = "0.33.0" @@ -2362,25 +2890,25 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.52.5", + "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -2390,9 +2918,9 @@ checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2402,15 +2930,15 @@ checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2420,9 +2948,9 @@ checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2432,15 +2960,15 @@ checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2450,9 +2978,9 @@ checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -2474,22 +3002,43 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" dependencies = [ - "zerocopy-derive", + "byteorder", + "zerocopy-derive 0.6.6", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy-derive" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] [[package]] @@ -2509,5 +3058,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.66", + "syn 2.0.72", ] diff --git a/arbitrator/Cargo.toml b/arbitrator/Cargo.toml index 138bdc2c69..94ca08b0b5 100644 --- a/arbitrator/Cargo.toml +++ b/arbitrator/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "arbutil", + "bench", "brotli", "brotli/fuzz", "caller-env", diff --git a/arbitrator/bench/Cargo.toml b/arbitrator/bench/Cargo.toml new file mode 100644 index 0000000000..3ab5b99b08 --- /dev/null +++ b/arbitrator/bench/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "bench" +version = "0.1.0" +edition = "2021" + +[lib] +name = "bench" +path = "src/lib.rs" + +[[bin]] +name = "benchbin" +path = "src/bin.rs" + +[dependencies] +hex = { version = "0.4.3", features = ["serde"] } +eyre = "0.6.5" +prover = { path = "../prover" } +arbutil = { path = "../arbutil" } +clap = { version = "4.4.8", features = ["derive"] } +gperftools = { version = "0.2.0", optional = true } +serde = { version = "1.0.130", features = ["derive", "rc"] } +serde_json = "1.0.67" +serde_with = { version = "3.8.1", features = ["base64"] } + +[features] +counters = [] +cpuprof = ["gperftools"] +heapprof = ["gperftools", "gperftools/heap"] diff --git a/arbitrator/bench/src/bin.rs b/arbitrator/bench/src/bin.rs new file mode 100644 index 0000000000..f7e69f5373 --- /dev/null +++ b/arbitrator/bench/src/bin.rs @@ -0,0 +1,120 @@ +use std::{path::PathBuf, time::Duration}; + +use bench::prepare::*; +use clap::Parser; +use eyre::bail; + +#[cfg(feature = "cpuprof")] +use gperftools::profiler::PROFILER; + +#[cfg(feature = "heapprof")] +use gperftools::heap_profiler::HEAP_PROFILER; + +use prover::machine::MachineStatus; + +#[cfg(feature = "counters")] +use prover::{machine, memory, merkle}; + +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// Path to a preimages text file + #[arg(short, long)] + preimages_path: PathBuf, + + /// Path to a machine.wavm.br + #[arg(short, long)] + machine_path: PathBuf, +} + +fn main() -> eyre::Result<()> { + let args = Args::parse(); + let step_sizes = [1, 1 << 10, 1 << 15, 1 << 20, 1 << 24, 1 << 26, 1 << 28]; + + println!("Running benchmark with always merkleize feature on"); + for step_size in step_sizes { + let mut machine = prepare_machine(args.preimages_path.clone(), args.machine_path.clone())?; + let _ = machine.hash(); + let mut hash_times = vec![]; + let mut step_times = vec![]; + let mut num_iters = 0; + + #[cfg(feature = "cpuprof")] + PROFILER + .lock() + .unwrap() + .start(format!("./target/bench-{}.prof", step_size)) + .unwrap(); + + #[cfg(feature = "heapprof")] + HEAP_PROFILER + .lock() + .unwrap() + .start(format!("./target/bench-{}.hprof", step_size)) + .unwrap(); + + #[cfg(feature = "counters")] + { + machine::reset_counters(); + memory::reset_counters(); + merkle::reset_counters(); + } + let total = std::time::Instant::now(); + loop { + let start = std::time::Instant::now(); + machine.step_n(step_size)?; + let step_end_time = start.elapsed(); + step_times.push(step_end_time); + match machine.get_status() { + MachineStatus::Errored => { + println!("Errored"); + break; + } + MachineStatus::TooFar => { + bail!("Machine too far => position {}", machine.get_steps()) + } + MachineStatus::Running => {} + MachineStatus::Finished => { + break; + } + } + let start = std::time::Instant::now(); + let _ = machine.hash(); + let hash_end_time = start.elapsed(); + hash_times.push(hash_end_time); + num_iters += 1; + if num_iters == 200 { + break; + } + } + + #[cfg(feature = "cpuprof")] + PROFILER.lock().unwrap().stop().unwrap(); + + #[cfg(feature = "heapprof")] + HEAP_PROFILER.lock().unwrap().stop().unwrap(); + + let total_end_time = total.elapsed(); + println!( + "avg hash time {:>11?}, avg step time {:>12?}, step size {:>9}, num_iters {:>3}, total time {:>12?}", + average(&hash_times), + average(&step_times), + step_size, + num_iters, + total_end_time, + ); + #[cfg(feature = "counters")] + { + machine::print_counters(); + memory::print_counters(); + merkle::print_counters(); + } + } + Ok(()) +} + +fn average(numbers: &[Duration]) -> Duration { + let sum: Duration = numbers.iter().sum(); + let sum: u64 = sum.as_nanos().try_into().unwrap(); + Duration::from_nanos(sum / numbers.len() as u64) +} diff --git a/arbitrator/bench/src/lib.rs b/arbitrator/bench/src/lib.rs new file mode 100644 index 0000000000..5f7c024094 --- /dev/null +++ b/arbitrator/bench/src/lib.rs @@ -0,0 +1,2 @@ +pub mod parse_input; +pub mod prepare; diff --git a/arbitrator/bench/src/parse_input.rs b/arbitrator/bench/src/parse_input.rs new file mode 100644 index 0000000000..decc67372a --- /dev/null +++ b/arbitrator/bench/src/parse_input.rs @@ -0,0 +1,76 @@ +use arbutil::Bytes32; +use serde::{Deserialize, Serialize}; +use serde_json; +use serde_with::base64::Base64; +use serde_with::As; +use serde_with::DisplayFromStr; +use std::{ + collections::HashMap, + io::{self, BufRead}, +}; + +mod prefixed_hex { + use serde::{self, Deserialize, Deserializer, Serializer}; + + pub fn serialize(bytes: &Vec, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&format!("0x{}", hex::encode(bytes))) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + if let Some(s) = s.strip_prefix("0x") { + hex::decode(s).map_err(serde::de::Error::custom) + } else { + Err(serde::de::Error::custom("missing 0x prefix")) + } + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct PreimageMap(HashMap>); + +#[derive(Debug, Clone, Deserialize, Serialize)] +#[serde(rename_all = "PascalCase")] +pub struct BatchInfo { + pub number: u64, + #[serde(with = "As::")] + pub data_b64: Vec, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "PascalCase")] +pub struct StartState { + #[serde(with = "prefixed_hex")] + pub block_hash: Vec, + #[serde(with = "prefixed_hex")] + pub send_root: Vec, + pub batch: u64, + pub pos_in_batch: u64, +} + +#[derive(Debug, Deserialize, Serialize)] +#[serde(rename_all = "PascalCase")] +pub struct FileData { + pub id: u64, + pub has_delayed_msg: bool, + pub delayed_msg_nr: u64, + #[serde(with = "As::>>")] + pub preimages_b64: HashMap>>, + pub batch_info: Vec, + #[serde(with = "As::")] + pub delayed_msg_b64: Vec, + pub start_state: StartState, +} + +impl FileData { + pub fn from_reader(mut reader: R) -> io::Result { + let data = serde_json::from_reader(&mut reader)?; + Ok(data) + } +} diff --git a/arbitrator/bench/src/prepare.rs b/arbitrator/bench/src/prepare.rs new file mode 100644 index 0000000000..741a7350ac --- /dev/null +++ b/arbitrator/bench/src/prepare.rs @@ -0,0 +1,56 @@ +use arbutil::{Bytes32, PreimageType}; +use prover::machine::{argument_data_to_inbox, GlobalState, Machine}; +use prover::utils::CBytes; +use std::collections::HashMap; +use std::fs::File; +use std::io::BufReader; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use crate::parse_input::*; + +pub fn prepare_machine(preimages: PathBuf, machines: PathBuf) -> eyre::Result { + let file = File::open(preimages)?; + let reader = BufReader::new(file); + + let data = FileData::from_reader(reader)?; + let preimages = data + .preimages_b64 + .into_iter() + .flat_map(|preimage| preimage.1.into_iter()) + .collect::>>(); + let preimage_resolver = move |_: u64, _: PreimageType, hash: Bytes32| -> Option { + preimages + .get(&hash) + .map(|data| CBytes::from(data.as_slice())) + }; + let preimage_resolver = Arc::new(Box::new(preimage_resolver)); + + let binary_path = Path::new(&machines); + let mut mach = Machine::new_from_wavm(binary_path)?; + + let block_hash: [u8; 32] = data.start_state.block_hash.try_into().unwrap(); + let block_hash: Bytes32 = block_hash.into(); + let send_root: [u8; 32] = data.start_state.send_root.try_into().unwrap(); + let send_root: Bytes32 = send_root.into(); + let bytes32_vals: [Bytes32; 2] = [block_hash, send_root]; + let u64_vals: [u64; 2] = [data.start_state.batch, data.start_state.pos_in_batch]; + let start_state = GlobalState { + bytes32_vals, + u64_vals, + }; + + mach.set_global_state(start_state); + + mach.set_preimage_resolver(preimage_resolver); + + let identifier = argument_data_to_inbox(0).unwrap(); + for batch_info in data.batch_info.iter() { + mach.add_inbox_msg(identifier, batch_info.number, batch_info.data_b64.clone()); + } + + let identifier = argument_data_to_inbox(1).unwrap(); + mach.add_inbox_msg(identifier, data.delayed_msg_nr, data.delayed_msg_b64); + + Ok(mach) +} diff --git a/arbitrator/brotli/src/lib.rs b/arbitrator/brotli/src/lib.rs index 5bc2e8dcf6..b2c808b1a4 100644 --- a/arbitrator/brotli/src/lib.rs +++ b/arbitrator/brotli/src/lib.rs @@ -192,7 +192,7 @@ pub fn compress_fixed<'a>( BrotliEncoderDestroyInstance(state); // SAFETY: brotli initialized this span of bytes - let output = mem::transmute(&output[..out_len]); + let output = mem::transmute::<&[MaybeUninit], &[u8]>(&output[..out_len]); Ok(output) } } @@ -304,7 +304,7 @@ pub fn decompress_fixed<'a>( BrotliDecoderDestroyInstance(state); // SAFETY: brotli initialized this span of bytes - let output = mem::transmute(&output[..out_len]); + let output = mem::transmute::<&[MaybeUninit], &[u8]>(&output[..out_len]); Ok(output) } } diff --git a/arbitrator/jit/src/caller_env.rs b/arbitrator/jit/src/caller_env.rs index f4fbff10ae..41240d3d98 100644 --- a/arbitrator/jit/src/caller_env.rs +++ b/arbitrator/jit/src/caller_env.rs @@ -8,7 +8,6 @@ use rand::RngCore; use rand_pcg::Pcg32; use std::{ cmp::Ordering, - collections::{BTreeSet, BinaryHeap}, fmt::Debug, mem::{self, MaybeUninit}, }; @@ -102,7 +101,7 @@ impl MemAccess for JitMemAccess<'_> { self.view() .read_uninit(ptr.into(), &mut data) .expect("bad read"); - mem::transmute(data) + mem::transmute::>, Vec>(data) } } @@ -175,11 +174,3 @@ impl PartialOrd for TimeoutInfo { 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 f51970c6dd..2a3c5c5616 100644 --- a/arbitrator/jit/src/machine.rs +++ b/arbitrator/jit/src/machine.rs @@ -15,7 +15,7 @@ use std::{ io::{BufReader, BufWriter, ErrorKind, Read}, net::TcpStream, sync::Arc, - time::{Duration, Instant}, + time::Instant, }; use thiserror::Error; use wasmer::{ @@ -322,8 +322,6 @@ 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, } @@ -335,7 +333,6 @@ impl Default for ProcessEnv { debug: false, socket: None, timestamp: Instant::now(), - child_timeout: Duration::from_secs(15), reached_wavmio: false, } } diff --git a/arbitrator/langs/bf b/arbitrator/langs/bf index cb5750580f..92420f8f34 160000 --- a/arbitrator/langs/bf +++ b/arbitrator/langs/bf @@ -1 +1 @@ -Subproject commit cb5750580f6990b5166ffce83de11b766a25ca31 +Subproject commit 92420f8f34b53f3c1d47047f9f894820d506c565 diff --git a/arbitrator/prover/Cargo.toml b/arbitrator/prover/Cargo.toml index 0b42013808..5475647765 100644 --- a/arbitrator/prover/Cargo.toml +++ b/arbitrator/prover/Cargo.toml @@ -8,6 +8,7 @@ publish = false bincode = "1.3.3" derivative = "2.2.0" digest = "0.9.0" +bitvec = { version = "1", features = ["serde"] } eyre = "0.6.5" fnv = "1.0.7" hex = "0.4.3" @@ -40,6 +41,15 @@ c-kzg = { version = "0.4.0", optional = true } # TODO: look into switching to ru sha2 = "0.9.9" lru = "0.12.3" once_cell = "1.19.0" +enum-iterator = "2.0.1" + +[dev-dependencies] +criterion = { version = "0.5.0", features = ["html_reports"] } +rand = "0.8.4" + +[[bench]] +name = "merkle_bench" +harness = false [lib] name = "prover" @@ -47,6 +57,7 @@ crate-type = ["staticlib", "lib"] [features] default = ["native", "rayon", "singlepass_rayon"] +counters = [] 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/benches/merkle_bench.rs b/arbitrator/prover/benches/merkle_bench.rs new file mode 100644 index 0000000000..98ac09a20e --- /dev/null +++ b/arbitrator/prover/benches/merkle_bench.rs @@ -0,0 +1,52 @@ +use arbutil::Bytes32; +use criterion::{criterion_group, criterion_main, Criterion}; +use prover::merkle::{Merkle, MerkleType}; +use rand::Rng; + +fn resize_and_set_leaves(merkle: Merkle, rng: &mut rand::rngs::ThreadRng) { + for _ in 0..100 { + merkle.resize(merkle.len() + 5).expect("resize failed"); + for _ in 0..(merkle.len() / 10) { + let random_index = rng.gen_range(0..merkle.len()); + merkle.set(random_index, Bytes32::from([rng.gen_range(0u8..9); 32])); + } + } + merkle.root(); +} + +fn merkle_benchmark(c: &mut Criterion) { + let mut rng = rand::thread_rng(); + let leaves = vec![ + Bytes32::from([1; 32]), + Bytes32::from([2; 32]), + Bytes32::from([3; 32]), + Bytes32::from([4; 32]), + Bytes32::from([5; 32]), + ]; + + // Perform many calls to set leaves to new values + c.bench_function("resize_set_leaves_and_root", |b| { + b.iter(|| { + let merkle = Merkle::new_advanced(MerkleType::Memory, leaves.clone(), 20); + resize_and_set_leaves(merkle.clone(), &mut rng); + }) + }); +} + +fn merkle_construction(c: &mut Criterion) { + let mut rng = rand::thread_rng(); + let mut leaves: Vec = Vec::with_capacity(1 << 20); + for _ in 0..(1 << 20) { + leaves.push(Bytes32::from([rng.gen_range(0u8..9); 32])); + } + + c.bench_function("merkle_construction", |b| { + b.iter(|| { + let merkle = Merkle::new_advanced(MerkleType::Memory, leaves.clone(), 21); + merkle.root(); + }) + }); +} + +criterion_group!(benches, merkle_benchmark, merkle_construction); +criterion_main!(benches); diff --git a/arbitrator/prover/fuzz/Cargo.lock b/arbitrator/prover/fuzz/Cargo.lock index 4966a07d3f..f0cc083864 100644 --- a/arbitrator/prover/fuzz/Cargo.lock +++ b/arbitrator/prover/fuzz/Cargo.lock @@ -17,6 +17,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490" +[[package]] +name = "arbutil" +version = "0.1.0" +dependencies = [ + "digest 0.10.7", + "num_enum", + "sha2", + "sha3 0.10.8", +] + [[package]] name = "arrayvec" version = "0.7.2" @@ -36,14 +46,14 @@ dependencies = [ [[package]] name = "auto_impl" -version = "0.5.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7862e21c893d65a1650125d157eaeec691439379a1cee17ee49031b79236ada4" +checksum = "fee3da8ef1276b0bee5dd1c7258010d8fffd31801447323115a25560e1327b89" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -126,9 +136,9 @@ dependencies = [ [[package]] name = "byte-slice-cast" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c5fdd0166095e1d463fc6cc01aa8ce547ad77a4e84d42eb6762b084e28067e" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" [[package]] name = "byteorder" @@ -169,6 +179,15 @@ dependencies = [ "vec_map", ] +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + [[package]] name = "crossbeam-channel" version = "0.5.4" @@ -251,7 +270,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn", + "syn 1.0.109", ] [[package]] @@ -262,7 +281,7 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -273,7 +292,7 @@ checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -287,9 +306,9 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.3" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.2", "crypto-common", @@ -307,14 +326,20 @@ version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b91989ae21441195d7d9b9993a2f9295c7e1a8c96255d8b729accddc124797" +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "ethbloom" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11da94e443c60508eb62cf256243a64da87304c2802ac2528847f79d750007ef" +checksum = "c22d4b5885b6aa2fe5e8b9329fb8d232bf739e434e6b87347c63bdd00c120f60" dependencies = [ "crunchy", - "fixed-hash", + "fixed-hash 0.8.0", "impl-codec", "impl-rlp", "scale-info", @@ -323,9 +348,9 @@ dependencies = [ [[package]] name = "ethereum" -version = "0.12.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23750149fe8834c0e24bb9adcbacbe06c45b9861f15df53e09f26cb7c4ab91ef" +checksum = "2e04d24d20b8ff2235cffbf242d5092de3aa45f77c5270ddbfadd2778ca13fea" dependencies = [ "bytes", "ethereum-types", @@ -333,33 +358,32 @@ dependencies = [ "hash256-std-hasher", "parity-scale-codec", "rlp", - "rlp-derive", "scale-info", "serde", - "sha3 0.10.1", - "triehash", + "sha3 0.10.8", + "trie-root", ] [[package]] name = "ethereum-types" -version = "0.13.1" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2827b94c556145446fcce834ca86b7abf0c39a805883fe20e72c5bfdb5a0dc6" +checksum = "02d215cbf040552efcbe99a38372fe80ab9d00268e20012b79fcd0f073edd8ee" dependencies = [ "ethbloom", - "fixed-hash", + "fixed-hash 0.8.0", "impl-codec", "impl-rlp", - "primitive-types", + "primitive-types 0.12.2", "scale-info", "uint", ] [[package]] name = "evm" -version = "0.36.0" +version = "0.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d388bbd18050623b996cc4ba0643971e2978693ad56ca8b7603080cfa5eaf738" +checksum = "767f43e9630cc36cf8ff2777cbb0121b055f0d1fd6eaaa13b46a1808f0d0e7e9" dependencies = [ "auto_impl", "environmental", @@ -369,48 +393,48 @@ dependencies = [ "evm-runtime", "log", "parity-scale-codec", - "primitive-types", + "primitive-types 0.12.2", "rlp", "scale-info", "serde", - "sha3 0.10.1", + "sha3 0.10.8", ] [[package]] name = "evm-core" -version = "0.36.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5fb3a449a544a67c879d2f74e1c3d9022de3ec31c9a20817015816f687aa2af" +checksum = "d1da6cedc5cedb4208e59467106db0d1f50db01b920920589f8e672c02fdc04f" dependencies = [ "parity-scale-codec", - "primitive-types", + "primitive-types 0.12.2", "scale-info", "serde", ] [[package]] name = "evm-gasometer" -version = "0.36.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "170a27b6e49b8279016afffcdc6ebae9225d5acff3a546ad8589929b091e7ac5" +checksum = "1dc0eb591abc5cd7b05bef6a036c2bb6c66ab6c5e0c5ce94bfe377ab670b1fd7" dependencies = [ "environmental", "evm-core", "evm-runtime", - "primitive-types", + "primitive-types 0.12.2", ] [[package]] name = "evm-runtime" -version = "0.36.0" +version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d48c2545a02e3a4d1a5184a96af11037334dce947b6bdb389b3503b3a6f8dcd" +checksum = "84bbe09b64ae13a29514048c1bb6fda6374ac0b4f6a1f15a443348ab88ef42cd" dependencies = [ "auto_impl", "environmental", "evm-core", - "primitive-types", - "sha3 0.10.1", + "primitive-types 0.12.2", + "sha3 0.10.8", ] [[package]] @@ -435,6 +459,18 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + [[package]] name = "fnv" version = "1.0.7" @@ -470,9 +506,9 @@ dependencies = [ [[package]] name = "hash-db" -version = "0.15.2" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23bd4e7b5eda0d0f3a307e8b381fdc8ba9000f26fbe912250c0a4cc3956364a" +checksum = "8e7d7786361d7425ae2fe4f9e407eb0efaa0840f5212d109cc018c40c35c6ab4" [[package]] name = "hash256-std-hasher" @@ -489,6 +525,12 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + [[package]] name = "heck" version = "0.3.3" @@ -545,7 +587,7 @@ checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -561,7 +603,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.11.2", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", ] [[package]] @@ -572,9 +624,12 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "keccak" -version = "0.1.0" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] [[package]] name = "lazy_static" @@ -582,11 +637,17 @@ 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.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libfuzzer-sys" @@ -736,6 +797,27 @@ dependencies = [ "libc", ] +[[package]] +name = "num_enum" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683751d591e6d81200c39fb0d1032608b77724f34114db54f571ff1317b337c0" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c11e44798ad209ccdd91fc192f0526a369a01234f7373e1b141c96d7cee4f0e" +dependencies = [ + "proc-macro-crate 1.1.3", + "proc-macro2", + "quote", + "syn 2.0.46", +] + [[package]] name = "once_cell" version = "1.10.0" @@ -750,9 +832,9 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" [[package]] name = "parity-scale-codec" -version = "3.1.2" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8b44461635bbb1a0300f100a841e571e7d919c81c73075ef5d152ffdb521066" +checksum = "881331e34fa842a2fb61cc2db9643a8fedc615e47cfcc52597d1af0db9a7e8fe" dependencies = [ "arrayvec", "bitvec", @@ -764,14 +846,14 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.2" +version = "3.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c45ed1f39709f5a89338fab50e59816b2e8815f5bb58276e7ddf9afd495f73f8" +checksum = "be30eaf4b0a9fba5336683b38de57bb86d179a35862ba6bfcf57625d006bde5b" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 2.0.1", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -792,7 +874,18 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e28720988bff275df1f51b171e1b2a18c30d194c4d2b61defdacecd625a5d94a" dependencies = [ - "fixed-hash", + "fixed-hash 0.7.0", + "impl-codec", + "uint", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash 0.8.0", "impl-codec", "impl-rlp", "scale-info", @@ -809,6 +902,16 @@ dependencies = [ "toml", ] +[[package]] +name = "proc-macro-crate" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97dc5fea232fc28d2f597b37c4876b348a40e33f3b02cc975c8d006d78d94b1a" +dependencies = [ + "toml_datetime", + "toml_edit", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -818,7 +921,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -835,23 +938,25 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.37" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1" +checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "prover" version = "0.1.0" dependencies = [ + "arbutil", "bincode", "brotli2", "digest 0.9.0", "eyre", "fnv", "hex", + "lazy_static", "libc", "nom", "nom-leb128", @@ -866,6 +971,7 @@ dependencies = [ "static_assertions", "structopt", "wasmparser", + "wat", ] [[package]] @@ -877,7 +983,7 @@ dependencies = [ "hex", "lazy_static", "libfuzzer-sys", - "primitive-types", + "primitive-types 0.11.1", "prover", "rayon", "serde", @@ -887,9 +993,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.18" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -956,11 +1062,12 @@ dependencies = [ [[package]] name = "rlp" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "999508abb0ae792aabed2460c45b89106d97fe4adac593bdaef433c2605847b5" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" dependencies = [ "bytes", + "rlp-derive", "rustc-hex", ] @@ -972,7 +1079,7 @@ checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1001,9 +1108,9 @@ checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "scale-info" -version = "2.1.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8980cafbe98a7ee7a9cc16b32ebce542c77883f512d83fbf2ddc8f6a85ea74c9" +checksum = "7f7d66a1128282b7ef025a8ead62a4a9fcf017382ec53b8ffbf4d7bf77bd3c60" dependencies = [ "bitvec", "cfg-if", @@ -1014,14 +1121,14 @@ dependencies = [ [[package]] name = "scale-info-derive" -version = "2.1.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4260c630e8a8a33429d1688eff2f163f24c65a4e1b1578ef6b565061336e4b6f" +checksum = "abf2c68b89cafb3b8d918dd07b42be0da66ff202cf1155c5739a4e0c1ea0dc19" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.1.3", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1032,22 +1139,22 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.46", ] [[package]] @@ -1081,7 +1188,18 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn", + "syn 1.0.109", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.7", ] [[package]] @@ -1098,11 +1216,11 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.1" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881bf8156c87b6301fc5ca6b27f11eeb2761224c7081e69b409d5a1951a70c86" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.3", + "digest 0.10.7", "keccak", ] @@ -1154,18 +1272,29 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "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 = "1.0.92" +version = "2.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52" +checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -1200,7 +1329,7 @@ checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -1232,13 +1361,29 @@ dependencies = [ ] [[package]] -name = "triehash" -version = "0.8.4" +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.1.0", + "toml_datetime", + "winnow", +] + +[[package]] +name = "trie-root" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1631b201eb031b563d2e85ca18ec8092508e262a3196ce9bd10a67ec87b9f5c" +checksum = "d4ed310ef5ab98f5fa467900ed906cb9232dd5376597e00fd4cba2a449d06c0b" dependencies = [ "hash-db", - "rlp", ] [[package]] @@ -1249,9 +1394,9 @@ checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "uint" -version = "0.9.3" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f03af7ccf01dd611cc450a0d10dbc9b745770d096473e2faf0ca6e2d66d1e0" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" dependencies = [ "byteorder", "crunchy", @@ -1259,6 +1404,12 @@ dependencies = [ "static_assertions", ] +[[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.9.0" @@ -1271,12 +1422,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" -[[package]] -name = "unicode-xid" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" - [[package]] name = "vec_map" version = "0.8.2" @@ -1295,13 +1440,43 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasm-encoder" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad2b51884de9c7f4fe2fd1043fccb8dcad4b1e29558146ee57a144d15779f3f" +dependencies = [ + "leb128", +] + [[package]] name = "wasmparser" version = "0.84.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77dc97c22bb5ce49a47b745bed8812d30206eff5ef3af31424f2c1820c0974b2" dependencies = [ - "indexmap", + "indexmap 1.8.1", +] + +[[package]] +name = "wast" +version = "69.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ee37317321afde358e4d7593745942c48d6d17e0e6e943704de9bbee121e7a" +dependencies = [ + "leb128", + "memchr", + "unicode-width", + "wasm-encoder", +] + +[[package]] +name = "wat" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb338ee8dee4d4cd05e6426683f21c5087dc7cfc8903e839ccf48d43332da3c" +dependencies = [ + "wast", ] [[package]] @@ -1326,6 +1501,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winnow" +version = "0.5.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6" +dependencies = [ + "memchr", +] + [[package]] name = "wyz" version = "0.5.0" diff --git a/arbitrator/prover/fuzz/Cargo.toml b/arbitrator/prover/fuzz/Cargo.toml index 08fcc863aa..7845b2a38e 100644 --- a/arbitrator/prover/fuzz/Cargo.toml +++ b/arbitrator/prover/fuzz/Cargo.toml @@ -15,7 +15,7 @@ eyre = "0.6.8" tokio = { version = "1.18.5", features = ["rt", "rt-multi-thread"] } serde = { version = "1.0.137", features = ["derive"] } hex = "0.4.3" -evm = "0.36.0" +evm = "0.41.1" serde_json = "1.0.81" primitive-types = "0.11.1" rayon = "1.5.1" diff --git a/arbitrator/prover/src/lib.rs b/arbitrator/prover/src/lib.rs index c8649a4b3d..0f537478eb 100644 --- a/arbitrator/prover/src/lib.rs +++ b/arbitrator/prover/src/lib.rs @@ -9,8 +9,8 @@ mod host; mod kzg; pub mod machine; /// cbindgen:ignore -mod memory; -mod merkle; +pub mod memory; +pub mod merkle; mod print; pub mod programs; mod reinterpret; @@ -101,8 +101,7 @@ unsafe fn arbitrator_load_machine_impl( &libraries, binary_path, true, - false, - false, + true, debug_chain, debug_chain, Default::default(), diff --git a/arbitrator/prover/src/machine.rs b/arbitrator/prover/src/machine.rs index 5466c7f790..358876bd25 100644 --- a/arbitrator/prover/src/machine.rs +++ b/arbitrator/prover/src/machine.rs @@ -32,6 +32,10 @@ use serde::{Deserialize, Serialize}; use serde_with::serde_as; use sha3::Keccak256; use smallvec::SmallVec; + +#[cfg(feature = "counters")] +use std::sync::atomic::{AtomicUsize, Ordering}; + use std::{ borrow::Cow, convert::{TryFrom, TryInto}, @@ -44,12 +48,29 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; + use wasmer_types::FunctionIndex; use wasmparser::{DataKind, ElementItems, ElementKind, Operator, RefType, TableType}; #[cfg(feature = "rayon")] use rayon::prelude::*; +#[cfg(feature = "counters")] +static GET_MODULES_MERKLE_COUNTER: AtomicUsize = AtomicUsize::new(0); + +#[cfg(feature = "counters")] +pub fn print_counters() { + println!( + "GET_MODULES_MERKLE_COUNTER: {}", + GET_MODULES_MERKLE_COUNTER.load(Ordering::Relaxed) + ); +} + +#[cfg(feature = "counters")] +pub fn reset_counters() { + GET_MODULES_MERKLE_COUNTER.store(0, Ordering::Relaxed); +} + fn hash_call_indirect_data(table: u32, ty: &FunctionType) -> Bytes32 { let mut h = Keccak256::new(); h.update("Call indirect:"); @@ -1200,7 +1221,6 @@ impl Machine { library_paths: &[PathBuf], binary_path: &Path, language_support: bool, - always_merkleize: bool, allow_hostapi_from_main: bool, debug_funcs: bool, debug_info: bool, @@ -1225,7 +1245,6 @@ impl Machine { &libraries, bin, language_support, - always_merkleize, allow_hostapi_from_main, debug_funcs, debug_info, @@ -1255,8 +1274,7 @@ impl Machine { &[soft_float, wasi_stub, user_test], bin, false, - false, - false, + true, compile.debug.debug_funcs, true, GlobalState::default(), @@ -1303,7 +1321,6 @@ impl Machine { libraries: &[WasmBinary<'_>], bin: WasmBinary<'_>, runtime_support: bool, - always_merkleize: bool, allow_hostapi_from_main: bool, debug_funcs: bool, debug_info: bool, @@ -1506,18 +1523,12 @@ impl Machine { let tables_hashes: Result<_, _> = module.tables.iter().map(Table::hash).collect(); module.tables_merkle = Merkle::new(MerkleType::Table, tables_hashes?); - - if always_merkleize { - module.memory.cache_merkle_tree(); - } - } - let mut modules_merkle = None; - if always_merkleize { - modules_merkle = Some(Merkle::new( - MerkleType::Module, - modules.iter().map(Module::hash).collect(), - )); + module.memory.cache_merkle_tree(); } + let modules_merkle = Some(Merkle::new( + MerkleType::Module, + modules.iter().map(Module::hash).collect(), + )); // find the first inbox index that's out of bounds let first_too_far = inbox_contents @@ -1577,7 +1588,12 @@ impl Machine { MerkleType::Function, module.funcs.iter().map(Function::hash).collect(), )); + module.memory.cache_merkle_tree(); } + let modules_merkle = Some(Merkle::new( + MerkleType::Module, + modules.iter().map(Module::hash).collect(), + )); let mut mach = Machine { status: MachineStatus::Running, thread_state: ThreadState::Main, @@ -1586,7 +1602,7 @@ impl Machine { internal_stack: Vec::new(), frame_stacks: vec![Vec::new()], modules, - modules_merkle: None, + modules_merkle, global_state: Default::default(), pc: ProgramCounter::default(), stdio_output: Vec::new(), @@ -1709,7 +1725,11 @@ impl Machine { /// 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 Some(module) = self + .modules + .iter() + .position(|m| m.name().trim_end_matches(".wasm") == 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()) @@ -1906,20 +1926,11 @@ impl Machine { func = &module.funcs[self.pc.func()]; }; } - macro_rules! flush_module { - () => { - if let Some(merkle) = self.modules_merkle.as_mut() { - merkle.set(self.pc.module(), module.hash()); - } - }; - } 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()),*); @@ -1938,7 +1949,6 @@ impl Machine { continue; } self.status = MachineStatus::Errored; - module = &mut self.modules[self.pc.module()]; break; }}; } @@ -1949,7 +1959,6 @@ impl Machine { println!("\n{}", "Machine out of steps".red()); self.status = MachineStatus::Errored; self.print_backtrace(true); - module = &mut self.modules[self.pc.module()]; break; } @@ -2009,9 +2018,6 @@ impl Machine { Value::RefNull => error!(), Value::InternalRef(pc) => { let changing_module = pc.module != self.pc.module; - if changing_module { - flush_module!(); - } self.pc = pc; if changing_module { module = &mut self.modules[self.pc.module()]; @@ -2031,7 +2037,6 @@ impl Machine { func = &module.funcs[self.pc.func()]; } Opcode::CrossModuleCall => { - flush_module!(); value_stack.push(Value::InternalRef(self.pc)); value_stack.push(self.pc.module.into()); value_stack.push(module.internals_offset.into()); @@ -2042,7 +2047,6 @@ impl Machine { 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()); @@ -2054,7 +2058,6 @@ impl Machine { 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)); @@ -2077,7 +2080,6 @@ impl Machine { .ok() .and_then(|o| current_frame.caller_module_internals.checked_add(o)) .expect("Internal call function index overflow"); - flush_module!(); self.pc.module = current_frame.caller_module; self.pc.func = func_idx; self.pc.inst = 0; @@ -2509,7 +2511,6 @@ impl Machine { 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; @@ -2522,7 +2523,6 @@ impl Machine { reset_refs!(); } Opcode::UnlinkModule => { - flush_module!(); self.modules.pop(); if let Some(cached) = &mut self.modules_merkle { cached.pop_leaf(); @@ -2562,7 +2562,6 @@ impl Machine { } } } - 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. Self::say(String::from_utf8_lossy(&self.stdio_output)); @@ -2686,7 +2685,13 @@ impl Machine { } fn get_modules_merkle(&self) -> Cow { + #[cfg(feature = "counters")] + GET_MODULES_MERKLE_COUNTER.fetch_add(1, Ordering::Relaxed); + if let Some(merkle) = &self.modules_merkle { + for (i, module) in self.modules.iter().enumerate() { + merkle.set(i, module.hash()); + } Cow::Borrowed(merkle) } else { Cow::Owned(Merkle::new( diff --git a/arbitrator/prover/src/main.rs b/arbitrator/prover/src/main.rs index 9ddd5020c8..dba32e0e72 100644 --- a/arbitrator/prover/src/main.rs +++ b/arbitrator/prover/src/main.rs @@ -35,12 +35,13 @@ struct Opts { #[structopt(long)] 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, + #[structopt(long)] + /// print wasm module root to the console + print_wasmmoduleroot: bool, /// profile output instead of generting proofs #[structopt(short = "p", long)] profile_run: bool, @@ -122,6 +123,18 @@ const DELAYED_HEADER_LEN: usize = 112; // also in test-case's host-io.rs & contr fn main() -> Result<()> { let opts = Opts::from_args(); + if opts.print_wasmmoduleroot { + match Machine::new_from_wavm(&opts.binary) { + Ok(mach) => { + println!("0x{}", mach.get_modules_root()); + return Ok(()); + } + Err(err) => { + eprintln!("Error loading binary: {err}"); + return Err(err); + } + } + } let mut inbox_contents = HashMap::default(); let mut inbox_position = opts.inbox_position; let mut delayed_position = opts.delayed_inbox_position; @@ -192,7 +205,6 @@ fn main() -> Result<()> { &opts.libraries, &opts.binary, true, - opts.always_merkleize, opts.allow_hostapi, opts.debug_funcs, true, diff --git a/arbitrator/prover/src/memory.rs b/arbitrator/prover/src/memory.rs index bd96221091..5853dc87e1 100644 --- a/arbitrator/prover/src/memory.rs +++ b/arbitrator/prover/src/memory.rs @@ -8,14 +8,35 @@ use crate::{ use arbutil::Bytes32; use digest::Digest; use eyre::{bail, ErrReport, Result}; +use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use sha3::Keccak256; -use std::{borrow::Cow, convert::TryFrom}; +use std::{borrow::Cow, collections::HashSet, convert::TryFrom}; + +#[cfg(feature = "counters")] +use std::sync::atomic::{AtomicUsize, Ordering}; + use wasmer_types::Pages; #[cfg(feature = "rayon")] use rayon::prelude::*; +#[cfg(feature = "counters")] +static MEM_HASH_COUNTER: AtomicUsize = AtomicUsize::new(0); + +#[cfg(feature = "counters")] +pub fn reset_counters() { + MEM_HASH_COUNTER.store(0, Ordering::Relaxed); +} + +#[cfg(feature = "counters")] +pub fn print_counters() { + println!( + "Memory hash count: {}", + MEM_HASH_COUNTER.load(Ordering::Relaxed) + ); +} + pub struct MemoryType { pub min: Pages, pub max: Option, @@ -44,12 +65,14 @@ impl TryFrom<&wasmparser::MemoryType> for MemoryType { } } -#[derive(PartialEq, Eq, Clone, Debug, Default, Serialize, Deserialize)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct Memory { buffer: Vec, #[serde(skip)] pub merkle: Option, pub max_size: u64, + #[serde(skip)] + dirty_leaves: Mutex>, } fn hash_leaf(bytes: [u8; Memory::LEAF_SIZE]) -> Bytes32 { @@ -78,6 +101,17 @@ fn div_round_up(num: usize, denom: usize) -> usize { res } +impl Clone for Memory { + fn clone(&self) -> Self { + Memory { + buffer: self.buffer.clone(), + merkle: self.merkle.clone(), + max_size: self.max_size, + dirty_leaves: Mutex::new(self.dirty_leaves.lock().clone()), + } + } +} + impl Memory { pub const LEAF_SIZE: usize = 32; /// Only used when initializing a memory to determine its size @@ -91,6 +125,7 @@ impl Memory { buffer: vec![0u8; size], merkle: None, max_size, + dirty_leaves: Mutex::new(HashSet::new()), } } @@ -100,6 +135,10 @@ impl Memory { pub fn merkelize(&self) -> Cow<'_, Merkle> { if let Some(m) = &self.merkle { + let mut dirt = self.dirty_leaves.lock(); + for leaf_idx in dirt.drain() { + m.set(leaf_idx, hash_leaf(self.get_leaf_data(leaf_idx))); + } return Cow::Borrowed(m); } // Round the size up to 8 byte long leaves, then round up to the next power of two number of leaves @@ -111,23 +150,22 @@ impl Memory { #[cfg(not(feature = "rayon"))] let leaf_hashes = self.buffer.chunks(Self::LEAF_SIZE); - let mut leaf_hashes: Vec = leaf_hashes + let leaf_hashes: Vec = leaf_hashes .map(|leaf| { let mut full_leaf = [0u8; 32]; full_leaf[..leaf.len()].copy_from_slice(leaf); hash_leaf(full_leaf) }) .collect(); - if leaf_hashes.len() < leaves { - let empty_hash = hash_leaf([0u8; 32]); - leaf_hashes.resize(leaves, empty_hash); + let size = leaf_hashes.len(); + let m = Merkle::new_advanced(MerkleType::Memory, leaf_hashes, Self::MEMORY_LAYERS); + if size < leaves { + m.resize(leaves).unwrap_or_else(|_| { + panic!("Couldn't resize merkle tree from {} to {}", size, leaves) + }); } - Cow::Owned(Merkle::new_advanced( - MerkleType::Memory, - leaf_hashes, - hash_leaf([0u8; 32]), - Self::MEMORY_LAYERS, - )) + self.dirty_leaves.lock().clear(); + Cow::Owned(m) } pub fn get_leaf_data(&self, leaf_idx: usize) -> [u8; Self::LEAF_SIZE] { @@ -142,6 +180,8 @@ impl Memory { } pub fn hash(&self) -> Bytes32 { + #[cfg(feature = "counters")] + MEM_HASH_COUNTER.fetch_add(1, Ordering::Relaxed); let mut h = Keccak256::new(); h.update("Memory:"); h.update((self.buffer.len() as u64).to_be_bytes()); @@ -222,7 +262,11 @@ impl Memory { } #[must_use] + /// Stores a value in memory, returns false if the value would overflow the buffer. + /// + /// bytes is the number of bytes to store. It must be <= 8. pub fn store_value(&mut self, idx: u64, value: u64, bytes: u8) -> bool { + assert!(bytes <= 8); let Some(end_idx) = idx.checked_add(bytes.into()) else { return false; }; @@ -233,22 +277,19 @@ impl Memory { let end_idx = end_idx as usize; let buf = value.to_le_bytes(); self.buffer[idx..end_idx].copy_from_slice(&buf[..bytes.into()]); - - if let Some(mut merkle) = self.merkle.take() { - let start_leaf = idx / Self::LEAF_SIZE; - merkle.set(start_leaf, hash_leaf(self.get_leaf_data(start_leaf))); - let end_leaf = (end_idx - 1) / Self::LEAF_SIZE; - if end_leaf != start_leaf { - merkle.set(end_leaf, hash_leaf(self.get_leaf_data(end_leaf))); - } - self.merkle = Some(merkle); - } + let mut dirty_leaves = self.dirty_leaves.lock(); + dirty_leaves.insert(idx / Self::LEAF_SIZE); + dirty_leaves.insert((end_idx - 1) / Self::LEAF_SIZE); true } #[must_use] + /// Stores a slice in memory, returns false if the value would overflow the buffer. + /// + /// The length of value <= 32. pub fn store_slice_aligned(&mut self, idx: u64, value: &[u8]) -> bool { + assert!(value.len() <= Self::LEAF_SIZE); if idx % Self::LEAF_SIZE as u64 != 0 { return false; } @@ -261,13 +302,7 @@ impl Memory { let idx = idx as usize; let end_idx = end_idx as usize; self.buffer[idx..end_idx].copy_from_slice(value); - - if let Some(mut merkle) = self.merkle.take() { - let start_leaf = idx / Self::LEAF_SIZE; - merkle.set(start_leaf, hash_leaf(self.get_leaf_data(start_leaf))); - // No need for second merkle - assert!(value.len() <= Self::LEAF_SIZE); - } + self.dirty_leaves.lock().insert(idx / Self::LEAF_SIZE); true } @@ -288,7 +323,7 @@ impl Memory { } pub fn get_range(&self, offset: usize, len: usize) -> Option<&[u8]> { - let end = offset.checked_add(len)?; + let end: usize = offset.checked_add(len)?; if end > self.buffer.len() { return None; } @@ -309,18 +344,61 @@ impl Memory { } pub fn resize(&mut self, new_size: usize) { - let had_merkle_tree = self.merkle.is_some(); - self.merkle = None; self.buffer.resize(new_size, 0); - if had_merkle_tree { - self.cache_merkle_tree(); + if let Some(merkle) = &mut self.merkle { + merkle + .resize(new_size / Self::LEAF_SIZE) + .unwrap_or_else(|_| { + panic!( + "Couldn't resize merkle tree from {} to {}", + merkle.len(), + new_size + ) + }); } } } +pub mod testing { + use arbutil::Bytes32; + + pub fn empty_leaf_hash() -> Bytes32 { + let leaf = [0u8; 32]; + super::hash_leaf(leaf) + } +} + #[cfg(test)] mod test { + use arbutil::Bytes32; + use crate::memory::round_up_to_power_of_two; + use crate::memory::testing; + + use super::Memory; + + #[test] + pub fn fixed_memory_hash() { + let module_memory_hash = Bytes32::from([ + 86u8, 177, 192, 60, 217, 123, 221, 153, 118, 79, 229, 122, 210, 48, 187, 104, 40, 84, + 112, 63, 137, 86, 54, 2, 56, 118, 72, 158, 242, 225, 65, 80, + ]); + let memory = Memory::new(65536, 1); + assert_eq!(memory.hash(), module_memory_hash); + } + + #[test] + pub fn empty_leaf_hash() { + let hash = testing::empty_leaf_hash(); + print!("Bytes32(["); + for i in 0..32 { + print!("{}", hash[i]); + if i < 31 { + print!(", "); + } + } + print!("]);"); + } #[test] pub fn test_round_up_power_of_two() { diff --git a/arbitrator/prover/src/merkle.rs b/arbitrator/prover/src/merkle.rs index 16306bd611..4a1278b4cb 100644 --- a/arbitrator/prover/src/merkle.rs +++ b/arbitrator/prover/src/merkle.rs @@ -2,15 +2,47 @@ // For license information, see https://github.com/nitro/blob/master/LICENSE use arbutil::Bytes32; +use bitvec::prelude::*; +use core::panic; use digest::Digest; +use enum_iterator::Sequence; +use parking_lot::Mutex; use serde::{Deserialize, Serialize}; use sha3::Keccak256; -use std::convert::TryFrom; +use std::cmp::max; +use std::convert::{TryFrom, TryInto}; #[cfg(feature = "rayon")] use rayon::prelude::*; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +mod zerohashes; +use self::zerohashes::{EMPTY_HASH, ZERO_HASHES}; +#[cfg(feature = "counters")] +use { + enum_iterator::all, + lazy_static::lazy_static, + std::collections::HashMap, + std::sync::atomic::{AtomicUsize, Ordering}, +}; + +#[cfg(feature = "counters")] +fn create_counters_hashmap() -> HashMap { + let mut map = HashMap::new(); + for ty in all::() { + map.insert(ty, AtomicUsize::new(0)); + } + map +} + +#[cfg(feature = "counters")] +lazy_static! { + static ref NEW_COUNTERS: HashMap = create_counters_hashmap(); + static ref ROOT_COUNTERS: HashMap = create_counters_hashmap(); + static ref SET_COUNTERS: HashMap = create_counters_hashmap(); + static ref RESIZE_COUNTERS: HashMap = create_counters_hashmap(); +} + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Serialize, Deserialize, Sequence)] pub enum MerkleType { Empty, Value, @@ -28,6 +60,36 @@ impl Default for MerkleType { } } +#[cfg(feature = "counters")] +pub fn print_counters() { + for ty in all::() { + if ty == MerkleType::Empty { + continue; + } + println!( + "{} New: {}, Root: {}, Set: {} Resize: {}", + ty.get_prefix(), + NEW_COUNTERS[&ty].load(Ordering::Relaxed), + ROOT_COUNTERS[&ty].load(Ordering::Relaxed), + SET_COUNTERS[&ty].load(Ordering::Relaxed), + RESIZE_COUNTERS[&ty].load(Ordering::Relaxed), + ); + } +} + +#[cfg(feature = "counters")] +pub fn reset_counters() { + for ty in all::() { + if ty == MerkleType::Empty { + continue; + } + NEW_COUNTERS[&ty].store(0, Ordering::Relaxed); + ROOT_COUNTERS[&ty].store(0, Ordering::Relaxed); + SET_COUNTERS[&ty].store(0, Ordering::Relaxed); + RESIZE_COUNTERS[&ty].store(0, Ordering::Relaxed); + } +} + impl MerkleType { pub fn get_prefix(self) -> &'static str { match self { @@ -43,15 +105,36 @@ impl MerkleType { } } -#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)] +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +struct Layers { + data: Vec>, + dirty_leaf_parents: BitVec, +} + +/// A Merkle tree with a fixed number of layers +/// +/// https://en.wikipedia.org/wiki/Merkle_tree +/// +/// Each instance's leaves contain the hashes of a specific [MerkleType]. +/// The tree does not grow in height, but it can be initialized with fewer +/// leaves than the number that could be contained in its layers. +/// +/// When initialized with [Merkle::new], the tree has the minimum depth +/// necessary to hold all the leaves. (e.g. 5 leaves -> 4 layers.) +/// +/// It can be over-provisioned using the [Merkle::new_advanced] method +/// and passing a minimum depth. +/// +/// This structure does not contain the data itself, only the hashes. +#[derive(Debug, Default, Serialize, Deserialize)] pub struct Merkle { ty: MerkleType, - layers: Vec>, - empty_layers: Vec, + #[serde(with = "mutex_sedre")] + layers: Mutex, min_depth: usize, } -fn hash_node(ty: MerkleType, a: Bytes32, b: Bytes32) -> Bytes32 { +fn hash_node(ty: MerkleType, a: impl AsRef<[u8]>, b: impl AsRef<[u8]>) -> Bytes32 { let mut h = Keccak256::new(); h.update(ty.get_prefix()); h.update(a); @@ -59,47 +142,131 @@ fn hash_node(ty: MerkleType, a: Bytes32, b: Bytes32) -> Bytes32 { h.finalize().into() } +const fn empty_hash_at(ty: MerkleType, layer_i: usize) -> &'static Bytes32 { + match ty { + MerkleType::Empty => EMPTY_HASH, + MerkleType::Value => &ZERO_HASHES[0][layer_i], + MerkleType::Function => &ZERO_HASHES[1][layer_i], + MerkleType::Instruction => &ZERO_HASHES[2][layer_i], + MerkleType::Memory => &ZERO_HASHES[3][layer_i], + MerkleType::Table => &ZERO_HASHES[4][layer_i], + MerkleType::TableElement => &ZERO_HASHES[5][layer_i], + MerkleType::Module => &ZERO_HASHES[6][layer_i], + } +} + +#[inline] +#[cfg(feature = "rayon")] +fn new_layer(ty: MerkleType, layer: &[Bytes32], empty_hash: &'static Bytes32) -> Vec { + let mut new_layer: Vec = Vec::with_capacity(layer.len() >> 1); + let chunks = layer.par_chunks(2); + chunks + .map(|chunk| hash_node(ty, chunk[0], chunk.get(1).unwrap_or(empty_hash))) + .collect_into_vec(&mut new_layer); + new_layer +} + +#[inline] +#[cfg(not(feature = "rayon"))] +fn new_layer(ty: MerkleType, layer: &[Bytes32], empty_hash: &'static Bytes32) -> Vec { + let new_layer = layer + .chunks(2) + .map(|chunk| hash_node(ty, chunk[0], chunk.get(1).unwrap_or(empty_hash))) + .collect(); + new_layer +} + +impl Clone for Merkle { + fn clone(&self) -> Self { + let leaves = self.layers.lock().data.first().cloned().unwrap_or_default(); + Merkle::new_advanced(self.ty, leaves, self.min_depth) + } +} + impl Merkle { + /// Creates a new Merkle tree with the given type and leaf hashes. + /// The tree is built up to the minimum depth necessary to hold all the + /// leaves. pub fn new(ty: MerkleType, hashes: Vec) -> Merkle { - Self::new_advanced(ty, hashes, Bytes32::default(), 0) + Self::new_advanced(ty, hashes, 0) } - pub fn new_advanced( - ty: MerkleType, - hashes: Vec, - empty_hash: Bytes32, - min_depth: usize, - ) -> Merkle { - if hashes.is_empty() { + /// Creates a new Merkle tree with the given type, leaf hashes, a hash to + /// use for representing empty leaves, and a minimum depth. + pub fn new_advanced(ty: MerkleType, hashes: Vec, min_depth: usize) -> Merkle { + #[cfg(feature = "counters")] + NEW_COUNTERS[&ty].fetch_add(1, Ordering::Relaxed); + if hashes.is_empty() && min_depth == 0 { return Merkle::default(); } - let mut layers = vec![hashes]; - let mut empty_layers = vec![empty_hash]; + let depth = if hashes.len() > 1 { + min_depth.max(((hashes.len() - 1).ilog2() + 1).try_into().unwrap()) + } else { + min_depth + }; + let mut layers: Vec> = Vec::with_capacity(depth); + let dirty_leaf_parents = bitvec![0; (hashes.len() + 1) >> 1]; + layers.push(hashes); while layers.last().unwrap().len() > 1 || layers.len() < min_depth { - let empty_layer = *empty_layers.last().unwrap(); - - #[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 layer = layers.last().unwrap(); + let empty_hash = empty_hash_at(ty, layers.len() - 1); - 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)); + let new_layer = new_layer(ty, layer, empty_hash); layers.push(new_layer); } + let layers = Mutex::new(Layers { + data: layers, + dirty_leaf_parents, + }); Merkle { ty, layers, - empty_layers, min_depth, } } + fn rehash(&self, layers: &mut Layers) { + // If nothing is dirty, then there's no need to rehash. + if layers.dirty_leaf_parents.is_empty() { + return; + } + let mut dirt = layers.dirty_leaf_parents.clone(); + // Process dirty indices starting from layer 1 (layer 0 is the leaves). + for layer_i in 1..layers.data.len() { + let mut new_dirt = bitvec![0; (dirt.len() + 1) >> 1]; + for idx in dirt.iter_ones() { + let left_child_idx = idx << 1; + let right_child_idx = left_child_idx + 1; + // The left child is guaranteed to exist, but the right one + // might not if the number of child nodes is odd. + let left = layers.data[layer_i - 1][left_child_idx]; + let right = layers.data[layer_i - 1] + .get(right_child_idx) + .unwrap_or(empty_hash_at(self.ty, layer_i - 1)); + let new_hash = hash_node(self.ty, left, right); + if idx < layers.data[layer_i].len() { + layers.data[layer_i][idx] = new_hash; + } else { + // Push the new parent hash onto the end of the layer. + assert_eq!(idx, layers.data[layer_i].len()); + layers.data[layer_i].push(new_hash); + } + // Mark the node's parent as dirty unless it's the root. + if layer_i < layers.data.len() - 1 { + new_dirt.set(idx >> 1, true); + } + } + dirt = new_dirt; + } + layers.dirty_leaf_parents.fill(false); + } + pub fn root(&self) -> Bytes32 { - if let Some(layer) = self.layers.last() { + #[cfg(feature = "counters")] + ROOT_COUNTERS[&self.ty].fetch_add(1, Ordering::Relaxed); + let mut layers = self.layers.lock(); + self.rehash(&mut layers); + if let Some(layer) = layers.data.last() { assert_eq!(layer.len(), 1); layer[0] } else { @@ -107,18 +274,33 @@ impl Merkle { } } - pub fn leaves(&self) -> &[Bytes32] { - if self.layers.is_empty() { - &[] - } else { - &self.layers[0] + // Returns the total number of leaves the tree can hold. + #[inline] + fn capacity(&self) -> usize { + let layers = self.layers.lock(); + if layers.data.is_empty() { + return 0; } + 1 << (layers.data.len() - 1) + } + + // Returns the number of leaves in the tree. + pub fn len(&self) -> usize { + self.layers.lock().data[0].len() + } + + pub fn is_empty(&self) -> bool { + let layers = self.layers.lock(); + layers.data.is_empty() || layers.data[0].is_empty() } #[must_use] pub fn prove(&self, idx: usize) -> Option> { - if idx >= self.leaves().len() { - return None; + { + let layers = self.layers.lock(); + if layers.data.is_empty() || idx >= layers.data[0].len() { + return None; + } } Some(self.prove_any(idx)) } @@ -126,9 +308,11 @@ impl Merkle { /// 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 { + let mut layers = self.layers.lock(); + self.rehash(&mut layers); + let mut proof = vec![u8::try_from(layers.data.len() - 1).unwrap()]; + for (layer_i, layer) in layers.data.iter().enumerate() { + if layer_i == layers.data.len() - 1 { break; } let counterpart = idx ^ 1; @@ -136,7 +320,7 @@ impl Merkle { layer .get(counterpart) .cloned() - .unwrap_or_else(|| self.empty_layers[layer_i]), + .unwrap_or_else(|| *empty_hash_at(self.ty, layer_i)), ); idx >>= 1; } @@ -146,44 +330,285 @@ impl Merkle { /// 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); + let mut leaves = self.layers.lock().data.swap_remove(0); leaves.push(leaf); - let empty = self.empty_layers[0]; - *self = Self::new_advanced(self.ty, leaves, empty, self.min_depth); + *self = Self::new_advanced(self.ty, leaves, 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); + let mut leaves = self.layers.lock().data.swap_remove(0); leaves.pop(); - let empty = self.empty_layers[0]; - *self = Self::new_advanced(self.ty, leaves, empty, self.min_depth); + *self = Self::new_advanced(self.ty, leaves, self.min_depth); } - pub fn set(&mut self, mut idx: usize, hash: Bytes32) { - if self.layers[0][idx] == hash { + // Sets the leaf at the given index to the given hash. + // Panics if the index is out of bounds (since the structure doesn't grow). + pub fn set(&self, idx: usize, hash: Bytes32) { + #[cfg(feature = "counters")] + SET_COUNTERS[&self.ty].fetch_add(1, Ordering::Relaxed); + let mut layers = self.layers.lock(); + if layers.data[0][idx] == hash { return; } - let mut next_hash = hash; - let empty_layers = &self.empty_layers; - let layers_len = self.layers.len(); - for (layer_i, layer) in self.layers.iter_mut().enumerate() { - layer[idx] = next_hash; - if layer_i == layers_len - 1 { - // next_hash isn't needed - break; + layers.data[0][idx] = hash; + layers.dirty_leaf_parents.set(idx >> 1, true); + } + + /// Resizes the number of leaves the tree can hold. + /// + /// The extra space is filled with empty hashes. + pub fn resize(&self, new_len: usize) -> Result { + #[cfg(feature = "counters")] + RESIZE_COUNTERS[&self.ty].fetch_add(1, Ordering::Relaxed); + if new_len > self.capacity() { + return Err( + "Cannot resize to a length greater than the capacity of the tree.".to_owned(), + ); + } + let mut layers = self.layers.lock(); + let start = layers.data[0].len(); + let mut layer_size = new_len; + for (layer_i, layer) in layers.data.iter_mut().enumerate() { + layer.resize(layer_size, *empty_hash_at(self.ty, layer_i)); + layer_size = max(layer_size >> 1, 1); + } + // This will set one or no values depending on if the length was even or odd. + layers.dirty_leaf_parents[(start >> 1)..].fill(true); + // This then resizes and marks the dirty leaf parents as dirty. + layers.dirty_leaf_parents.resize((new_len + 1) >> 1, true); + Ok(layers.data[0].len()) + } +} + +impl PartialEq for Merkle { + // There are only three members of a Merkle, the type, the layers, and the min_depth. + // + // It should be obvious that only if the type and layers are equal, will the root hash + // be equal. So, it is sufficient to compare the root hash when checking equality. + // + // However, it is possible that the min_depth may differ between two merkle trees which + // have the same type and layers. The root hash will still be equal unless the min_depth + // is larger than the depth required to hold the data in the layers. + // + // For example, a Merkle tree with 5 leaves requires 3 layeers to hold the data. If the + // min_depth is 1 on one tree and 2 on another, the root has would still be equal + // because the same nodes are hashed together. However, the min_dpeth was 4, then, + // there would be 4 layers in that tree, and the root hash would be different. + fn eq(&self, other: &Self) -> bool { + self.root() == other.root() && self.ty == other.ty + } +} + +impl Eq for Merkle {} + +pub mod mutex_sedre { + use parking_lot::Mutex; + + pub fn serialize(data: &Mutex, serializer: S) -> Result + where + S: serde::Serializer, + T: serde::Serialize, + { + data.lock().serialize(serializer) + } + + pub fn deserialize<'de, D, T>(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + T: serde::Deserialize<'de>, + { + Ok(Mutex::new(T::deserialize(deserializer)?)) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::memory; + use arbutil::Bytes32; + use core::panic; + use enum_iterator::all; + + #[test] + fn resize_works() { + let hashes = vec![ + Bytes32::from([1; 32]), + Bytes32::from([2; 32]), + Bytes32::from([3; 32]), + Bytes32::from([4; 32]), + Bytes32::from([5; 32]), + ]; + let mut expected = hash_node( + MerkleType::Value, + hash_node( + MerkleType::Value, + hash_node( + MerkleType::Value, + Bytes32::from([1; 32]), + Bytes32::from([2; 32]), + ), + hash_node( + MerkleType::Value, + Bytes32::from([3; 32]), + Bytes32::from([4; 32]), + ), + ), + hash_node( + MerkleType::Value, + hash_node( + MerkleType::Value, + Bytes32::from([5; 32]), + Bytes32::from([0; 32]), + ), + hash_node( + MerkleType::Value, + Bytes32::from([0; 32]), + Bytes32::from([0; 32]), + ), + ), + ); + let merkle = Merkle::new(MerkleType::Value, hashes.clone()); + assert_eq!(merkle.capacity(), 8); + assert_eq!(merkle.root(), expected); + + let new_size = match merkle.resize(6) { + Ok(size) => size, + Err(e) => panic!("{}", e), + }; + assert_eq!(new_size, 6); + assert_eq!(merkle.root(), expected); + + merkle.set(5, Bytes32::from([6; 32])); + expected = hash_node( + MerkleType::Value, + hash_node( + MerkleType::Value, + hash_node( + MerkleType::Value, + Bytes32::from([1; 32]), + Bytes32::from([2; 32]), + ), + hash_node( + MerkleType::Value, + Bytes32::from([3; 32]), + Bytes32::from([4; 32]), + ), + ), + hash_node( + MerkleType::Value, + hash_node( + MerkleType::Value, + Bytes32::from([5; 32]), + Bytes32::from([6; 32]), + ), + hash_node( + MerkleType::Value, + Bytes32::from([0; 32]), + Bytes32::from([0; 32]), + ), + ), + ); + assert_eq!(merkle.root(), expected); + } + + #[test] + fn correct_capacity() { + let merkle: Merkle = Merkle::new(MerkleType::Value, vec![]); + assert_eq!(merkle.capacity(), 0); + let merkle = Merkle::new(MerkleType::Value, vec![Bytes32::from([1; 32])]); + assert_eq!(merkle.capacity(), 1); + let merkle = Merkle::new( + MerkleType::Value, + vec![Bytes32::from([1; 32]), Bytes32::from([2; 32])], + ); + assert_eq!(merkle.capacity(), 2); + let merkle = Merkle::new_advanced(MerkleType::Memory, vec![Bytes32::from([1; 32])], 11); + assert_eq!(merkle.capacity(), 1024); + } + + #[test] + fn resize_and_set_odd() { + let merkle = Merkle::new_advanced(MerkleType::Value, vec![Bytes32::from([1; 32])], 20); + merkle.resize(9).expect("resize failed"); + merkle.set(8, Bytes32::from([2; 32])); + } + + #[test] + fn resize_and_set_even() { + let merkle = Merkle::new_advanced(MerkleType::Value, vec![Bytes32::from([1; 32])], 20); + merkle.resize(10).expect("resize failed"); + merkle.set(9, Bytes32::from([2; 32])); + } + + #[test] + #[ignore = "This is just used for generating the zero hashes for the memory merkle trees."] + fn emit_memory_zerohashes() { + // The following code was generated from the empty_leaf_hash() test in the memory package. + let mut empty_node = Bytes32([ + 57, 29, 211, 154, 252, 227, 18, 99, 65, 126, 203, 166, 252, 232, 32, 3, 98, 194, 254, + 186, 118, 14, 139, 192, 101, 156, 55, 194, 101, 11, 11, 168, + ]) + .clone(); + for _ in 0..64 { + print!("Bytes32(["); + for i in 0..32 { + print!("{}", empty_node[i]); + if i < 31 { + print!(", "); + } } - let counterpart = layer - .get(idx ^ 1) - .cloned() - .unwrap_or_else(|| empty_layers[layer_i]); - if idx % 2 == 0 { - next_hash = hash_node(self.ty, next_hash, counterpart); - } else { - next_hash = hash_node(self.ty, counterpart, next_hash); + println!("]),"); + empty_node = hash_node(MerkleType::Memory, empty_node, empty_node); + } + } + + #[test] + fn clone_is_separate() { + let merkle = Merkle::new_advanced(MerkleType::Value, vec![Bytes32::from([1; 32])], 4); + let m2 = merkle.clone(); + m2.resize(4).expect("resize failed"); + m2.set(3, Bytes32::from([2; 32])); + assert_ne!(merkle, m2); + } + + #[test] + fn serialization_roundtrip() { + let merkle = Merkle::new_advanced(MerkleType::Value, vec![Bytes32::from([1; 32])], 4); + merkle.resize(4).expect("resize failed"); + merkle.set(3, Bytes32::from([2; 32])); + let serialized = bincode::serialize(&merkle).unwrap(); + let deserialized: Merkle = bincode::deserialize(&serialized).unwrap(); + assert_eq!(merkle, deserialized); + } + + #[test] + #[should_panic(expected = "index out of bounds")] + fn set_with_bad_index_panics() { + let merkle = Merkle::new( + MerkleType::Value, + vec![Bytes32::default(), Bytes32::default()], + ); + assert_eq!(merkle.capacity(), 2); + merkle.set(2, Bytes32::default()); + } + + #[test] + fn test_zero_hashes() { + for ty in all::() { + if ty == MerkleType::Empty { + continue; + } + let mut empty_hash = Bytes32::from([0; 32]); + if ty == MerkleType::Memory { + empty_hash = memory::testing::empty_leaf_hash(); + } + for layer in 0..64 { + // empty_hash_at is just a lookup, but empty_hash is calculated iteratively. + assert_eq!(empty_hash_at(ty, layer), &empty_hash); + empty_hash = hash_node(ty, &empty_hash, &empty_hash); } - idx >>= 1; } } } diff --git a/arbitrator/prover/src/merkle/zerohashes.rs b/arbitrator/prover/src/merkle/zerohashes.rs new file mode 100644 index 0000000000..a4724a609d --- /dev/null +++ b/arbitrator/prover/src/merkle/zerohashes.rs @@ -0,0 +1,1827 @@ +use arbutil::Bytes32; + +const VALUE_HASHES: &[Bytes32; 64] = &[ + Bytes32([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + ]), + Bytes32([ + 119, 47, 167, 222, 218, 156, 26, 105, 57, 248, 170, 182, 183, 151, 18, 150, 142, 70, 138, + 253, 54, 123, 132, 86, 170, 76, 157, 52, 113, 138, 253, 254, + ]), + Bytes32([ + 8, 117, 27, 211, 196, 42, 6, 98, 185, 88, 231, 145, 14, 3, 207, 145, 158, 255, 69, 52, 198, + 196, 148, 154, 59, 94, 229, 157, 93, 51, 200, 69, + ]), + Bytes32([ + 247, 208, 196, 206, 204, 168, 129, 100, 238, 95, 52, 26, 132, 138, 233, 149, 196, 72, 179, + 226, 60, 114, 27, 94, 173, 189, 197, 100, 163, 232, 180, 63, + ]), + Bytes32([ + 178, 100, 198, 112, 40, 33, 187, 175, 130, 245, 59, 210, 133, 234, 158, 158, 133, 64, 213, + 72, 222, 157, 216, 42, 210, 134, 36, 13, 211, 156, 163, 211, + ]), + Bytes32([ + 203, 127, 32, 130, 225, 40, 219, 15, 35, 236, 47, 201, 161, 48, 53, 145, 134, 169, 109, 29, + 26, 29, 178, 201, 118, 217, 57, 165, 116, 172, 232, 4, + ]), + Bytes32([ + 209, 109, 33, 102, 227, 251, 133, 224, 170, 130, 65, 90, 185, 209, 146, 77, 244, 145, 181, + 206, 159, 70, 209, 64, 241, 19, 149, 223, 138, 31, 163, 61, + ]), + Bytes32([ + 145, 168, 124, 48, 254, 12, 34, 41, 4, 213, 245, 86, 61, 29, 177, 192, 67, 165, 237, 236, + 87, 126, 193, 191, 15, 126, 104, 173, 165, 186, 1, 107, + ]), + Bytes32([ + 235, 161, 247, 143, 69, 95, 210, 40, 243, 76, 102, 29, 118, 86, 180, 54, 81, 144, 103, 54, + 202, 164, 81, 83, 180, 90, 154, 184, 54, 28, 7, 220, + ]), + Bytes32([ + 203, 62, 12, 83, 35, 122, 214, 199, 125, 61, 236, 8, 253, 64, 145, 34, 127, 116, 217, 118, + 245, 107, 15, 81, 9, 7, 31, 154, 89, 28, 123, 132, + ]), + Bytes32([ + 222, 94, 234, 109, 142, 74, 142, 176, 248, 3, 172, 30, 204, 138, 241, 195, 121, 232, 104, + 219, 32, 54, 240, 77, 125, 247, 70, 137, 80, 46, 221, 247, + ]), + Bytes32([ + 162, 122, 237, 101, 178, 31, 12, 62, 50, 133, 242, 82, 111, 215, 166, 136, 138, 185, 72, + 220, 163, 58, 58, 97, 6, 97, 167, 114, 179, 227, 19, 170, + ]), + Bytes32([ + 67, 251, 144, 11, 175, 194, 149, 65, 173, 179, 69, 140, 213, 191, 133, 59, 81, 175, 161, + 115, 252, 125, 232, 150, 52, 241, 102, 164, 122, 32, 70, 229, + ]), + Bytes32([ + 249, 161, 115, 93, 215, 247, 145, 182, 75, 191, 48, 38, 123, 220, 232, 47, 246, 46, 81, + 164, 98, 41, 141, 249, 137, 173, 93, 213, 96, 15, 151, 245, + ]), + Bytes32([ + 222, 93, 144, 194, 107, 42, 144, 4, 242, 153, 203, 176, 146, 242, 94, 72, 9, 222, 194, 2, + 73, 200, 167, 147, 106, 9, 67, 73, 59, 127, 174, 178, + ]), + Bytes32([ + 56, 228, 248, 59, 153, 215, 120, 224, 111, 48, 159, 82, 171, 227, 93, 214, 208, 151, 78, + 67, 112, 31, 218, 94, 219, 3, 79, 250, 227, 24, 222, 207, + ]), + Bytes32([ + 26, 171, 68, 22, 162, 195, 65, 97, 219, 55, 51, 46, 178, 6, 247, 65, 123, 219, 128, 86, + 193, 3, 253, 0, 239, 228, 187, 94, 184, 240, 67, 11, + ]), + Bytes32([ + 250, 36, 5, 131, 94, 0, 99, 187, 112, 155, 252, 52, 68, 236, 185, 152, 93, 239, 0, 185, + 101, 101, 182, 92, 112, 5, 67, 55, 121, 203, 100, 245, + ]), + Bytes32([ + 208, 57, 149, 71, 227, 213, 246, 213, 175, 88, 95, 238, 93, 122, 168, 223, 149, 84, 30, 89, + 98, 228, 113, 72, 90, 48, 112, 171, 231, 67, 113, 216, + ]), + Bytes32([ + 125, 48, 157, 119, 154, 38, 14, 211, 223, 68, 87, 187, 159, 126, 234, 72, 84, 60, 236, 237, + 227, 130, 87, 43, 217, 105, 81, 196, 159, 238, 240, 10, + ]), + Bytes32([ + 141, 217, 59, 213, 245, 231, 7, 85, 178, 197, 115, 188, 31, 49, 126, 237, 83, 199, 56, 196, + 133, 169, 239, 218, 90, 131, 178, 70, 126, 132, 114, 186, + ]), + Bytes32([ + 18, 2, 140, 12, 121, 238, 8, 160, 7, 208, 89, 194, 105, 136, 211, 58, 179, 200, 226, 138, + 186, 242, 4, 237, 18, 113, 131, 135, 31, 222, 28, 195, + ]), + Bytes32([ + 156, 222, 183, 39, 119, 110, 130, 60, 33, 221, 36, 39, 223, 155, 33, 2, 71, 32, 209, 25, + 194, 172, 209, 1, 40, 105, 235, 140, 160, 18, 63, 168, + ]), + Bytes32([ + 211, 210, 123, 27, 217, 111, 97, 42, 167, 191, 112, 239, 66, 42, 199, 131, 124, 213, 89, + 56, 92, 245, 166, 238, 185, 42, 48, 2, 113, 255, 156, 7, + ]), + Bytes32([ + 66, 255, 87, 69, 86, 6, 54, 162, 162, 5, 93, 138, 194, 194, 151, 195, 57, 155, 58, 17, 21, + 149, 77, 2, 134, 107, 125, 54, 220, 247, 160, 209, + ]), + Bytes32([ + 169, 216, 13, 41, 251, 216, 191, 87, 91, 55, 236, 6, 242, 103, 98, 209, 46, 202, 29, 55, 2, + 50, 233, 76, 107, 92, 82, 156, 113, 226, 78, 211, + ]), + Bytes32([ + 88, 189, 113, 129, 119, 11, 141, 60, 128, 134, 113, 107, 7, 186, 81, 200, 14, 76, 197, 14, + 96, 19, 247, 220, 95, 62, 81, 153, 61, 231, 26, 86, + ]), + Bytes32([ + 41, 214, 41, 96, 151, 11, 231, 7, 203, 70, 242, 156, 247, 133, 49, 223, 254, 164, 202, 154, + 188, 104, 124, 120, 225, 229, 148, 203, 141, 218, 228, 56, + ]), + Bytes32([ + 181, 157, 136, 110, 158, 152, 105, 61, 125, 220, 32, 180, 53, 115, 155, 189, 91, 152, 96, + 172, 7, 121, 65, 143, 82, 180, 3, 69, 118, 219, 31, 244, + ]), + Bytes32([ + 29, 12, 159, 30, 12, 37, 126, 102, 218, 149, 67, 116, 182, 125, 134, 208, 184, 199, 181, + 191, 87, 144, 48, 150, 87, 67, 222, 218, 254, 118, 12, 160, + ]), + Bytes32([ + 193, 249, 209, 245, 102, 95, 15, 55, 160, 156, 203, 160, 49, 198, 203, 30, 99, 172, 89, 92, + 235, 89, 131, 205, 53, 254, 219, 168, 202, 195, 10, 242, + ]), + Bytes32([ + 131, 64, 102, 7, 226, 96, 60, 111, 221, 120, 41, 86, 239, 157, 121, 38, 57, 244, 221, 55, + 32, 237, 13, 20, 244, 238, 133, 189, 89, 118, 204, 247, + ]), + Bytes32([ + 239, 211, 89, 185, 191, 211, 243, 207, 134, 159, 59, 19, 87, 254, 115, 27, 152, 156, 120, + 226, 39, 63, 155, 51, 77, 130, 162, 63, 16, 43, 112, 121, + ]), + Bytes32([ + 10, 146, 190, 23, 235, 67, 23, 169, 33, 140, 54, 209, 129, 176, 29, 218, 40, 55, 13, 80, + 57, 68, 92, 97, 44, 82, 209, 240, 207, 214, 22, 119, + ]), + Bytes32([ + 59, 161, 125, 95, 7, 155, 141, 55, 253, 106, 238, 72, 109, 158, 201, 200, 236, 157, 227, + 178, 176, 47, 166, 84, 87, 111, 25, 133, 194, 55, 244, 254, + ]), + Bytes32([ + 8, 134, 213, 182, 108, 15, 225, 138, 152, 138, 132, 240, 27, 80, 231, 117, 194, 157, 205, + 20, 110, 239, 166, 37, 50, 46, 213, 165, 216, 101, 245, 98, + ]), + Bytes32([ + 137, 57, 186, 173, 198, 113, 221, 42, 0, 211, 127, 200, 128, 7, 112, 97, 75, 155, 152, 224, + 71, 220, 16, 124, 32, 50, 30, 48, 251, 132, 120, 147, + ]), + Bytes32([ + 201, 8, 226, 108, 147, 192, 3, 240, 168, 237, 131, 47, 150, 51, 251, 126, 23, 182, 223, 99, + 162, 167, 248, 248, 233, 8, 121, 99, 71, 212, 120, 251, + ]), + Bytes32([ + 140, 30, 196, 244, 166, 80, 95, 116, 140, 218, 94, 20, 236, 233, 241, 129, 175, 187, 110, + 88, 174, 173, 152, 203, 67, 75, 51, 232, 225, 6, 17, 134, + ]), + Bytes32([ + 92, 132, 115, 164, 66, 159, 201, 147, 233, 228, 16, 95, 81, 233, 129, 254, 155, 148, 38, + 239, 186, 40, 118, 104, 144, 134, 17, 46, 99, 150, 211, 9, + ]), + Bytes32([ + 81, 73, 72, 117, 215, 159, 240, 86, 136, 196, 69, 201, 149, 208, 48, 245, 105, 153, 97, + 107, 221, 85, 49, 121, 197, 129, 32, 140, 206, 231, 120, 123, + ]), + Bytes32([ + 0, 203, 34, 135, 242, 129, 113, 204, 198, 134, 219, 134, 187, 240, 235, 59, 44, 163, 180, + 83, 174, 40, 66, 141, 95, 99, 71, 221, 186, 216, 37, 250, + ]), + Bytes32([ + 225, 181, 151, 12, 26, 209, 127, 99, 248, 177, 126, 113, 215, 220, 252, 89, 149, 123, 19, + 65, 89, 122, 180, 169, 195, 87, 147, 9, 196, 31, 203, 178, + ]), + Bytes32([ + 125, 81, 154, 100, 204, 138, 194, 248, 156, 95, 119, 58, 60, 221, 150, 248, 216, 19, 123, + 15, 198, 108, 228, 228, 224, 122, 16, 249, 223, 84, 117, 89, + ]), + Bytes32([ + 99, 153, 60, 217, 168, 254, 244, 149, 214, 115, 250, 90, 117, 112, 66, 163, 143, 207, 121, + 70, 108, 92, 2, 47, 56, 109, 56, 207, 152, 27, 196, 141, + ]), + Bytes32([ + 99, 251, 124, 97, 158, 155, 101, 251, 98, 124, 239, 73, 50, 50, 173, 142, 39, 208, 167, 53, + 5, 151, 85, 239, 25, 77, 36, 52, 141, 225, 66, 186, + ]), + Bytes32([ + 235, 44, 31, 180, 197, 243, 114, 60, 199, 242, 167, 21, 206, 27, 116, 165, 18, 76, 162, + 163, 14, 91, 109, 104, 118, 51, 33, 172, 9, 165, 214, 38, + ]), + Bytes32([ + 188, 4, 5, 50, 169, 70, 231, 90, 220, 90, 226, 214, 89, 19, 159, 207, 201, 80, 241, 214, + 32, 240, 63, 74, 6, 251, 177, 182, 236, 19, 156, 92, + ]), + Bytes32([ + 128, 150, 66, 27, 197, 229, 103, 137, 175, 252, 19, 131, 213, 63, 112, 172, 182, 255, 235, + 246, 226, 37, 25, 180, 24, 38, 35, 138, 207, 149, 186, 175, + ]), + Bytes32([ + 5, 123, 231, 106, 12, 171, 157, 189, 58, 109, 144, 7, 38, 97, 4, 148, 1, 138, 124, 123, 2, + 202, 108, 221, 194, 160, 53, 73, 115, 36, 231, 142, + ]), + Bytes32([ + 160, 157, 243, 224, 214, 120, 59, 206, 24, 30, 231, 209, 123, 227, 15, 221, 86, 30, 128, + 117, 210, 109, 132, 117, 204, 87, 243, 213, 231, 224, 91, 75, + ]), + Bytes32([ + 74, 238, 110, 147, 63, 110, 246, 102, 252, 212, 120, 225, 124, 218, 145, 204, 129, 63, 13, + 34, 1, 0, 15, 9, 27, 221, 39, 199, 24, 231, 9, 38, + ]), + Bytes32([ + 194, 20, 160, 161, 90, 2, 118, 75, 103, 228, 51, 42, 191, 220, 63, 150, 140, 32, 85, 172, + 218, 29, 251, 31, 99, 96, 98, 46, 130, 157, 201, 61, + ]), + Bytes32([ + 88, 123, 95, 139, 94, 66, 69, 27, 223, 223, 216, 130, 99, 210, 130, 54, 248, 35, 226, 252, + 151, 238, 7, 175, 12, 97, 188, 43, 233, 129, 82, 222, + ]), + Bytes32([ + 179, 248, 157, 243, 50, 157, 172, 17, 139, 10, 245, 208, 241, 210, 67, 199, 135, 233, 234, + 39, 127, 38, 54, 39, 40, 159, 94, 122, 36, 221, 180, 195, + ]), + Bytes32([ + 190, 185, 147, 237, 82, 32, 165, 236, 252, 183, 173, 181, 68, 44, 216, 20, 110, 243, 158, + 213, 88, 209, 218, 207, 244, 241, 121, 239, 136, 167, 245, 155, + ]), + Bytes32([ + 114, 131, 254, 248, 183, 0, 175, 217, 194, 58, 178, 238, 250, 119, 158, 205, 19, 74, 94, + 35, 100, 175, 114, 55, 186, 192, 239, 126, 42, 86, 60, 119, + ]), + Bytes32([ + 1, 253, 107, 150, 25, 240, 27, 172, 252, 185, 151, 152, 107, 105, 164, 230, 221, 242, 7, + 166, 142, 53, 237, 83, 19, 30, 81, 149, 150, 39, 123, 220, + ]), + Bytes32([ + 57, 240, 92, 232, 26, 238, 69, 252, 110, 171, 164, 109, 199, 73, 110, 49, 11, 253, 109, 69, + 155, 26, 84, 52, 155, 204, 115, 99, 90, 38, 247, 167, + ]), + Bytes32([ + 46, 87, 146, 25, 215, 40, 208, 78, 73, 60, 35, 19, 194, 41, 34, 186, 215, 131, 205, 29, + 107, 100, 95, 169, 45, 1, 242, 115, 30, 226, 93, 48, + ]), + Bytes32([ + 95, 183, 66, 0, 196, 173, 44, 27, 175, 151, 17, 0, 159, 168, 123, 236, 124, 198, 85, 181, + 118, 89, 21, 52, 169, 104, 178, 111, 120, 173, 94, 109, + ]), + Bytes32([ + 13, 111, 162, 25, 133, 108, 250, 75, 197, 221, 19, 160, 147, 36, 130, 70, 224, 39, 134, + 144, 149, 166, 187, 96, 155, 154, 123, 90, 241, 154, 44, 102, + ]), + Bytes32([ + 95, 171, 183, 135, 121, 205, 161, 160, 212, 226, 64, 232, 180, 166, 176, 95, 51, 96, 110, + 20, 76, 143, 47, 96, 197, 132, 210, 109, 174, 132, 13, 95, + ]), + Bytes32([ + 66, 51, 176, 1, 106, 199, 192, 16, 106, 180, 212, 53, 72, 121, 218, 148, 77, 26, 15, 244, + 124, 131, 88, 84, 147, 211, 254, 156, 187, 91, 193, 120, + ]), +]; + +const FUNCTION_HASHES: &[Bytes32; 64] = &[ + Bytes32([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + ]), + Bytes32([ + 239, 247, 49, 255, 201, 159, 228, 149, 22, 115, 41, 139, 56, 117, 149, 46, 67, 155, 98, 70, + 216, 225, 85, 100, 113, 242, 144, 167, 97, 238, 117, 207, + ]), + Bytes32([ + 46, 87, 136, 101, 122, 12, 6, 120, 39, 121, 167, 193, 66, 146, 150, 1, 90, 240, 97, 225, + 179, 150, 197, 127, 56, 214, 116, 24, 180, 109, 96, 51, + ]), + Bytes32([ + 54, 125, 90, 195, 243, 1, 115, 18, 251, 68, 203, 216, 48, 156, 216, 200, 215, 250, 80, 152, + 203, 58, 245, 128, 70, 112, 221, 104, 34, 125, 234, 138, + ]), + Bytes32([ + 103, 99, 44, 86, 57, 0, 102, 201, 99, 214, 249, 24, 45, 112, 234, 183, 225, 196, 9, 162, + 168, 33, 121, 118, 93, 184, 14, 70, 148, 135, 211, 214, + ]), + Bytes32([ + 69, 7, 89, 47, 41, 94, 148, 139, 67, 197, 99, 98, 100, 63, 25, 230, 100, 118, 213, 25, 28, + 129, 5, 39, 69, 89, 71, 21, 102, 84, 101, 175, + ]), + Bytes32([ + 232, 195, 82, 123, 189, 200, 139, 61, 179, 141, 208, 69, 102, 58, 127, 66, 69, 127, 7, 179, + 108, 128, 253, 154, 207, 254, 201, 18, 192, 241, 99, 208, + ]), + Bytes32([ + 138, 25, 185, 215, 159, 159, 214, 251, 161, 103, 180, 78, 209, 215, 131, 132, 134, 180, + 177, 37, 79, 219, 19, 93, 45, 30, 53, 125, 142, 194, 114, 34, + ]), + Bytes32([ + 185, 24, 200, 28, 68, 7, 118, 226, 65, 240, 85, 120, 123, 174, 239, 85, 247, 210, 104, 247, + 20, 229, 41, 91, 242, 5, 2, 121, 132, 69, 43, 33, + ]), + Bytes32([ + 78, 191, 92, 97, 205, 120, 114, 216, 112, 101, 44, 98, 148, 2, 112, 221, 168, 199, 254, 55, + 160, 116, 116, 105, 109, 227, 130, 219, 194, 32, 209, 196, + ]), + Bytes32([ + 177, 203, 26, 1, 216, 115, 242, 158, 202, 107, 60, 221, 81, 167, 8, 233, 247, 235, 49, 54, + 45, 133, 202, 193, 34, 158, 205, 29, 232, 245, 195, 194, + ]), + Bytes32([ + 193, 118, 182, 153, 10, 237, 67, 229, 136, 193, 253, 250, 46, 125, 141, 22, 107, 168, 74, + 130, 60, 193, 194, 196, 249, 129, 153, 82, 162, 143, 188, 42, + ]), + Bytes32([ + 239, 57, 105, 90, 116, 61, 160, 214, 55, 191, 17, 228, 44, 149, 103, 180, 229, 4, 175, 240, + 209, 231, 141, 95, 109, 195, 78, 142, 122, 177, 227, 20, + ]), + Bytes32([ + 153, 247, 105, 126, 178, 44, 190, 57, 12, 63, 169, 27, 206, 19, 151, 38, 33, 83, 35, 3, + 177, 97, 36, 182, 37, 78, 150, 243, 222, 32, 180, 148, + ]), + Bytes32([ + 29, 109, 110, 167, 27, 136, 235, 226, 147, 175, 241, 236, 138, 135, 163, 200, 21, 94, 220, + 238, 23, 84, 46, 14, 44, 100, 47, 131, 134, 43, 98, 173, + ]), + Bytes32([ + 73, 241, 108, 102, 184, 200, 64, 80, 58, 162, 29, 211, 173, 150, 6, 195, 2, 137, 246, 51, + 155, 132, 41, 255, 21, 71, 74, 143, 197, 180, 217, 159, + ]), + Bytes32([ + 109, 189, 226, 33, 70, 157, 161, 166, 223, 31, 224, 221, 37, 112, 27, 215, 163, 250, 217, + 155, 168, 121, 119, 232, 100, 177, 104, 210, 10, 118, 215, 0, + ]), + Bytes32([ + 175, 156, 32, 156, 238, 82, 62, 25, 96, 251, 146, 24, 13, 2, 115, 174, 163, 50, 14, 251, + 196, 173, 5, 87, 171, 228, 191, 9, 90, 84, 156, 63, + ]), + Bytes32([ + 244, 161, 238, 217, 14, 60, 162, 139, 34, 84, 68, 121, 213, 11, 230, 95, 57, 221, 174, 107, + 103, 105, 76, 137, 58, 247, 65, 209, 167, 63, 96, 212, + ]), + Bytes32([ + 31, 177, 221, 130, 152, 92, 190, 4, 59, 90, 122, 63, 128, 180, 140, 201, 75, 239, 200, 151, + 94, 65, 36, 227, 21, 250, 10, 253, 26, 114, 238, 188, + ]), + Bytes32([ + 48, 216, 87, 30, 113, 214, 69, 1, 9, 112, 244, 232, 127, 55, 43, 162, 156, 83, 235, 199, + 126, 132, 241, 106, 65, 209, 240, 32, 11, 179, 219, 135, + ]), + Bytes32([ + 20, 55, 225, 132, 0, 219, 175, 17, 235, 73, 236, 16, 24, 227, 250, 155, 32, 83, 234, 123, + 191, 209, 157, 133, 215, 202, 3, 221, 135, 223, 221, 90, + ]), + Bytes32([ + 113, 185, 118, 164, 139, 219, 203, 140, 245, 123, 21, 186, 102, 22, 54, 14, 19, 230, 223, + 120, 211, 114, 203, 77, 173, 214, 61, 80, 151, 10, 2, 205, + ]), + Bytes32([ + 50, 20, 214, 105, 232, 5, 9, 101, 16, 72, 46, 139, 177, 188, 25, 93, 14, 57, 60, 168, 49, + 147, 134, 5, 62, 181, 120, 75, 193, 229, 107, 67, + ]), + Bytes32([ + 92, 167, 117, 18, 186, 193, 28, 56, 86, 25, 75, 64, 1, 8, 126, 212, 56, 163, 157, 242, 34, + 73, 65, 58, 187, 2, 112, 58, 225, 229, 113, 58, + ]), + Bytes32([ + 173, 139, 156, 215, 250, 231, 15, 123, 8, 5, 98, 192, 164, 48, 106, 203, 152, 189, 118, + 120, 245, 221, 214, 26, 79, 39, 148, 232, 217, 88, 34, 246, + ]), + Bytes32([ + 8, 207, 11, 196, 253, 51, 182, 90, 62, 110, 125, 142, 209, 154, 5, 203, 252, 62, 117, 215, + 226, 136, 140, 201, 227, 210, 71, 34, 113, 160, 42, 68, + ]), + Bytes32([ + 108, 135, 226, 117, 179, 142, 93, 181, 20, 186, 189, 217, 49, 154, 66, 244, 180, 170, 122, + 233, 153, 214, 104, 71, 44, 57, 103, 240, 97, 80, 170, 198, + ]), + Bytes32([ + 123, 168, 184, 254, 241, 78, 23, 91, 32, 77, 218, 176, 179, 189, 61, 162, 114, 158, 212, 7, + 201, 15, 58, 4, 161, 211, 188, 156, 190, 2, 20, 183, + ]), + Bytes32([ + 60, 170, 86, 130, 38, 170, 202, 88, 97, 206, 67, 69, 140, 122, 201, 38, 70, 229, 144, 202, + 146, 10, 146, 148, 21, 247, 119, 196, 150, 223, 110, 67, + ]), + Bytes32([ + 102, 154, 215, 132, 143, 248, 151, 101, 101, 253, 80, 211, 254, 64, 179, 73, 96, 132, 144, + 216, 225, 130, 48, 56, 14, 39, 97, 223, 134, 10, 124, 138, + ]), + Bytes32([ + 197, 131, 194, 166, 168, 160, 24, 116, 47, 120, 89, 3, 44, 154, 83, 250, 142, 64, 77, 63, + 15, 60, 97, 100, 150, 39, 227, 139, 42, 146, 146, 104, + ]), + Bytes32([ + 7, 160, 246, 93, 64, 79, 76, 208, 66, 80, 60, 123, 13, 51, 115, 251, 196, 209, 145, 106, + 61, 105, 136, 58, 7, 129, 142, 203, 205, 69, 45, 203, + ]), + Bytes32([ + 64, 162, 48, 187, 213, 182, 41, 85, 62, 148, 26, 161, 227, 108, 173, 249, 195, 106, 203, + 18, 28, 59, 184, 85, 249, 16, 224, 222, 114, 200, 99, 22, + ]), + Bytes32([ + 246, 194, 74, 122, 165, 216, 211, 144, 101, 143, 134, 9, 53, 229, 197, 227, 121, 248, 89, + 169, 48, 220, 104, 216, 117, 118, 36, 234, 108, 135, 81, 162, + ]), + Bytes32([ + 232, 59, 196, 2, 103, 242, 44, 129, 41, 231, 226, 115, 238, 151, 166, 210, 158, 110, 43, + 45, 86, 163, 127, 8, 162, 81, 131, 145, 165, 252, 170, 12, + ]), + Bytes32([ + 204, 88, 102, 180, 241, 148, 27, 183, 223, 71, 157, 119, 155, 31, 102, 239, 4, 83, 204, + 246, 38, 228, 234, 20, 97, 233, 203, 149, 186, 67, 143, 51, + ]), + Bytes32([ + 246, 170, 233, 8, 202, 38, 67, 219, 175, 13, 250, 174, 98, 36, 84, 166, 6, 75, 27, 43, 62, + 128, 106, 23, 163, 191, 86, 115, 164, 50, 127, 151, + ]), + Bytes32([ + 204, 72, 199, 57, 106, 136, 244, 66, 217, 155, 183, 211, 166, 71, 140, 211, 210, 181, 178, + 218, 204, 91, 200, 204, 153, 60, 22, 32, 24, 153, 131, 31, + ]), + Bytes32([ + 204, 193, 13, 167, 214, 136, 121, 99, 198, 137, 11, 223, 85, 40, 167, 126, 153, 80, 187, + 35, 226, 31, 226, 42, 9, 175, 31, 206, 119, 67, 153, 39, + ]), + Bytes32([ + 63, 82, 16, 182, 255, 45, 123, 220, 252, 52, 199, 222, 76, 172, 123, 232, 133, 182, 143, + 202, 223, 161, 203, 91, 239, 225, 123, 184, 231, 53, 19, 253, + ]), + Bytes32([ + 5, 13, 255, 217, 70, 105, 205, 70, 41, 25, 44, 3, 245, 136, 16, 255, 101, 61, 189, 225, + 142, 201, 210, 226, 9, 237, 171, 109, 63, 204, 204, 139, + ]), + Bytes32([ + 161, 218, 126, 252, 32, 196, 32, 105, 98, 13, 201, 117, 64, 37, 232, 32, 159, 106, 48, 121, + 250, 139, 219, 137, 129, 3, 83, 194, 102, 251, 148, 71, + ]), + Bytes32([ + 72, 17, 9, 21, 142, 89, 135, 91, 81, 155, 72, 247, 56, 232, 136, 57, 251, 182, 211, 35, + 207, 169, 87, 123, 31, 222, 72, 85, 212, 92, 159, 171, + ]), + Bytes32([ + 193, 253, 238, 224, 198, 216, 154, 52, 59, 87, 183, 80, 242, 162, 238, 7, 218, 195, 253, + 210, 92, 116, 43, 181, 109, 40, 74, 38, 154, 211, 102, 173, + ]), + Bytes32([ + 193, 88, 249, 81, 11, 128, 242, 239, 220, 1, 237, 199, 235, 12, 153, 73, 145, 158, 15, 111, + 108, 244, 19, 51, 159, 206, 66, 25, 147, 215, 157, 165, + ]), + Bytes32([ + 195, 90, 0, 163, 8, 199, 55, 59, 47, 117, 186, 204, 5, 42, 205, 164, 239, 233, 203, 210, + 69, 205, 244, 152, 113, 184, 206, 110, 101, 197, 120, 101, + ]), + Bytes32([ + 114, 169, 222, 11, 169, 15, 123, 245, 42, 25, 120, 198, 66, 140, 125, 123, 201, 107, 3, 50, + 245, 226, 52, 46, 162, 44, 134, 157, 226, 11, 115, 186, + ]), + Bytes32([ + 6, 47, 131, 89, 216, 138, 92, 250, 126, 232, 238, 50, 181, 78, 69, 115, 227, 25, 49, 50, + 116, 138, 47, 61, 179, 75, 121, 124, 127, 106, 228, 178, + ]), + Bytes32([ + 152, 253, 228, 228, 120, 255, 82, 66, 24, 145, 159, 223, 39, 185, 81, 201, 81, 110, 47, 57, + 105, 128, 8, 42, 130, 119, 92, 7, 14, 247, 137, 135, + ]), + Bytes32([ + 162, 28, 11, 245, 48, 21, 8, 137, 161, 131, 84, 18, 129, 70, 197, 122, 165, 39, 71, 213, + 142, 115, 159, 52, 46, 213, 81, 10, 217, 212, 74, 20, + ]), + Bytes32([ + 129, 250, 71, 119, 46, 25, 220, 212, 143, 120, 0, 101, 85, 101, 109, 107, 142, 20, 107, + 174, 39, 134, 128, 244, 237, 155, 190, 19, 110, 10, 240, 94, + ]), + Bytes32([ + 90, 70, 190, 48, 191, 45, 227, 17, 185, 162, 159, 12, 18, 94, 186, 251, 252, 238, 95, 174, + 253, 99, 194, 204, 5, 178, 117, 139, 50, 154, 93, 170, + ]), + Bytes32([ + 201, 8, 48, 126, 70, 22, 83, 82, 229, 84, 168, 86, 100, 169, 227, 175, 196, 154, 217, 150, + 2, 36, 1, 100, 48, 216, 141, 205, 113, 148, 109, 126, + ]), + Bytes32([ + 16, 182, 10, 119, 255, 70, 127, 242, 64, 68, 67, 44, 52, 195, 63, 185, 227, 97, 138, 244, + 53, 246, 110, 99, 41, 176, 146, 206, 81, 168, 66, 140, + ]), + Bytes32([ + 95, 9, 211, 227, 219, 119, 221, 132, 248, 92, 236, 190, 110, 227, 153, 109, 103, 63, 121, + 221, 165, 212, 20, 141, 33, 95, 52, 157, 69, 76, 2, 70, + ]), + Bytes32([ + 216, 138, 115, 198, 102, 135, 226, 247, 235, 185, 206, 147, 130, 9, 48, 166, 92, 22, 41, + 158, 169, 52, 233, 1, 60, 229, 165, 208, 60, 161, 194, 166, + ]), + Bytes32([ + 129, 241, 87, 138, 103, 241, 79, 26, 52, 35, 125, 237, 54, 147, 253, 137, 101, 146, 235, + 127, 124, 113, 60, 203, 181, 4, 250, 12, 72, 27, 24, 7, + ]), + Bytes32([ + 52, 120, 68, 97, 147, 229, 251, 178, 161, 233, 142, 136, 38, 40, 85, 46, 121, 156, 115, + 186, 72, 95, 128, 43, 252, 176, 176, 51, 113, 83, 154, 232, + ]), + Bytes32([ + 227, 11, 188, 57, 98, 160, 248, 247, 112, 49, 247, 92, 94, 224, 135, 182, 211, 230, 69, + 185, 138, 154, 141, 157, 18, 34, 138, 236, 67, 243, 26, 132, + ]), + Bytes32([ + 61, 246, 31, 52, 44, 146, 190, 215, 214, 105, 253, 86, 172, 174, 140, 112, 30, 154, 82, + 173, 62, 199, 157, 180, 255, 104, 73, 136, 163, 79, 210, 252, + ]), + Bytes32([ + 200, 155, 200, 87, 142, 123, 62, 176, 250, 44, 18, 238, 229, 121, 125, 109, 102, 173, 60, + 203, 88, 65, 33, 122, 22, 111, 157, 143, 164, 7, 214, 26, + ]), + Bytes32([ + 179, 76, 168, 179, 139, 86, 151, 173, 26, 70, 236, 116, 202, 123, 63, 80, 106, 208, 63, + 138, 179, 225, 114, 64, 48, 34, 78, 124, 132, 210, 224, 190, + ]), + Bytes32([ + 138, 116, 89, 240, 28, 79, 42, 149, 199, 118, 149, 49, 146, 212, 104, 62, 17, 137, 138, + 223, 225, 187, 152, 43, 134, 60, 95, 55, 175, 128, 95, 144, + ]), +]; + +const INSTRUCTION_HASHES: &[Bytes32; 64] = &[ + Bytes32([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + ]), + Bytes32([ + 248, 140, 83, 29, 241, 220, 66, 40, 126, 248, 5, 153, 77, 179, 26, 64, 221, 108, 205, 162, + 173, 52, 179, 140, 202, 105, 0, 170, 166, 130, 120, 176, + ]), + Bytes32([ + 35, 74, 103, 235, 245, 71, 66, 212, 99, 147, 15, 200, 1, 237, 95, 163, 10, 146, 122, 251, + 243, 239, 93, 186, 46, 74, 206, 130, 63, 17, 199, 80, + ]), + Bytes32([ + 113, 169, 40, 37, 169, 205, 28, 78, 37, 104, 52, 28, 29, 222, 39, 32, 208, 183, 215, 38, + 255, 69, 87, 70, 174, 113, 221, 183, 206, 62, 192, 118, + ]), + Bytes32([ + 1, 148, 183, 39, 234, 23, 138, 182, 169, 58, 52, 158, 248, 212, 186, 127, 60, 42, 25, 210, + 20, 170, 182, 139, 20, 185, 196, 85, 241, 214, 181, 64, + ]), + Bytes32([ + 241, 14, 59, 8, 168, 222, 232, 125, 149, 227, 125, 41, 223, 198, 88, 135, 54, 146, 140, + 212, 164, 76, 239, 106, 10, 20, 67, 180, 24, 159, 174, 99, + ]), + Bytes32([ + 117, 111, 220, 19, 204, 227, 211, 18, 61, 239, 179, 46, 142, 75, 67, 98, 159, 221, 205, + 158, 125, 42, 221, 227, 148, 162, 193, 229, 147, 70, 214, 127, + ]), + Bytes32([ + 20, 72, 211, 188, 38, 126, 166, 75, 54, 22, 225, 21, 113, 155, 167, 89, 161, 6, 37, 78, + 227, 211, 219, 141, 161, 199, 151, 122, 81, 45, 159, 187, + ]), + Bytes32([ + 69, 198, 100, 170, 106, 174, 208, 152, 152, 121, 118, 66, 111, 89, 131, 14, 159, 115, 142, + 200, 205, 173, 89, 77, 68, 234, 5, 171, 105, 248, 199, 83, + ]), + Bytes32([ + 17, 1, 220, 13, 154, 194, 88, 60, 253, 223, 149, 35, 44, 184, 107, 141, 205, 177, 185, 32, + 22, 99, 212, 96, 194, 91, 255, 179, 121, 153, 254, 40, + ]), + Bytes32([ + 222, 247, 186, 138, 222, 183, 135, 183, 170, 79, 182, 23, 69, 89, 70, 68, 88, 87, 235, 22, + 178, 134, 114, 220, 249, 153, 70, 71, 229, 139, 10, 190, + ]), + Bytes32([ + 183, 77, 65, 51, 207, 78, 55, 149, 222, 3, 100, 82, 232, 230, 21, 115, 249, 177, 36, 89, + 12, 41, 70, 31, 199, 254, 213, 3, 184, 2, 85, 137, + ]), + Bytes32([ + 82, 15, 215, 135, 95, 18, 143, 49, 239, 237, 6, 15, 213, 113, 153, 49, 149, 54, 50, 139, + 100, 192, 128, 226, 168, 9, 120, 167, 157, 42, 45, 19, + ]), + Bytes32([ + 70, 32, 130, 243, 186, 144, 224, 217, 130, 222, 175, 221, 218, 190, 179, 222, 217, 115, 70, + 160, 141, 231, 221, 96, 2, 173, 27, 17, 158, 66, 180, 58, + ]), + Bytes32([ + 76, 243, 65, 235, 12, 31, 48, 195, 62, 176, 202, 57, 99, 80, 237, 222, 138, 199, 124, 251, + 143, 243, 205, 226, 18, 164, 67, 64, 68, 238, 53, 71, + ]), + Bytes32([ + 70, 52, 254, 193, 211, 92, 63, 238, 92, 2, 160, 29, 154, 25, 221, 136, 2, 49, 96, 27, 15, + 152, 238, 57, 241, 253, 32, 131, 73, 38, 229, 243, + ]), + Bytes32([ + 214, 162, 82, 44, 146, 145, 61, 77, 36, 3, 131, 81, 217, 39, 127, 81, 66, 82, 172, 230, + 246, 3, 243, 217, 210, 38, 201, 109, 240, 143, 253, 79, + ]), + Bytes32([ + 127, 18, 198, 116, 112, 141, 16, 39, 115, 118, 37, 208, 118, 119, 147, 46, 196, 124, 19, + 196, 34, 183, 172, 157, 18, 91, 4, 210, 77, 195, 220, 59, + ]), + Bytes32([ + 87, 54, 42, 60, 39, 223, 84, 148, 158, 230, 70, 53, 191, 1, 108, 103, 139, 111, 58, 220, + 114, 46, 46, 110, 20, 211, 84, 1, 173, 104, 175, 191, + ]), + Bytes32([ + 182, 147, 66, 127, 229, 144, 125, 93, 84, 220, 116, 107, 51, 13, 24, 161, 68, 172, 165, 50, + 12, 198, 229, 173, 49, 26, 114, 159, 84, 190, 6, 42, + ]), + Bytes32([ + 183, 20, 36, 145, 76, 36, 61, 136, 224, 139, 89, 23, 124, 29, 210, 137, 1, 160, 22, 128, + 48, 242, 234, 85, 84, 24, 206, 49, 143, 185, 237, 110, + ]), + Bytes32([ + 38, 19, 77, 150, 92, 129, 177, 155, 139, 162, 90, 91, 231, 146, 216, 21, 54, 152, 18, 5, + 98, 21, 67, 160, 154, 113, 175, 65, 214, 78, 19, 97, + ]), + Bytes32([ + 163, 236, 47, 120, 121, 17, 217, 4, 200, 247, 206, 178, 211, 212, 163, 48, 38, 172, 111, + 123, 75, 221, 63, 117, 42, 3, 252, 233, 104, 13, 210, 203, + ]), + Bytes32([ + 127, 106, 132, 246, 28, 129, 245, 83, 159, 109, 196, 219, 68, 81, 230, 77, 254, 166, 55, + 73, 169, 179, 93, 97, 117, 176, 53, 74, 121, 238, 184, 240, + ]), + Bytes32([ + 255, 23, 108, 186, 145, 182, 196, 67, 163, 95, 173, 17, 67, 239, 199, 189, 145, 153, 120, + 152, 159, 126, 255, 39, 55, 178, 230, 173, 91, 165, 229, 106, + ]), + Bytes32([ + 184, 156, 20, 114, 222, 219, 89, 218, 102, 62, 130, 200, 179, 237, 32, 237, 127, 75, 251, + 163, 160, 2, 221, 34, 166, 30, 172, 97, 115, 137, 179, 175, + ]), + Bytes32([ + 244, 165, 127, 27, 66, 22, 38, 243, 235, 107, 30, 181, 31, 193, 210, 174, 254, 129, 39, 71, + 59, 117, 186, 37, 217, 129, 138, 23, 146, 114, 96, 78, + ]), + Bytes32([ + 27, 57, 244, 93, 208, 90, 64, 181, 161, 110, 161, 188, 62, 180, 46, 103, 136, 233, 63, 1, + 243, 202, 236, 206, 173, 45, 45, 218, 238, 184, 83, 121, + ]), + Bytes32([ + 138, 185, 33, 206, 120, 214, 246, 27, 100, 91, 106, 213, 60, 177, 39, 182, 149, 204, 11, + 225, 254, 5, 87, 9, 19, 186, 166, 210, 33, 67, 146, 37, + ]), + Bytes32([ + 243, 161, 7, 52, 35, 240, 227, 144, 29, 50, 232, 3, 101, 174, 192, 139, 89, 75, 204, 245, + 173, 55, 139, 76, 126, 19, 142, 18, 230, 57, 219, 227, + ]), + Bytes32([ + 97, 73, 36, 70, 120, 55, 49, 114, 107, 193, 66, 219, 47, 122, 201, 194, 52, 142, 12, 163, + 108, 2, 107, 58, 185, 251, 168, 146, 57, 189, 150, 134, + ]), + Bytes32([ + 11, 215, 29, 229, 151, 208, 62, 1, 172, 253, 253, 156, 177, 31, 117, 214, 161, 192, 68, + 255, 140, 131, 66, 23, 247, 161, 107, 98, 199, 57, 123, 110, + ]), + Bytes32([ + 41, 166, 54, 134, 242, 64, 153, 167, 5, 230, 226, 128, 71, 10, 251, 160, 19, 187, 70, 4, + 95, 98, 77, 233, 181, 24, 11, 239, 142, 152, 229, 215, + ]), + Bytes32([ + 221, 220, 68, 168, 143, 124, 235, 77, 241, 190, 214, 6, 102, 40, 32, 245, 131, 114, 170, + 151, 134, 49, 79, 57, 242, 124, 85, 172, 83, 81, 68, 99, + ]), + Bytes32([ + 142, 200, 102, 97, 170, 43, 14, 11, 139, 207, 150, 163, 99, 103, 2, 46, 44, 63, 109, 61, + 62, 64, 199, 104, 36, 194, 155, 232, 139, 250, 97, 224, + ]), + Bytes32([ + 162, 15, 26, 217, 96, 195, 70, 196, 253, 70, 127, 156, 39, 120, 120, 232, 197, 154, 192, + 147, 104, 33, 104, 189, 120, 160, 36, 129, 208, 237, 128, 23, + ]), + Bytes32([ + 42, 228, 74, 47, 20, 55, 197, 252, 173, 70, 178, 8, 2, 43, 97, 238, 155, 124, 87, 225, 217, + 6, 103, 134, 136, 235, 193, 175, 33, 228, 219, 217, + ]), + Bytes32([ + 219, 143, 25, 70, 78, 155, 119, 63, 126, 20, 158, 54, 27, 181, 104, 184, 74, 192, 75, 157, + 42, 204, 45, 122, 25, 248, 247, 24, 225, 207, 51, 251, + ]), + Bytes32([ + 151, 253, 36, 33, 104, 222, 126, 234, 112, 74, 59, 94, 239, 58, 36, 114, 90, 195, 208, 46, + 250, 138, 165, 200, 178, 54, 238, 154, 219, 238, 6, 183, + ]), + Bytes32([ + 205, 57, 187, 174, 221, 9, 164, 77, 76, 143, 54, 212, 115, 196, 67, 182, 200, 147, 78, 194, + 245, 92, 173, 114, 180, 160, 168, 5, 200, 82, 217, 64, + ]), + Bytes32([ + 211, 104, 62, 114, 157, 56, 254, 26, 32, 168, 208, 248, 62, 195, 142, 68, 53, 13, 7, 36, + 216, 5, 127, 153, 97, 88, 222, 206, 91, 42, 14, 114, + ]), + Bytes32([ + 203, 158, 225, 200, 62, 223, 202, 52, 207, 179, 250, 236, 29, 144, 181, 31, 87, 192, 203, + 66, 56, 39, 123, 25, 123, 157, 196, 161, 34, 245, 22, 130, + ]), + Bytes32([ + 147, 247, 233, 96, 190, 104, 227, 251, 99, 2, 16, 8, 77, 119, 1, 97, 243, 78, 103, 46, 24, + 228, 217, 65, 219, 238, 135, 18, 164, 219, 132, 39, + ]), + Bytes32([ + 239, 153, 72, 139, 52, 60, 171, 44, 72, 218, 138, 124, 45, 30, 143, 122, 147, 158, 12, 62, + 213, 151, 217, 217, 212, 51, 110, 75, 122, 67, 134, 80, + ]), + Bytes32([ + 46, 47, 130, 237, 178, 232, 239, 217, 165, 114, 255, 202, 132, 214, 18, 8, 72, 8, 255, 0, + 162, 82, 5, 228, 141, 201, 70, 135, 255, 21, 220, 3, + ]), + Bytes32([ + 21, 11, 196, 5, 215, 241, 194, 200, 198, 129, 133, 244, 204, 47, 131, 70, 138, 38, 107, 39, + 211, 243, 72, 72, 34, 145, 35, 42, 14, 69, 218, 227, + ]), + Bytes32([ + 73, 103, 117, 195, 67, 126, 242, 57, 232, 74, 142, 64, 169, 248, 113, 69, 110, 103, 127, + 185, 177, 51, 212, 178, 48, 183, 247, 44, 246, 69, 35, 208, + ]), + Bytes32([ + 125, 8, 83, 22, 215, 82, 179, 234, 169, 227, 213, 39, 170, 164, 195, 187, 63, 38, 59, 146, + 144, 101, 38, 0, 110, 212, 202, 20, 245, 105, 187, 201, + ]), + Bytes32([ + 186, 231, 186, 148, 68, 208, 226, 170, 120, 80, 68, 79, 23, 41, 51, 205, 240, 201, 10, 16, + 24, 38, 137, 151, 7, 102, 92, 244, 3, 194, 228, 229, + ]), + Bytes32([ + 46, 8, 51, 150, 63, 114, 126, 210, 30, 130, 76, 142, 13, 149, 14, 0, 205, 199, 48, 90, 86, + 75, 151, 135, 164, 224, 129, 77, 174, 194, 212, 112, + ]), + Bytes32([ + 150, 239, 42, 111, 3, 112, 25, 168, 179, 36, 106, 73, 212, 188, 215, 79, 180, 142, 191, 29, + 219, 12, 244, 74, 254, 205, 160, 54, 68, 144, 81, 207, + ]), + Bytes32([ + 23, 164, 189, 201, 250, 65, 212, 148, 132, 125, 94, 209, 115, 123, 29, 66, 3, 172, 57, 58, + 46, 191, 96, 7, 212, 25, 226, 79, 120, 68, 202, 210, + ]), + Bytes32([ + 252, 13, 137, 225, 208, 6, 84, 75, 97, 32, 177, 156, 248, 191, 113, 97, 254, 122, 86, 194, + 3, 103, 219, 74, 227, 160, 231, 202, 13, 126, 46, 255, + ]), + Bytes32([ + 70, 35, 27, 128, 17, 79, 226, 131, 144, 118, 248, 125, 188, 110, 20, 15, 255, 73, 205, 69, + 0, 227, 26, 66, 39, 61, 191, 224, 239, 234, 83, 170, + ]), + Bytes32([ + 129, 52, 148, 121, 230, 84, 28, 112, 75, 68, 181, 197, 80, 227, 107, 22, 60, 118, 81, 209, + 198, 125, 233, 190, 84, 189, 18, 25, 72, 143, 2, 190, + ]), + Bytes32([ + 244, 237, 81, 131, 251, 35, 56, 158, 229, 102, 150, 122, 213, 206, 119, 38, 54, 238, 158, + 159, 1, 214, 31, 147, 245, 9, 172, 247, 156, 115, 209, 140, + ]), + Bytes32([ + 47, 5, 174, 62, 80, 172, 77, 111, 136, 114, 188, 85, 249, 157, 182, 175, 187, 207, 29, 43, + 152, 88, 130, 156, 138, 30, 188, 51, 82, 105, 145, 161, + ]), + Bytes32([ + 102, 2, 197, 195, 206, 149, 53, 224, 61, 219, 163, 246, 208, 218, 140, 47, 16, 42, 196, 20, + 145, 158, 101, 31, 225, 135, 49, 68, 95, 5, 228, 92, + ]), + Bytes32([ + 78, 136, 176, 216, 253, 15, 136, 48, 162, 5, 57, 120, 93, 222, 179, 174, 129, 40, 8, 63, + 18, 102, 97, 41, 58, 214, 1, 52, 58, 126, 186, 146, + ]), + Bytes32([ + 145, 56, 231, 59, 247, 191, 104, 137, 240, 113, 32, 245, 235, 129, 255, 54, 99, 176, 109, + 175, 107, 232, 201, 235, 95, 104, 54, 67, 252, 184, 200, 54, + ]), + Bytes32([ + 181, 3, 229, 152, 113, 182, 55, 93, 138, 137, 21, 67, 27, 204, 96, 69, 72, 247, 237, 216, + 28, 36, 115, 185, 111, 149, 224, 69, 66, 121, 188, 228, + ]), + Bytes32([ + 212, 25, 201, 68, 187, 25, 10, 96, 40, 88, 100, 216, 145, 200, 26, 203, 220, 14, 14, 11, + 190, 62, 143, 137, 53, 208, 233, 158, 37, 34, 8, 15, + ]), + Bytes32([ + 80, 101, 192, 136, 203, 90, 189, 204, 181, 36, 28, 107, 220, 69, 119, 99, 145, 227, 198, + 55, 37, 237, 212, 114, 31, 27, 19, 90, 231, 143, 87, 149, + ]), + Bytes32([ + 248, 97, 168, 33, 15, 230, 128, 82, 157, 26, 186, 89, 200, 7, 101, 210, 117, 111, 75, 233, + 244, 187, 1, 180, 30, 220, 15, 21, 203, 5, 243, 223, + ]), +]; + +/// These have been pre-calculated to match the current implementation of Memory::hash_leaf. +const MEMORY_HASHES: &[Bytes32; 64] = &[ + Bytes32([ + 57, 29, 211, 154, 252, 227, 18, 99, 65, 126, 203, 166, 252, 232, 32, 3, 98, 194, 254, 186, + 118, 14, 139, 192, 101, 156, 55, 194, 101, 11, 11, 168, + ]), + Bytes32([ + 190, 70, 141, 125, 34, 236, 162, 217, 92, 130, 99, 237, 252, 176, 62, 93, 182, 71, 180, + 204, 178, 127, 225, 18, 119, 68, 89, 244, 245, 143, 151, 9, + ]), + Bytes32([ + 9, 87, 12, 3, 155, 102, 114, 211, 160, 40, 161, 7, 3, 192, 11, 145, 241, 17, 215, 209, 18, + 230, 221, 198, 189, 52, 34, 224, 245, 121, 253, 194, + ]), + Bytes32([ + 10, 61, 178, 250, 4, 50, 245, 174, 54, 189, 184, 161, 208, 164, 55, 145, 70, 4, 81, 28, + 129, 97, 216, 23, 41, 192, 91, 97, 9, 83, 1, 215, + ]), + Bytes32([ + 244, 167, 192, 124, 155, 131, 227, 49, 180, 187, 181, 23, 114, 255, 114, 237, 129, 149, + 159, 244, 63, 138, 0, 237, 37, 38, 159, 106, 4, 153, 119, 255, + ]), + Bytes32([ + 63, 174, 225, 251, 118, 10, 96, 200, 132, 107, 27, 128, 250, 62, 47, 221, 227, 241, 107, + 254, 3, 171, 26, 81, 199, 162, 154, 249, 154, 72, 185, 196, + ]), + Bytes32([ + 89, 112, 167, 92, 33, 192, 100, 158, 137, 92, 61, 197, 73, 216, 188, 164, 72, 37, 5, 0, + 161, 144, 254, 18, 10, 4, 248, 174, 26, 5, 149, 142, + ]), + Bytes32([ + 202, 243, 125, 81, 15, 253, 233, 83, 91, 186, 151, 205, 84, 225, 16, 241, 42, 27, 7, 15, + 143, 128, 189, 233, 206, 106, 21, 7, 96, 167, 149, 2, + ]), + Bytes32([ + 155, 19, 224, 94, 41, 7, 172, 244, 76, 43, 181, 160, 220, 80, 64, 0, 140, 33, 238, 79, 87, + 43, 40, 34, 162, 57, 122, 232, 216, 48, 64, 114, + ]), + Bytes32([ + 64, 199, 52, 187, 21, 58, 215, 47, 174, 163, 203, 89, 35, 199, 187, 86, 137, 145, 236, 145, + 66, 21, 218, 151, 88, 61, 109, 238, 61, 58, 215, 247, + ]), + Bytes32([ + 197, 59, 74, 219, 191, 151, 46, 252, 54, 248, 228, 94, 22, 143, 69, 197, 114, 247, 37, 212, + 243, 158, 179, 192, 205, 50, 50, 36, 40, 130, 115, 247, + ]), + Bytes32([ + 168, 115, 30, 246, 210, 241, 88, 129, 115, 193, 107, 199, 199, 35, 118, 28, 145, 183, 82, + 101, 162, 52, 67, 147, 208, 234, 115, 58, 40, 124, 16, 79, + ]), + Bytes32([ + 253, 6, 238, 29, 37, 160, 45, 23, 26, 114, 137, 254, 44, 68, 147, 33, 212, 149, 16, 188, + 28, 174, 189, 22, 12, 228, 99, 101, 60, 248, 174, 34, + ]), + Bytes32([ + 0, 89, 208, 120, 115, 63, 79, 9, 29, 16, 81, 231, 78, 251, 117, 98, 172, 207, 54, 97, 38, + 72, 37, 110, 102, 207, 38, 198, 165, 157, 208, 6, + ]), + Bytes32([ + 193, 144, 83, 148, 50, 15, 138, 224, 145, 12, 222, 94, 88, 91, 151, 74, 180, 47, 102, 38, + 244, 38, 251, 13, 230, 130, 141, 133, 10, 194, 108, 211, + ]), + Bytes32([ + 182, 202, 171, 144, 153, 127, 43, 239, 77, 89, 56, 144, 39, 180, 30, 164, 1, 120, 105, 55, + 21, 17, 147, 96, 185, 219, 67, 146, 86, 42, 44, 117, + ]), + Bytes32([ + 240, 76, 90, 37, 238, 134, 53, 105, 125, 238, 19, 80, 162, 60, 63, 104, 193, 201, 233, 10, + 246, 175, 15, 167, 239, 50, 201, 138, 153, 127, 40, 247, + ]), + Bytes32([ + 127, 96, 10, 125, 139, 9, 213, 13, 60, 75, 10, 85, 88, 169, 180, 24, 18, 171, 157, 161, + 252, 181, 178, 191, 227, 200, 140, 72, 205, 231, 67, 34, + ]), + Bytes32([ + 8, 81, 79, 192, 81, 171, 28, 73, 80, 93, 207, 58, 0, 192, 109, 40, 59, 227, 148, 172, 144, + 146, 237, 103, 47, 77, 112, 155, 215, 125, 49, 205, + ]), + Bytes32([ + 181, 244, 223, 157, 69, 161, 149, 150, 31, 65, 52, 113, 11, 233, 51, 145, 135, 161, 34, + 114, 133, 86, 133, 255, 161, 176, 170, 255, 21, 163, 146, 79, + ]), + Bytes32([ + 251, 112, 107, 186, 121, 118, 206, 118, 164, 136, 208, 159, 220, 188, 218, 111, 190, 44, + 16, 16, 251, 116, 206, 236, 111, 184, 20, 247, 252, 129, 200, 164, + ]), + Bytes32([ + 101, 111, 124, 218, 143, 90, 110, 65, 52, 118, 81, 72, 234, 164, 106, 84, 54, 136, 212, + 192, 46, 87, 56, 241, 227, 38, 37, 101, 250, 134, 254, 144, + ]), + Bytes32([ + 209, 232, 185, 167, 159, 116, 4, 11, 114, 98, 71, 225, 233, 246, 135, 173, 175, 126, 125, + 101, 16, 27, 254, 198, 79, 21, 181, 120, 50, 139, 76, 4, + ]), + Bytes32([ + 30, 122, 82, 142, 43, 26, 170, 27, 0, 177, 218, 106, 39, 13, 154, 151, 92, 47, 144, 113, + 173, 242, 169, 89, 230, 135, 188, 190, 57, 170, 66, 220, + ]), + Bytes32([ + 39, 137, 200, 154, 57, 148, 82, 232, 82, 214, 232, 251, 187, 13, 253, 222, 184, 161, 144, + 231, 67, 116, 160, 231, 214, 47, 228, 88, 146, 14, 244, 108, + ]), + Bytes32([ + 122, 9, 214, 205, 84, 161, 177, 137, 68, 9, 178, 177, 141, 219, 162, 109, 116, 124, 124, + 56, 189, 139, 28, 220, 98, 165, 217, 156, 157, 148, 43, 232, + ]), + Bytes32([ + 83, 209, 1, 98, 176, 225, 170, 233, 144, 110, 148, 135, 159, 243, 202, 14, 201, 243, 218, + 255, 75, 90, 222, 25, 141, 148, 128, 132, 101, 8, 178, 180, + ]), + Bytes32([ + 93, 131, 135, 74, 118, 43, 37, 103, 218, 116, 47, 5, 212, 230, 133, 11, 76, 199, 46, 91, + 17, 222, 122, 114, 166, 228, 113, 213, 31, 5, 32, 164, + ]), + Bytes32([ + 230, 49, 245, 147, 146, 113, 214, 229, 67, 34, 234, 170, 14, 37, 33, 105, 12, 192, 127, + 177, 195, 70, 29, 44, 232, 191, 3, 56, 70, 40, 70, 69, + ]), + Bytes32([ + 64, 48, 103, 64, 135, 164, 51, 233, 201, 62, 5, 28, 35, 183, 178, 104, 54, 238, 0, 58, 215, + 157, 29, 149, 57, 23, 224, 247, 85, 104, 240, 113, + ]), + Bytes32([ + 12, 156, 88, 246, 58, 5, 82, 65, 113, 178, 127, 112, 138, 170, 91, 11, 79, 54, 122, 65, 87, + 104, 104, 93, 134, 176, 56, 5, 139, 142, 73, 54, + ]), + Bytes32([ + 92, 41, 81, 165, 165, 210, 34, 91, 127, 46, 250, 148, 104, 192, 211, 77, 108, 49, 111, 57, + 161, 242, 142, 46, 100, 47, 24, 24, 51, 219, 92, 186, + ]), + Bytes32([ + 201, 120, 116, 179, 124, 183, 117, 97, 200, 35, 138, 235, 77, 177, 138, 42, 47, 58, 69, + 188, 70, 2, 71, 95, 51, 233, 69, 14, 22, 24, 195, 153, + ]), + Bytes32([ + 249, 189, 13, 189, 45, 69, 100, 141, 50, 145, 24, 54, 91, 45, 125, 223, 181, 222, 63, 49, + 66, 49, 106, 175, 55, 85, 112, 25, 69, 187, 44, 171, + ]), + Bytes32([ + 86, 114, 135, 219, 78, 73, 34, 205, 239, 144, 56, 231, 164, 101, 129, 115, 51, 167, 158, + 96, 1, 241, 178, 216, 54, 84, 97, 185, 200, 239, 215, 7, + ]), + Bytes32([ + 35, 232, 131, 249, 70, 6, 185, 10, 58, 53, 72, 254, 74, 166, 76, 41, 122, 234, 77, 61, 16, + 39, 26, 66, 102, 78, 102, 159, 55, 113, 195, 19, + ]), + Bytes32([ + 204, 120, 229, 250, 86, 164, 18, 58, 82, 11, 112, 225, 76, 253, 63, 35, 245, 72, 81, 254, + 193, 182, 255, 159, 200, 120, 135, 60, 253, 180, 185, 112, + ]), + Bytes32([ + 35, 194, 73, 72, 85, 35, 3, 215, 21, 38, 67, 254, 195, 250, 173, 177, 156, 70, 47, 13, 178, + 120, 227, 214, 92, 71, 181, 40, 213, 43, 193, 147, + ]), + Bytes32([ + 148, 91, 40, 131, 45, 85, 232, 203, 101, 47, 189, 225, 128, 118, 138, 12, 83, 98, 227, 166, + 192, 69, 188, 19, 84, 13, 162, 250, 65, 209, 47, 191, + ]), + Bytes32([ + 100, 36, 35, 232, 157, 164, 39, 175, 77, 211, 19, 26, 20, 226, 226, 255, 121, 134, 19, 46, + 185, 213, 2, 4, 6, 92, 47, 239, 206, 240, 225, 58, + ]), + Bytes32([ + 121, 36, 6, 138, 185, 33, 130, 28, 55, 238, 127, 203, 99, 32, 56, 245, 237, 135, 207, 190, + 72, 243, 137, 32, 218, 103, 150, 171, 3, 246, 48, 225, + ]), + Bytes32([ + 233, 56, 238, 34, 238, 134, 69, 104, 220, 255, 47, 202, 241, 144, 207, 249, 107, 153, 38, + 120, 101, 12, 177, 64, 151, 114, 146, 6, 227, 159, 40, 107, + ]), + Bytes32([ + 237, 115, 194, 93, 160, 113, 23, 120, 250, 186, 21, 163, 155, 20, 179, 177, 55, 20, 24, 89, + 77, 113, 163, 0, 176, 130, 228, 133, 239, 122, 177, 65, + ]), + Bytes32([ + 108, 150, 128, 44, 189, 57, 148, 254, 56, 134, 118, 165, 95, 15, 168, 179, 162, 252, 169, + 58, 223, 20, 38, 221, 25, 138, 101, 131, 32, 8, 2, 58, + ]), + Bytes32([ + 154, 13, 160, 129, 192, 79, 86, 40, 135, 113, 123, 38, 53, 254, 62, 94, 255, 14, 119, 4, + 102, 168, 200, 191, 112, 245, 29, 200, 164, 155, 141, 139, + ]), + Bytes32([ + 238, 189, 209, 174, 202, 251, 240, 99, 133, 44, 221, 34, 17, 88, 220, 143, 188, 160, 235, + 121, 167, 96, 106, 249, 213, 176, 31, 84, 26, 144, 201, 89, + ]), + Bytes32([ + 178, 186, 146, 221, 228, 41, 150, 76, 220, 216, 144, 64, 108, 178, 242, 120, 153, 191, 212, + 37, 114, 21, 30, 117, 45, 238, 46, 19, 132, 212, 112, 44, + ]), + Bytes32([ + 191, 19, 180, 171, 125, 68, 104, 43, 66, 173, 194, 168, 213, 192, 117, 82, 167, 41, 139, + 133, 92, 103, 124, 85, 241, 66, 1, 154, 68, 72, 171, 80, + ]), + Bytes32([ + 79, 215, 118, 157, 123, 16, 70, 30, 101, 234, 117, 41, 35, 186, 99, 5, 232, 45, 253, 142, + 185, 18, 172, 187, 36, 50, 218, 252, 111, 184, 158, 254, + ]), + Bytes32([ + 17, 193, 206, 44, 67, 98, 171, 63, 78, 209, 71, 20, 196, 255, 64, 114, 191, 19, 246, 131, + 8, 105, 211, 169, 192, 114, 249, 240, 28, 55, 230, 143, + ]), + Bytes32([ + 200, 94, 187, 80, 140, 194, 238, 35, 62, 228, 202, 122, 136, 119, 228, 133, 33, 147, 161, + 218, 94, 19, 207, 129, 48, 252, 242, 19, 56, 251, 92, 76, + ]), + Bytes32([ + 223, 124, 52, 170, 180, 101, 70, 1, 153, 22, 178, 138, 181, 253, 35, 113, 159, 136, 77, 23, + 158, 62, 11, 48, 6, 185, 147, 81, 220, 191, 94, 3, + ]), + Bytes32([ + 126, 124, 205, 157, 16, 239, 172, 241, 70, 70, 205, 98, 211, 8, 84, 204, 149, 9, 208, 56, + 122, 51, 224, 231, 200, 59, 41, 215, 141, 183, 91, 223, + ]), + Bytes32([ + 20, 122, 156, 134, 248, 95, 140, 199, 59, 151, 120, 164, 109, 202, 165, 126, 108, 161, 8, + 253, 18, 35, 36, 83, 219, 162, 58, 83, 136, 57, 43, 35, + ]), + Bytes32([ + 58, 175, 246, 31, 114, 109, 109, 219, 230, 129, 128, 99, 175, 228, 65, 148, 53, 233, 226, + 171, 44, 254, 222, 211, 200, 28, 193, 130, 251, 152, 160, 210, + ]), + Bytes32([ + 177, 255, 19, 82, 14, 229, 84, 206, 156, 198, 252, 176, 126, 211, 226, 255, 154, 231, 131, + 158, 165, 154, 142, 166, 131, 221, 203, 30, 251, 205, 27, 217, + ]), + Bytes32([ + 32, 168, 192, 93, 240, 173, 218, 42, 78, 215, 104, 243, 154, 2, 50, 159, 21, 176, 28, 59, + 29, 57, 193, 122, 32, 244, 91, 148, 91, 184, 126, 31, + ]), + Bytes32([ + 28, 66, 164, 117, 43, 120, 84, 240, 48, 3, 55, 95, 44, 229, 231, 29, 217, 67, 49, 130, 110, + 209, 72, 71, 46, 208, 63, 81, 218, 98, 167, 207, + ]), + Bytes32([ + 38, 192, 60, 175, 136, 217, 168, 69, 90, 236, 62, 137, 9, 116, 244, 151, 181, 57, 229, 198, + 100, 125, 114, 46, 80, 136, 150, 94, 225, 162, 170, 91, + ]), + Bytes32([ + 121, 67, 11, 63, 56, 140, 67, 231, 175, 126, 41, 37, 252, 62, 35, 215, 250, 206, 190, 3, + 154, 83, 237, 174, 65, 86, 83, 251, 110, 8, 44, 216, + ]), + Bytes32([ + 25, 251, 117, 251, 14, 64, 166, 211, 216, 123, 38, 1, 98, 19, 119, 194, 60, 234, 185, 148, + 118, 201, 4, 233, 69, 135, 13, 191, 8, 6, 136, 240, + ]), + Bytes32([ + 137, 1, 201, 130, 166, 152, 216, 48, 178, 242, 46, 3, 66, 92, 4, 81, 11, 145, 205, 82, 253, + 102, 78, 6, 53, 13, 36, 171, 48, 75, 165, 153, + ]), + Bytes32([ + 126, 38, 201, 183, 226, 145, 26, 196, 124, 127, 185, 12, 77, 198, 146, 16, 202, 189, 113, + 44, 100, 87, 81, 53, 172, 227, 97, 191, 120, 241, 86, 110, + ]), + Bytes32([ + 45, 8, 41, 170, 235, 127, 133, 14, 253, 53, 201, 220, 96, 192, 168, 234, 110, 20, 174, 226, + 18, 4, 230, 182, 204, 99, 138, 27, 82, 209, 28, 68, + ]), +]; + +const TABLE_HASHES: &[Bytes32; 64] = &[ + Bytes32([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + ]), + Bytes32([ + 164, 47, 121, 69, 150, 44, 30, 204, 210, 181, 242, 154, 59, 111, 5, 10, 47, 121, 213, 80, + 163, 196, 205, 188, 241, 64, 202, 26, 242, 47, 50, 205, + ]), + Bytes32([ + 166, 9, 224, 145, 14, 138, 194, 78, 202, 114, 110, 38, 244, 33, 189, 185, 170, 85, 144, + 229, 123, 63, 229, 194, 59, 130, 156, 6, 184, 154, 187, 17, + ]), + Bytes32([ + 32, 51, 186, 232, 39, 244, 218, 98, 243, 254, 82, 247, 63, 120, 34, 123, 128, 240, 220, + 145, 41, 114, 251, 208, 209, 244, 10, 9, 179, 5, 105, 181, + ]), + Bytes32([ + 73, 148, 74, 132, 241, 45, 129, 169, 83, 223, 101, 12, 184, 60, 10, 201, 169, 197, 75, 17, + 198, 168, 79, 171, 207, 195, 123, 47, 249, 30, 176, 216, + ]), + Bytes32([ + 145, 224, 51, 196, 211, 137, 189, 133, 232, 210, 170, 157, 227, 160, 218, 143, 15, 82, 170, + 226, 241, 83, 7, 112, 138, 227, 58, 59, 149, 168, 81, 94, + ]), + Bytes32([ + 241, 90, 42, 7, 231, 63, 36, 86, 238, 131, 19, 20, 38, 118, 65, 180, 109, 121, 147, 147, + 213, 142, 203, 244, 239, 75, 5, 160, 16, 85, 221, 95, + ]), + Bytes32([ + 213, 113, 178, 221, 213, 107, 77, 153, 222, 48, 148, 140, 250, 1, 17, 59, 73, 19, 110, 106, + 94, 195, 126, 49, 126, 109, 65, 55, 104, 111, 18, 171, + ]), + Bytes32([ + 43, 198, 12, 197, 95, 201, 76, 108, 96, 235, 76, 8, 117, 99, 42, 20, 177, 45, 112, 40, 2, + 180, 68, 19, 25, 11, 210, 168, 170, 178, 99, 27, + ]), + Bytes32([ + 142, 205, 24, 125, 81, 234, 138, 161, 206, 121, 163, 225, 50, 9, 180, 102, 0, 25, 93, 180, + 137, 164, 206, 249, 222, 20, 175, 177, 199, 14, 115, 207, + ]), + Bytes32([ + 213, 41, 249, 19, 173, 165, 236, 103, 42, 146, 223, 6, 190, 229, 161, 13, 96, 163, 110, 23, + 203, 238, 24, 95, 130, 203, 35, 211, 63, 234, 32, 137, + ]), + Bytes32([ + 176, 82, 229, 48, 113, 46, 179, 38, 231, 83, 50, 136, 179, 190, 242, 126, 152, 210, 141, + 102, 191, 21, 20, 96, 235, 245, 81, 254, 231, 117, 159, 146, + ]), + Bytes32([ + 216, 15, 120, 147, 70, 215, 12, 160, 223, 73, 246, 73, 195, 189, 200, 3, 19, 131, 102, 61, + 173, 79, 50, 76, 39, 150, 151, 147, 35, 251, 200, 0, + ]), + Bytes32([ + 88, 215, 168, 30, 218, 72, 206, 159, 91, 121, 14, 133, 241, 220, 195, 46, 93, 244, 179, 53, + 158, 75, 211, 161, 8, 120, 167, 141, 235, 16, 232, 42, + ]), + Bytes32([ + 114, 99, 218, 52, 203, 162, 199, 16, 225, 154, 182, 20, 59, 121, 250, 249, 166, 35, 118, + 18, 142, 57, 122, 131, 32, 219, 83, 126, 147, 91, 116, 16, + ]), + Bytes32([ + 193, 157, 15, 251, 179, 155, 215, 190, 98, 93, 30, 167, 23, 175, 178, 172, 142, 143, 222, + 20, 18, 157, 215, 42, 105, 107, 64, 16, 216, 143, 109, 45, + ]), + Bytes32([ + 178, 2, 203, 32, 83, 90, 114, 97, 124, 210, 81, 81, 179, 213, 71, 19, 181, 173, 227, 88, + 226, 83, 48, 169, 134, 163, 62, 103, 222, 82, 128, 30, + ]), + Bytes32([ + 252, 17, 142, 50, 185, 19, 111, 148, 204, 30, 199, 138, 143, 207, 30, 212, 254, 7, 129, 43, + 100, 167, 52, 115, 247, 143, 64, 106, 220, 5, 7, 89, + ]), + Bytes32([ + 117, 136, 111, 98, 95, 97, 253, 43, 82, 22, 43, 187, 65, 154, 132, 105, 55, 194, 240, 148, + 170, 199, 185, 91, 157, 138, 43, 117, 218, 176, 110, 101, + ]), + Bytes32([ + 228, 56, 186, 202, 73, 47, 253, 151, 48, 173, 2, 101, 153, 197, 108, 145, 227, 170, 247, + 86, 80, 70, 30, 93, 232, 148, 63, 211, 197, 135, 23, 97, + ]), + Bytes32([ + 162, 35, 93, 45, 208, 241, 87, 57, 138, 79, 231, 83, 75, 81, 102, 91, 115, 58, 1, 205, 193, + 197, 62, 210, 144, 88, 59, 52, 149, 234, 121, 224, + ]), + Bytes32([ + 165, 67, 16, 239, 234, 92, 207, 81, 64, 93, 227, 154, 132, 33, 54, 53, 102, 198, 90, 134, + 24, 29, 190, 158, 121, 23, 127, 30, 27, 239, 140, 88, + ]), + Bytes32([ + 61, 125, 183, 139, 186, 82, 193, 166, 2, 147, 231, 215, 179, 107, 163, 67, 122, 0, 90, 188, + 142, 216, 72, 20, 63, 146, 85, 89, 231, 41, 153, 117, + ]), + Bytes32([ + 206, 31, 38, 182, 78, 170, 183, 207, 211, 114, 212, 4, 108, 84, 62, 99, 245, 196, 10, 1, + 188, 63, 240, 199, 27, 85, 68, 101, 212, 222, 85, 12, + ]), + Bytes32([ + 106, 0, 225, 130, 195, 87, 113, 106, 101, 80, 56, 119, 93, 45, 74, 168, 240, 209, 155, 123, + 8, 129, 73, 225, 204, 125, 42, 73, 102, 122, 228, 158, + ]), + Bytes32([ + 251, 63, 88, 92, 59, 176, 71, 194, 46, 19, 18, 27, 83, 57, 197, 231, 202, 242, 155, 63, + 252, 240, 205, 216, 96, 130, 161, 197, 123, 110, 175, 220, + ]), + Bytes32([ + 239, 6, 249, 150, 189, 234, 197, 18, 79, 222, 80, 72, 205, 147, 251, 135, 226, 33, 46, 111, + 186, 185, 4, 33, 219, 129, 30, 182, 57, 229, 34, 16, + ]), + Bytes32([ + 192, 35, 149, 149, 16, 111, 3, 119, 173, 215, 223, 247, 70, 25, 31, 107, 104, 5, 249, 243, + 193, 66, 99, 48, 107, 158, 25, 147, 163, 151, 96, 149, + ]), + Bytes32([ + 197, 133, 241, 197, 67, 52, 255, 227, 180, 235, 137, 164, 174, 160, 183, 9, 159, 254, 210, + 180, 231, 140, 176, 165, 113, 215, 182, 1, 177, 204, 219, 246, + ]), + Bytes32([ + 51, 32, 109, 173, 131, 6, 99, 63, 75, 191, 207, 187, 73, 26, 5, 139, 167, 216, 123, 26, + 199, 176, 84, 215, 158, 243, 105, 0, 91, 254, 59, 146, + ]), + Bytes32([ + 229, 120, 15, 22, 225, 50, 131, 240, 190, 232, 83, 119, 169, 246, 172, 35, 99, 163, 58, + 146, 234, 253, 252, 83, 5, 48, 54, 142, 172, 91, 145, 13, + ]), + Bytes32([ + 159, 9, 100, 20, 215, 92, 217, 43, 208, 254, 65, 234, 124, 167, 250, 53, 159, 151, 124, 70, + 26, 0, 113, 28, 78, 44, 238, 8, 34, 255, 19, 36, + ]), + Bytes32([ + 193, 135, 44, 144, 13, 46, 76, 43, 127, 208, 79, 196, 229, 58, 125, 33, 71, 118, 215, 240, + 80, 123, 44, 181, 174, 47, 102, 42, 137, 98, 190, 41, + ]), + Bytes32([ + 78, 19, 3, 217, 174, 154, 142, 109, 189, 14, 241, 12, 23, 185, 224, 152, 209, 108, 223, + 202, 191, 11, 190, 149, 31, 236, 104, 234, 22, 38, 196, 3, + ]), + Bytes32([ + 60, 93, 237, 228, 186, 222, 179, 231, 151, 95, 147, 20, 188, 10, 191, 34, 126, 57, 253, + 161, 154, 117, 182, 108, 143, 72, 48, 102, 191, 240, 81, 208, + ]), + Bytes32([ + 179, 114, 163, 89, 172, 211, 154, 204, 11, 125, 140, 186, 107, 90, 17, 116, 214, 31, 165, + 94, 36, 187, 184, 151, 210, 208, 76, 215, 75, 166, 107, 91, + ]), + Bytes32([ + 125, 131, 52, 139, 120, 156, 163, 243, 206, 143, 204, 221, 123, 140, 1, 103, 235, 186, 64, + 82, 194, 191, 205, 2, 32, 242, 38, 77, 184, 23, 236, 228, + ]), + Bytes32([ + 137, 224, 90, 1, 116, 95, 215, 47, 8, 3, 227, 132, 154, 232, 216, 63, 3, 80, 52, 18, 42, + 29, 85, 38, 117, 255, 9, 99, 88, 233, 129, 92, + ]), + Bytes32([ + 54, 182, 145, 174, 54, 24, 168, 13, 71, 43, 178, 49, 153, 246, 94, 79, 228, 254, 134, 0, + 21, 210, 176, 242, 82, 117, 76, 40, 221, 181, 30, 62, + ]), + Bytes32([ + 145, 171, 156, 87, 15, 174, 31, 111, 175, 161, 31, 116, 123, 123, 37, 216, 43, 147, 197, + 79, 218, 49, 236, 111, 35, 49, 44, 188, 197, 79, 136, 45, + ]), + Bytes32([ + 169, 48, 186, 93, 10, 48, 80, 103, 68, 226, 103, 31, 176, 154, 146, 199, 166, 89, 48, 149, + 116, 101, 157, 149, 149, 72, 123, 54, 153, 26, 210, 122, + ]), + Bytes32([ + 225, 45, 163, 167, 213, 84, 40, 87, 106, 253, 206, 80, 189, 71, 153, 5, 15, 107, 151, 150, + 14, 22, 143, 207, 4, 192, 63, 250, 207, 207, 179, 26, + ]), + Bytes32([ + 110, 249, 172, 218, 206, 202, 107, 64, 48, 153, 226, 127, 165, 227, 50, 146, 140, 239, 220, + 252, 188, 157, 77, 210, 92, 73, 38, 6, 16, 87, 244, 66, + ]), + Bytes32([ + 39, 217, 230, 91, 60, 231, 96, 137, 37, 58, 93, 51, 79, 222, 132, 171, 196, 109, 155, 4, + 252, 213, 17, 33, 160, 24, 250, 118, 187, 62, 107, 113, + ]), + Bytes32([ + 120, 167, 145, 121, 184, 31, 185, 178, 241, 54, 20, 48, 132, 89, 132, 127, 235, 189, 197, + 127, 249, 58, 255, 197, 40, 229, 130, 254, 213, 186, 114, 74, + ]), + Bytes32([ + 7, 40, 187, 172, 225, 196, 3, 77, 113, 16, 118, 148, 95, 65, 42, 234, 233, 29, 120, 223, + 105, 77, 250, 119, 245, 20, 21, 155, 177, 13, 56, 151, + ]), + Bytes32([ + 22, 248, 25, 75, 36, 179, 251, 57, 101, 170, 213, 19, 216, 64, 164, 253, 151, 199, 88, 5, + 92, 129, 138, 185, 9, 236, 231, 68, 112, 24, 218, 216, + ]), + Bytes32([ + 95, 141, 49, 39, 213, 214, 130, 49, 143, 124, 178, 114, 200, 136, 160, 90, 233, 73, 233, + 185, 60, 250, 174, 74, 2, 123, 2, 62, 192, 174, 115, 158, + ]), + Bytes32([ + 142, 29, 212, 35, 71, 107, 91, 164, 226, 225, 180, 218, 99, 34, 200, 171, 33, 235, 113, + 105, 191, 78, 105, 70, 221, 135, 167, 148, 24, 225, 6, 146, + ]), + Bytes32([ + 176, 194, 115, 243, 138, 104, 3, 247, 184, 74, 81, 60, 173, 45, 29, 189, 252, 202, 206, 66, + 129, 65, 150, 25, 246, 145, 133, 4, 93, 201, 201, 240, + ]), + Bytes32([ + 18, 233, 186, 158, 43, 12, 206, 228, 80, 102, 187, 25, 78, 77, 180, 28, 82, 62, 168, 252, + 14, 113, 126, 91, 243, 120, 213, 91, 189, 244, 247, 177, + ]), + Bytes32([ + 79, 126, 97, 23, 112, 98, 93, 86, 24, 103, 82, 33, 155, 188, 202, 61, 29, 146, 132, 169, + 116, 207, 216, 35, 29, 229, 213, 120, 135, 240, 129, 205, + ]), + Bytes32([ + 146, 121, 255, 202, 145, 53, 46, 8, 102, 205, 56, 234, 49, 42, 85, 231, 217, 114, 235, 141, + 233, 187, 29, 9, 122, 232, 61, 253, 42, 104, 4, 113, + ]), + Bytes32([ + 103, 174, 255, 6, 115, 155, 156, 65, 84, 251, 208, 55, 89, 124, 171, 167, 133, 227, 22, + 134, 32, 71, 243, 181, 16, 100, 81, 63, 22, 207, 58, 190, + ]), + Bytes32([ + 69, 10, 247, 243, 56, 0, 64, 72, 218, 21, 211, 192, 62, 243, 66, 244, 230, 58, 107, 54, + 180, 29, 216, 23, 86, 211, 173, 181, 118, 188, 238, 72, + ]), + Bytes32([ + 25, 179, 4, 93, 169, 76, 76, 69, 158, 3, 24, 146, 130, 9, 225, 198, 102, 238, 29, 73, 74, + 179, 159, 74, 40, 111, 181, 110, 76, 173, 251, 255, + ]), + Bytes32([ + 236, 113, 37, 202, 149, 150, 90, 50, 233, 141, 125, 12, 212, 163, 137, 176, 15, 214, 194, + 73, 138, 104, 4, 33, 235, 76, 185, 58, 125, 182, 56, 0, + ]), + Bytes32([ + 238, 54, 183, 94, 50, 33, 98, 31, 133, 181, 230, 98, 43, 154, 95, 111, 250, 71, 15, 197, + 156, 60, 8, 90, 208, 50, 62, 127, 30, 18, 181, 176, + ]), + Bytes32([ + 246, 143, 72, 228, 143, 15, 15, 131, 178, 105, 113, 46, 85, 36, 165, 116, 63, 131, 61, 228, + 98, 144, 232, 209, 228, 110, 168, 171, 252, 47, 79, 165, + ]), + Bytes32([ + 146, 70, 74, 79, 139, 107, 73, 28, 231, 88, 235, 214, 163, 185, 87, 195, 147, 186, 153, + 222, 191, 214, 30, 231, 173, 166, 151, 169, 244, 15, 15, 227, + ]), + Bytes32([ + 137, 182, 175, 97, 212, 23, 62, 70, 50, 150, 67, 153, 253, 52, 179, 105, 147, 177, 217, + 170, 125, 251, 1, 96, 4, 127, 196, 37, 254, 7, 217, 100, + ]), + Bytes32([ + 32, 253, 229, 209, 120, 43, 120, 189, 245, 227, 178, 79, 134, 175, 240, 107, 25, 124, 5, + 67, 120, 213, 187, 200, 159, 9, 26, 119, 13, 1, 8, 120, + ]), + Bytes32([ + 115, 241, 26, 44, 83, 229, 130, 21, 249, 87, 72, 212, 75, 220, 95, 22, 151, 77, 182, 8, + 196, 207, 95, 18, 9, 92, 89, 206, 114, 80, 36, 159, + ]), + Bytes32([ + 174, 151, 88, 171, 52, 40, 161, 16, 106, 45, 44, 9, 142, 95, 5, 205, 82, 24, 172, 103, 152, + 202, 187, 34, 217, 173, 236, 129, 32, 203, 205, 38, + ]), +]; + +const TABLE_ELEMENT_HASHES: &[Bytes32; 64] = &[ + Bytes32([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + ]), + Bytes32([ + 145, 218, 63, 74, 131, 182, 204, 163, 74, 206, 234, 50, 47, 3, 51, 74, 121, 225, 146, 237, + 5, 128, 20, 230, 243, 89, 33, 248, 136, 154, 189, 5, + ]), + Bytes32([ + 36, 240, 95, 13, 214, 136, 92, 102, 96, 137, 39, 96, 108, 223, 147, 130, 220, 61, 147, 226, + 52, 133, 67, 223, 211, 164, 6, 53, 45, 117, 120, 241, + ]), + Bytes32([ + 9, 2, 218, 34, 228, 8, 178, 127, 160, 30, 104, 198, 49, 37, 71, 115, 14, 35, 20, 15, 253, + 160, 147, 143, 24, 186, 12, 90, 41, 169, 30, 8, + ]), + Bytes32([ + 137, 242, 105, 78, 161, 99, 76, 224, 226, 100, 216, 235, 156, 132, 214, 154, 107, 120, 197, + 113, 45, 221, 170, 119, 92, 228, 76, 139, 170, 159, 111, 58, + ]), + Bytes32([ + 231, 165, 221, 185, 139, 22, 70, 40, 67, 162, 123, 94, 161, 156, 57, 53, 102, 217, 128, + 227, 18, 132, 17, 170, 90, 31, 214, 181, 249, 220, 70, 37, + ]), + Bytes32([ + 171, 253, 174, 187, 9, 186, 87, 37, 22, 83, 13, 88, 75, 11, 177, 226, 237, 136, 132, 137, + 101, 109, 135, 192, 224, 80, 87, 105, 95, 81, 255, 21, + ]), + Bytes32([ + 179, 50, 36, 230, 159, 162, 184, 132, 140, 121, 133, 155, 189, 44, 132, 93, 80, 79, 32, + 244, 131, 57, 79, 222, 104, 63, 224, 182, 91, 162, 73, 122, + ]), + Bytes32([ + 17, 55, 206, 87, 127, 185, 135, 231, 41, 56, 166, 104, 229, 102, 222, 181, 236, 233, 230, + 39, 43, 163, 16, 1, 225, 86, 211, 51, 145, 7, 169, 84, + ]), + Bytes32([ + 201, 174, 3, 154, 104, 190, 240, 99, 123, 201, 7, 118, 196, 85, 28, 247, 35, 232, 39, 25, + 151, 52, 167, 136, 179, 13, 213, 60, 138, 158, 39, 131, + ]), + Bytes32([ + 35, 33, 109, 155, 171, 252, 195, 80, 10, 115, 40, 199, 250, 1, 241, 82, 146, 225, 118, 237, + 11, 125, 198, 97, 57, 21, 171, 237, 94, 60, 49, 32, + ]), + Bytes32([ + 126, 95, 42, 157, 183, 168, 137, 120, 114, 64, 148, 170, 32, 156, 137, 96, 194, 91, 119, + 11, 20, 3, 30, 62, 118, 12, 31, 48, 245, 144, 252, 29, + ]), + Bytes32([ + 48, 135, 201, 114, 103, 35, 8, 96, 5, 101, 125, 26, 87, 0, 98, 122, 239, 140, 230, 10, 102, + 112, 155, 87, 249, 181, 152, 235, 28, 79, 176, 56, + ]), + Bytes32([ + 76, 70, 222, 210, 242, 159, 52, 123, 112, 106, 253, 140, 55, 160, 189, 255, 220, 64, 8, + 168, 39, 196, 160, 8, 105, 154, 91, 184, 62, 63, 227, 156, + ]), + Bytes32([ + 138, 201, 8, 240, 218, 190, 210, 227, 236, 168, 45, 161, 209, 115, 126, 165, 235, 162, 6, + 117, 76, 239, 3, 225, 163, 251, 111, 233, 233, 228, 14, 92, + ]), + Bytes32([ + 190, 166, 27, 117, 242, 0, 25, 148, 230, 125, 237, 22, 153, 158, 151, 56, 121, 194, 221, + 198, 184, 147, 249, 149, 27, 29, 166, 243, 254, 181, 209, 102, + ]), + Bytes32([ + 235, 63, 149, 47, 164, 152, 80, 11, 178, 203, 99, 178, 176, 226, 164, 151, 39, 65, 83, 200, + 102, 90, 12, 102, 178, 15, 232, 16, 222, 141, 78, 3, + ]), + Bytes32([ + 37, 179, 99, 114, 214, 120, 87, 53, 198, 229, 207, 47, 16, 112, 208, 33, 216, 152, 209, + 228, 196, 14, 237, 250, 212, 90, 176, 185, 21, 248, 92, 109, + ]), + Bytes32([ + 237, 200, 122, 93, 145, 116, 138, 59, 86, 220, 58, 171, 160, 31, 142, 193, 231, 131, 106, + 164, 166, 44, 120, 170, 62, 81, 73, 85, 0, 52, 219, 220, + ]), + Bytes32([ + 244, 173, 85, 141, 81, 198, 214, 0, 54, 21, 20, 169, 177, 235, 104, 206, 43, 218, 96, 68, + 73, 37, 252, 77, 253, 18, 30, 97, 223, 122, 1, 12, + ]), + Bytes32([ + 104, 169, 247, 140, 131, 245, 193, 3, 36, 119, 249, 119, 108, 102, 115, 250, 173, 225, 33, + 156, 116, 87, 180, 27, 84, 215, 126, 15, 187, 189, 32, 139, + ]), + Bytes32([ + 47, 156, 244, 88, 145, 227, 224, 87, 233, 138, 138, 9, 169, 96, 29, 204, 100, 170, 23, 105, + 63, 4, 119, 90, 167, 161, 72, 109, 82, 254, 123, 48, + ]), + Bytes32([ + 196, 68, 107, 111, 141, 253, 138, 140, 162, 104, 247, 104, 87, 144, 123, 240, 105, 251, + 246, 210, 172, 172, 241, 162, 155, 90, 83, 175, 131, 251, 107, 215, + ]), + Bytes32([ + 28, 100, 243, 226, 32, 234, 211, 106, 197, 179, 14, 192, 70, 11, 203, 125, 151, 53, 20, 77, + 175, 99, 154, 100, 159, 249, 54, 39, 73, 168, 162, 66, + ]), + Bytes32([ + 83, 176, 7, 180, 201, 178, 172, 98, 47, 34, 80, 6, 62, 10, 4, 112, 43, 93, 10, 227, 74, 91, + 11, 217, 83, 230, 104, 0, 167, 2, 114, 89, + ]), + Bytes32([ + 185, 162, 212, 82, 149, 182, 104, 215, 62, 244, 114, 124, 181, 74, 226, 119, 4, 42, 181, + 238, 25, 212, 215, 219, 174, 231, 32, 170, 9, 234, 12, 212, + ]), + Bytes32([ + 173, 38, 241, 20, 137, 119, 33, 252, 174, 245, 252, 234, 165, 181, 128, 225, 14, 147, 182, + 97, 145, 19, 40, 193, 90, 238, 140, 87, 0, 222, 70, 254, + ]), + Bytes32([ + 51, 218, 162, 213, 211, 23, 9, 158, 185, 156, 39, 145, 5, 146, 70, 144, 172, 10, 26, 212, + 253, 37, 24, 32, 21, 65, 179, 140, 64, 237, 96, 157, + ]), + Bytes32([ + 221, 87, 57, 81, 246, 126, 53, 163, 190, 220, 109, 205, 114, 234, 85, 151, 75, 245, 254, + 16, 140, 131, 239, 120, 9, 209, 54, 61, 177, 6, 188, 252, + ]), + Bytes32([ + 54, 161, 25, 198, 75, 151, 214, 76, 83, 74, 117, 229, 155, 237, 88, 95, 143, 119, 56, 12, + 201, 164, 235, 20, 117, 49, 177, 88, 14, 248, 95, 198, + ]), + Bytes32([ + 60, 16, 183, 115, 241, 45, 253, 24, 105, 43, 79, 72, 212, 238, 24, 156, 132, 93, 78, 246, + 1, 228, 222, 200, 254, 130, 170, 46, 91, 254, 205, 143, + ]), + Bytes32([ + 184, 30, 34, 235, 214, 40, 120, 64, 182, 15, 162, 189, 164, 202, 89, 146, 202, 177, 16, + 105, 172, 158, 1, 95, 101, 146, 253, 115, 226, 233, 38, 171, + ]), + Bytes32([ + 70, 152, 3, 73, 249, 222, 93, 214, 74, 179, 140, 47, 34, 42, 149, 214, 152, 75, 34, 145, + 68, 226, 151, 101, 121, 54, 252, 240, 206, 140, 81, 165, + ]), + Bytes32([ + 9, 88, 51, 124, 67, 108, 84, 244, 61, 241, 110, 246, 184, 105, 6, 116, 242, 58, 15, 144, + 43, 76, 176, 107, 0, 138, 155, 235, 210, 138, 226, 215, + ]), + Bytes32([ + 98, 170, 168, 147, 158, 223, 186, 7, 58, 130, 89, 200, 153, 24, 158, 251, 69, 100, 7, 57, + 96, 115, 136, 170, 57, 145, 143, 240, 102, 6, 248, 111, + ]), + Bytes32([ + 76, 75, 216, 216, 194, 146, 2, 59, 247, 64, 98, 160, 62, 105, 193, 186, 161, 191, 64, 154, + 254, 196, 55, 214, 201, 103, 223, 131, 181, 192, 152, 176, + ]), + Bytes32([ + 188, 141, 114, 199, 33, 45, 183, 18, 123, 231, 92, 215, 51, 191, 11, 199, 253, 46, 28, 10, + 156, 23, 92, 255, 219, 236, 176, 247, 52, 105, 210, 223, + ]), + Bytes32([ + 200, 67, 112, 29, 180, 70, 50, 18, 220, 122, 39, 122, 83, 186, 81, 77, 241, 44, 190, 3, + 162, 217, 76, 61, 230, 237, 157, 230, 205, 172, 186, 247, + ]), + Bytes32([ + 131, 131, 129, 83, 251, 217, 83, 88, 93, 39, 66, 174, 51, 19, 190, 91, 16, 187, 95, 63, 35, + 178, 94, 237, 194, 42, 231, 227, 138, 202, 216, 93, + ]), + Bytes32([ + 18, 177, 28, 41, 199, 59, 160, 244, 26, 162, 73, 48, 16, 74, 190, 195, 249, 216, 64, 229, + 145, 65, 18, 131, 75, 57, 71, 74, 127, 239, 112, 13, + ]), + Bytes32([ + 41, 120, 53, 253, 151, 12, 143, 213, 212, 31, 245, 61, 168, 113, 30, 38, 112, 126, 156, + 133, 241, 102, 110, 237, 4, 138, 50, 160, 79, 235, 52, 35, + ]), + Bytes32([ + 38, 74, 146, 27, 110, 86, 122, 250, 58, 243, 64, 185, 207, 174, 28, 124, 35, 183, 53, 88, + 34, 234, 89, 58, 98, 223, 69, 13, 48, 15, 239, 230, + ]), + Bytes32([ + 221, 132, 1, 28, 157, 232, 235, 132, 32, 58, 26, 36, 60, 217, 197, 165, 141, 19, 64, 120, + 197, 139, 190, 86, 153, 152, 235, 11, 99, 129, 222, 91, + ]), + Bytes32([ + 237, 76, 61, 239, 169, 108, 131, 62, 163, 67, 158, 210, 14, 45, 91, 105, 36, 182, 206, 182, + 71, 11, 159, 169, 159, 248, 247, 176, 6, 176, 237, 123, + ]), + Bytes32([ + 232, 190, 198, 78, 12, 148, 86, 176, 221, 115, 1, 22, 122, 163, 223, 234, 74, 129, 125, 53, + 211, 137, 115, 18, 241, 170, 69, 42, 34, 74, 180, 172, + ]), + Bytes32([ + 215, 199, 144, 158, 222, 153, 123, 125, 150, 97, 127, 151, 96, 43, 69, 155, 150, 119, 75, + 82, 218, 48, 101, 70, 121, 18, 42, 146, 215, 183, 100, 158, + ]), + Bytes32([ + 121, 182, 45, 62, 255, 78, 32, 48, 107, 186, 208, 155, 158, 80, 231, 241, 90, 227, 219, + 113, 52, 20, 66, 45, 124, 67, 209, 61, 167, 74, 60, 34, + ]), + Bytes32([ + 34, 136, 15, 103, 128, 220, 202, 103, 150, 123, 249, 145, 218, 227, 17, 64, 142, 129, 4, + 42, 93, 7, 6, 147, 111, 117, 25, 185, 88, 104, 85, 115, + ]), + Bytes32([ + 144, 191, 235, 38, 205, 102, 127, 160, 139, 105, 75, 63, 86, 192, 194, 49, 240, 35, 65, + 154, 4, 113, 51, 87, 40, 70, 32, 58, 243, 16, 136, 67, + ]), + Bytes32([ + 217, 150, 254, 250, 111, 69, 45, 227, 111, 59, 199, 149, 59, 216, 17, 117, 8, 226, 85, 250, + 183, 187, 21, 129, 208, 0, 63, 158, 232, 106, 131, 16, + ]), + Bytes32([ + 148, 212, 84, 16, 204, 49, 69, 96, 137, 125, 102, 221, 111, 8, 41, 51, 221, 241, 87, 231, + 88, 215, 211, 24, 254, 131, 111, 56, 160, 143, 230, 109, + ]), + Bytes32([ + 144, 48, 37, 157, 153, 7, 123, 206, 46, 224, 211, 249, 208, 217, 91, 243, 128, 62, 100, + 126, 3, 253, 223, 161, 50, 108, 26, 151, 129, 242, 94, 132, + ]), + Bytes32([ + 97, 167, 177, 53, 183, 154, 164, 1, 129, 184, 234, 182, 190, 197, 244, 226, 150, 217, 57, + 152, 243, 206, 60, 76, 114, 194, 51, 240, 58, 93, 219, 76, + ]), + Bytes32([ + 139, 70, 147, 227, 94, 147, 130, 53, 38, 112, 138, 176, 7, 148, 6, 21, 50, 199, 70, 33, 87, + 180, 33, 167, 56, 89, 66, 95, 245, 247, 135, 24, + ]), + Bytes32([ + 175, 169, 204, 13, 145, 18, 40, 29, 84, 88, 206, 99, 208, 137, 249, 146, 0, 237, 188, 130, + 135, 223, 121, 54, 126, 251, 247, 109, 141, 217, 62, 127, + ]), + Bytes32([ + 139, 188, 3, 234, 198, 124, 194, 238, 217, 237, 214, 250, 66, 220, 0, 51, 1, 203, 122, 125, + 46, 213, 22, 253, 8, 218, 145, 253, 170, 156, 159, 96, + ]), + Bytes32([ + 107, 130, 208, 55, 16, 249, 46, 4, 98, 236, 130, 52, 190, 126, 232, 229, 108, 126, 239, + 175, 25, 229, 84, 33, 249, 66, 128, 162, 47, 221, 110, 163, + ]), + Bytes32([ + 239, 176, 14, 134, 210, 125, 177, 40, 200, 42, 229, 32, 200, 58, 9, 73, 210, 161, 8, 105, + 4, 70, 19, 134, 117, 132, 167, 18, 16, 108, 207, 113, + ]), + Bytes32([ + 237, 116, 250, 79, 151, 228, 171, 189, 89, 213, 122, 121, 55, 25, 52, 238, 79, 19, 31, 194, + 73, 158, 43, 78, 86, 194, 36, 141, 105, 10, 87, 58, + ]), + Bytes32([ + 216, 125, 158, 123, 121, 158, 74, 152, 46, 18, 196, 115, 233, 149, 205, 189, 10, 232, 174, + 21, 2, 52, 202, 111, 74, 160, 191, 196, 84, 93, 98, 46, + ]), + Bytes32([ + 201, 148, 124, 172, 62, 154, 127, 129, 205, 197, 134, 133, 144, 78, 47, 190, 197, 190, 103, + 44, 129, 57, 155, 227, 152, 212, 130, 12, 192, 223, 244, 68, + ]), + Bytes32([ + 102, 147, 19, 104, 237, 188, 140, 66, 166, 77, 105, 55, 5, 165, 143, 107, 91, 247, 53, 203, + 187, 163, 109, 66, 19, 164, 153, 170, 82, 251, 148, 10, + ]), + Bytes32([ + 33, 89, 246, 96, 201, 15, 77, 99, 252, 111, 250, 152, 124, 80, 50, 57, 197, 124, 254, 202, + 246, 131, 11, 219, 249, 102, 35, 79, 100, 173, 146, 167, + ]), + Bytes32([ + 6, 27, 205, 165, 249, 112, 70, 105, 232, 156, 155, 204, 71, 5, 21, 239, 91, 69, 248, 111, + 218, 154, 187, 85, 118, 72, 210, 99, 138, 40, 233, 75, + ]), +]; + +const MODULE_HASHES: &[Bytes32; 64] = &[ + Bytes32([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, + ]), + Bytes32([ + 252, 178, 61, 5, 174, 66, 106, 30, 131, 163, 114, 90, 231, 83, 228, 153, 108, 255, 60, 37, + 254, 139, 153, 129, 208, 156, 109, 74, 233, 129, 174, 182, + ]), + Bytes32([ + 128, 23, 154, 62, 181, 220, 154, 183, 222, 214, 79, 76, 162, 169, 179, 162, 185, 93, 179, + 115, 161, 55, 19, 63, 227, 168, 140, 66, 182, 6, 60, 120, + ]), + Bytes32([ + 243, 119, 126, 55, 151, 128, 119, 12, 124, 84, 146, 243, 156, 68, 82, 174, 201, 162, 29, + 73, 29, 77, 103, 205, 194, 250, 45, 133, 39, 71, 224, 177, + ]), + Bytes32([ + 209, 179, 222, 187, 184, 84, 131, 182, 172, 78, 20, 104, 236, 246, 114, 74, 55, 98, 29, 34, + 117, 249, 89, 76, 85, 38, 248, 193, 75, 10, 243, 100, + ]), + Bytes32([ + 195, 14, 58, 139, 148, 140, 218, 174, 131, 204, 69, 84, 127, 8, 142, 88, 136, 83, 253, 241, + 74, 247, 143, 204, 38, 15, 138, 98, 47, 188, 255, 245, + ]), + Bytes32([ + 167, 4, 177, 126, 159, 76, 19, 220, 160, 159, 81, 227, 110, 147, 179, 175, 253, 187, 46, + 24, 15, 168, 153, 84, 4, 167, 247, 225, 121, 175, 134, 85, + ]), + Bytes32([ + 237, 144, 152, 155, 211, 100, 202, 187, 41, 181, 45, 224, 143, 139, 18, 77, 110, 34, 212, + 207, 239, 180, 73, 84, 166, 194, 22, 149, 89, 215, 86, 21, + ]), + Bytes32([ + 226, 192, 91, 176, 242, 185, 212, 189, 178, 80, 238, 252, 183, 82, 220, 242, 22, 237, 84, + 142, 132, 91, 95, 219, 252, 171, 51, 76, 28, 146, 86, 155, + ]), + Bytes32([ + 199, 200, 237, 66, 220, 14, 221, 121, 117, 150, 118, 36, 138, 142, 50, 173, 12, 195, 131, + 90, 27, 189, 247, 221, 89, 151, 41, 27, 135, 207, 137, 172, + ]), + Bytes32([ + 160, 60, 124, 200, 56, 240, 67, 161, 121, 95, 247, 51, 153, 249, 125, 88, 231, 2, 85, 167, + 251, 187, 21, 103, 49, 132, 213, 52, 219, 30, 146, 59, + ]), + Bytes32([ + 123, 52, 142, 254, 117, 160, 215, 99, 155, 130, 134, 183, 76, 99, 166, 246, 49, 51, 142, + 67, 116, 234, 69, 199, 201, 19, 70, 3, 208, 247, 40, 82, + ]), + Bytes32([ + 165, 32, 183, 79, 246, 224, 220, 103, 11, 160, 233, 66, 129, 238, 194, 73, 17, 37, 128, + 185, 175, 88, 144, 228, 231, 185, 22, 17, 149, 174, 72, 1, + ]), + Bytes32([ + 12, 24, 45, 128, 57, 246, 187, 176, 154, 247, 169, 18, 116, 176, 70, 246, 203, 141, 211, + 104, 5, 147, 78, 214, 161, 71, 33, 97, 7, 21, 158, 61, + ]), + Bytes32([ + 215, 18, 110, 29, 36, 34, 133, 219, 15, 204, 206, 158, 186, 213, 181, 41, 184, 160, 71, + 165, 163, 121, 88, 106, 228, 58, 34, 105, 112, 168, 84, 123, + ]), + Bytes32([ + 6, 169, 156, 220, 55, 53, 26, 229, 104, 60, 113, 111, 153, 18, 251, 234, 179, 159, 56, 14, + 112, 7, 144, 199, 45, 180, 116, 239, 21, 207, 173, 205, + ]), + Bytes32([ + 24, 172, 76, 192, 21, 91, 250, 75, 196, 119, 231, 221, 13, 239, 113, 39, 62, 40, 165, 26, + 245, 16, 61, 106, 39, 28, 78, 212, 249, 230, 84, 108, + ]), + Bytes32([ + 248, 34, 1, 62, 213, 235, 20, 21, 220, 78, 178, 126, 184, 102, 242, 50, 49, 174, 163, 122, + 166, 96, 40, 110, 78, 199, 89, 158, 155, 251, 233, 49, + ]), + Bytes32([ + 103, 54, 133, 19, 189, 117, 46, 209, 17, 2, 64, 251, 77, 195, 197, 91, 38, 48, 214, 36, 96, + 255, 133, 87, 244, 101, 166, 218, 167, 129, 225, 167, + ]), + Bytes32([ + 196, 159, 226, 85, 100, 246, 179, 251, 133, 33, 114, 51, 104, 245, 81, 54, 137, 126, 245, + 76, 112, 146, 139, 185, 190, 19, 106, 132, 48, 203, 34, 237, + ]), + Bytes32([ + 41, 190, 7, 23, 227, 80, 13, 134, 244, 18, 215, 148, 1, 72, 1, 22, 91, 165, 182, 69, 160, + 179, 241, 106, 254, 116, 245, 131, 136, 35, 190, 226, + ]), + Bytes32([ + 205, 132, 211, 121, 67, 210, 191, 102, 218, 92, 56, 204, 68, 160, 188, 100, 229, 80, 77, + 27, 26, 86, 248, 86, 216, 39, 242, 211, 183, 139, 219, 83, + ]), + Bytes32([ + 13, 215, 68, 100, 180, 74, 23, 58, 157, 140, 76, 31, 114, 254, 85, 78, 234, 40, 251, 199, + 35, 204, 111, 243, 163, 64, 221, 21, 96, 55, 127, 118, + ]), + Bytes32([ + 225, 14, 246, 105, 64, 96, 225, 200, 186, 66, 40, 253, 131, 80, 102, 178, 83, 22, 150, 32, + 15, 139, 76, 172, 144, 3, 30, 43, 217, 213, 89, 43, + ]), + Bytes32([ + 200, 83, 93, 27, 98, 246, 87, 100, 53, 166, 89, 138, 157, 88, 148, 245, 61, 115, 246, 20, + 142, 156, 54, 237, 179, 64, 148, 218, 67, 5, 142, 248, + ]), + Bytes32([ + 159, 25, 150, 25, 114, 252, 162, 57, 21, 125, 147, 111, 220, 117, 17, 107, 40, 21, 157, + 183, 6, 63, 226, 37, 64, 179, 133, 58, 107, 245, 78, 14, + ]), + Bytes32([ + 58, 243, 16, 193, 174, 42, 255, 46, 40, 128, 26, 167, 173, 164, 71, 226, 155, 205, 26, 127, + 154, 24, 74, 244, 106, 251, 25, 221, 197, 176, 194, 17, + ]), + Bytes32([ + 51, 47, 251, 206, 27, 9, 142, 27, 253, 132, 189, 62, 109, 150, 246, 2, 137, 90, 157, 185, + 59, 96, 0, 154, 29, 17, 231, 3, 84, 95, 49, 51, + ]), + Bytes32([ + 250, 158, 158, 248, 206, 150, 95, 254, 53, 140, 80, 102, 67, 26, 76, 127, 236, 86, 182, + 120, 72, 239, 190, 118, 60, 77, 28, 82, 152, 234, 73, 201, + ]), + Bytes32([ + 255, 137, 155, 144, 74, 231, 231, 43, 187, 202, 137, 66, 205, 37, 40, 225, 0, 33, 87, 137, + 113, 163, 58, 64, 147, 74, 123, 128, 19, 156, 46, 83, + ]), + Bytes32([ + 207, 61, 79, 4, 202, 96, 238, 144, 154, 230, 145, 65, 230, 130, 246, 10, 140, 71, 66, 246, + 53, 230, 148, 1, 248, 103, 145, 149, 157, 38, 169, 8, + ]), + Bytes32([ + 33, 168, 110, 82, 84, 151, 70, 98, 115, 2, 221, 116, 210, 10, 247, 189, 203, 245, 186, 86, + 121, 142, 218, 95, 68, 241, 21, 219, 131, 241, 185, 203, + ]), + Bytes32([ + 125, 247, 7, 149, 181, 71, 68, 141, 123, 207, 144, 83, 170, 27, 244, 37, 210, 148, 0, 140, + 144, 159, 237, 216, 227, 247, 85, 229, 80, 174, 40, 48, + ]), + Bytes32([ + 0, 205, 68, 89, 77, 97, 204, 140, 113, 244, 168, 76, 49, 137, 236, 21, 132, 99, 10, 206, + 195, 206, 208, 120, 227, 77, 21, 223, 145, 227, 48, 139, + ]), + Bytes32([ + 10, 153, 106, 155, 149, 65, 211, 247, 246, 5, 129, 126, 74, 14, 113, 168, 152, 154, 205, + 139, 49, 2, 182, 65, 163, 15, 62, 126, 62, 16, 26, 36, + ]), + Bytes32([ + 24, 251, 204, 187, 179, 146, 114, 66, 110, 182, 131, 248, 36, 170, 169, 128, 22, 221, 134, + 193, 121, 189, 128, 94, 83, 255, 226, 37, 133, 83, 220, 181, + ]), + Bytes32([ + 25, 52, 245, 155, 53, 39, 73, 130, 12, 73, 106, 255, 44, 204, 208, 41, 32, 126, 159, 98, + 197, 67, 58, 90, 6, 146, 25, 166, 195, 194, 215, 70, + ]), + Bytes32([ + 194, 167, 206, 222, 96, 6, 86, 241, 95, 105, 225, 147, 99, 93, 201, 220, 166, 145, 4, 25, + 168, 188, 21, 168, 80, 163, 132, 216, 239, 19, 115, 36, + ]), + Bytes32([ + 243, 16, 4, 9, 140, 42, 62, 105, 97, 7, 4, 234, 200, 227, 42, 10, 51, 167, 38, 148, 69, + 154, 143, 179, 94, 177, 248, 28, 33, 61, 166, 187, + ]), + Bytes32([ + 156, 201, 97, 197, 223, 83, 229, 186, 187, 33, 49, 210, 181, 222, 180, 234, 150, 33, 99, + 168, 157, 26, 241, 1, 213, 220, 165, 8, 31, 76, 134, 243, + ]), + Bytes32([ + 34, 218, 29, 74, 212, 218, 95, 6, 53, 10, 63, 174, 119, 90, 90, 35, 92, 124, 169, 222, 75, + 61, 199, 118, 132, 40, 193, 9, 250, 80, 12, 197, + ]), + Bytes32([ + 102, 137, 75, 155, 181, 222, 86, 192, 140, 167, 3, 178, 212, 165, 49, 163, 86, 8, 76, 240, + 32, 202, 34, 159, 217, 173, 101, 125, 237, 34, 48, 179, + ]), + Bytes32([ + 136, 53, 156, 19, 107, 234, 210, 218, 180, 236, 9, 7, 80, 63, 146, 57, 77, 106, 135, 64, + 23, 184, 9, 139, 61, 237, 225, 157, 183, 182, 137, 31, + ]), + Bytes32([ + 237, 226, 115, 218, 228, 122, 45, 46, 67, 21, 130, 204, 80, 2, 28, 47, 148, 191, 28, 33, + 177, 47, 207, 14, 33, 117, 28, 46, 88, 254, 97, 227, + ]), + Bytes32([ + 38, 87, 252, 28, 105, 20, 117, 29, 234, 237, 60, 91, 48, 101, 219, 219, 24, 52, 169, 243, + 71, 214, 117, 41, 207, 17, 148, 77, 218, 44, 122, 167, + ]), + Bytes32([ + 235, 31, 185, 222, 3, 22, 222, 174, 126, 128, 55, 67, 201, 229, 55, 28, 241, 144, 184, 85, + 141, 153, 183, 211, 66, 80, 152, 248, 214, 130, 64, 205, + ]), + Bytes32([ + 52, 169, 226, 179, 195, 44, 245, 101, 35, 92, 145, 174, 152, 212, 192, 148, 241, 72, 248, + 61, 239, 124, 98, 213, 181, 75, 84, 121, 57, 90, 172, 164, + ]), + Bytes32([ + 27, 19, 12, 74, 128, 193, 65, 63, 15, 6, 174, 167, 79, 56, 88, 13, 117, 6, 189, 14, 11, 13, + 133, 136, 141, 64, 74, 17, 159, 192, 251, 242, + ]), + Bytes32([ + 101, 69, 132, 221, 127, 162, 162, 92, 248, 131, 39, 52, 169, 170, 231, 137, 123, 106, 161, + 16, 202, 242, 216, 245, 211, 170, 74, 128, 108, 243, 166, 38, + ]), + Bytes32([ + 157, 82, 193, 234, 117, 199, 118, 245, 63, 31, 186, 32, 135, 233, 29, 213, 217, 119, 208, + 64, 149, 116, 117, 129, 211, 225, 207, 22, 225, 99, 194, 222, + ]), + Bytes32([ + 112, 194, 64, 229, 154, 223, 109, 8, 191, 120, 32, 176, 154, 7, 174, 103, 2, 156, 230, 164, + 245, 252, 0, 116, 213, 40, 20, 239, 135, 62, 198, 42, + ]), + Bytes32([ + 32, 91, 5, 170, 221, 134, 167, 144, 16, 130, 237, 114, 71, 184, 131, 223, 92, 149, 161, 68, + 196, 56, 73, 148, 203, 187, 174, 178, 178, 53, 156, 177, + ]), + Bytes32([ + 234, 97, 64, 211, 81, 59, 14, 249, 38, 8, 247, 37, 243, 37, 88, 234, 19, 54, 146, 63, 88, + 79, 127, 190, 144, 51, 240, 19, 15, 143, 18, 9, + ]), + Bytes32([ + 247, 102, 43, 28, 24, 201, 189, 198, 66, 58, 210, 51, 174, 234, 52, 239, 37, 83, 212, 184, + 127, 170, 168, 127, 47, 11, 248, 213, 84, 3, 201, 181, + ]), + Bytes32([ + 126, 18, 116, 33, 129, 25, 247, 2, 162, 202, 103, 201, 237, 67, 28, 80, 46, 182, 163, 239, + 90, 204, 47, 196, 137, 79, 19, 165, 209, 84, 140, 132, + ]), + Bytes32([ + 189, 160, 148, 101, 82, 248, 215, 206, 90, 91, 205, 148, 37, 38, 172, 82, 223, 228, 66, + 173, 220, 246, 194, 73, 231, 17, 220, 47, 222, 175, 7, 24, + ]), + Bytes32([ + 237, 149, 54, 210, 99, 72, 182, 55, 69, 140, 161, 215, 104, 90, 104, 26, 216, 241, 232, + 210, 37, 77, 56, 216, 6, 160, 199, 247, 164, 227, 142, 255, + ]), + Bytes32([ + 11, 57, 131, 77, 203, 89, 156, 126, 200, 215, 103, 209, 70, 235, 78, 247, 0, 126, 209, 248, + 220, 135, 158, 244, 141, 74, 6, 158, 19, 37, 42, 39, + ]), + Bytes32([ + 107, 131, 231, 102, 64, 182, 78, 253, 77, 143, 94, 34, 199, 93, 44, 131, 169, 211, 71, 223, + 53, 230, 59, 107, 146, 22, 188, 207, 210, 220, 168, 129, + ]), + Bytes32([ + 78, 71, 246, 216, 154, 188, 152, 6, 234, 254, 21, 1, 128, 148, 56, 91, 250, 95, 76, 103, + 15, 250, 137, 186, 114, 254, 122, 69, 208, 242, 69, 111, + ]), + Bytes32([ + 82, 241, 242, 155, 248, 109, 84, 118, 225, 255, 122, 40, 3, 87, 20, 223, 212, 205, 141, + 178, 32, 229, 234, 146, 101, 73, 79, 55, 28, 82, 81, 178, + ]), + Bytes32([ + 35, 169, 243, 198, 215, 220, 113, 91, 6, 79, 48, 201, 97, 143, 212, 34, 247, 46, 38, 93, + 166, 213, 158, 218, 61, 86, 240, 149, 224, 174, 225, 73, + ]), + Bytes32([ + 25, 108, 21, 212, 193, 192, 96, 39, 57, 72, 193, 99, 239, 0, 231, 83, 183, 195, 105, 33, + 220, 187, 18, 148, 43, 30, 42, 127, 192, 240, 13, 224, + ]), + Bytes32([ + 119, 117, 186, 31, 43, 128, 143, 25, 182, 135, 156, 58, 152, 81, 70, 116, 171, 137, 125, + 157, 152, 163, 34, 65, 240, 178, 12, 24, 168, 111, 39, 35, + ]), +]; + +pub const EMPTY_HASH: &Bytes32 = &Bytes32([0; 32]); + +pub const ZERO_HASHES: &[&[Bytes32; 64]; 7] = &[ + VALUE_HASHES, + FUNCTION_HASHES, + INSTRUCTION_HASHES, + MEMORY_HASHES, + TABLE_HASHES, + TABLE_ELEMENT_HASHES, + MODULE_HASHES, +]; diff --git a/arbitrator/prover/src/programs/config.rs b/arbitrator/prover/src/programs/config.rs index 1a37294b04..0353589358 100644 --- a/arbitrator/prover/src/programs/config.rs +++ b/arbitrator/prover/src/programs/config.rs @@ -17,7 +17,7 @@ use { meter::Meter, start::StartMover, MiddlewareWrapper, }, std::sync::Arc, - wasmer::{Cranelift, CraneliftOptLevel, Engine, Store}, + wasmer::{Cranelift, CraneliftOptLevel, Engine, Store, Target}, wasmer_compiler_singlepass::Singlepass, }; @@ -180,17 +180,19 @@ impl CompileConfig { } #[cfg(feature = "native")] - pub fn store(&self) -> Store { - let mut compiler: Box = match self.debug.cranelift { + pub fn engine(&self, target: Target) -> Engine { + use wasmer::sys::EngineBuilder; + + let mut wasmer_config: Box = match self.debug.cranelift { true => { - let mut compiler = Cranelift::new(); - compiler.opt_level(CraneliftOptLevel::Speed); - Box::new(compiler) + let mut wasmer_config = Cranelift::new(); + wasmer_config.opt_level(CraneliftOptLevel::Speed); + Box::new(wasmer_config) } false => Box::new(Singlepass::new()), }; - compiler.canonicalize_nans(true); - compiler.enable_verifier(); + wasmer_config.canonicalize_nans(true); + wasmer_config.enable_verifier(); let start = MiddlewareWrapper::new(StartMover::new(self.debug.debug_info)); let meter = MiddlewareWrapper::new(Meter::new(&self.pricing)); @@ -200,22 +202,24 @@ impl CompileConfig { // 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)); + wasmer_config.push_middleware(Arc::new(start)); + wasmer_config.push_middleware(Arc::new(meter)); + wasmer_config.push_middleware(Arc::new(dygas)); + wasmer_config.push_middleware(Arc::new(depth)); + wasmer_config.push_middleware(Arc::new(bound)); if self.debug.count_ops { let counter = Counter::new(); - compiler.push_middleware(Arc::new(MiddlewareWrapper::new(counter))); + wasmer_config.push_middleware(Arc::new(MiddlewareWrapper::new(counter))); } - Store::new(compiler) + EngineBuilder::new(wasmer_config) + .set_target(Some(target)) + .into() } #[cfg(feature = "native")] - pub fn engine(&self) -> Engine { - self.store().engine().clone() + pub fn store(&self, target: Target) -> Store { + Store::new(self.engine(target)) } } diff --git a/arbitrator/prover/src/utils.rs b/arbitrator/prover/src/utils.rs index 49b4ea0c3d..48889e1199 100644 --- a/arbitrator/prover/src/utils.rs +++ b/arbitrator/prover/src/utils.rs @@ -82,13 +82,13 @@ struct RemoteRefType(pub [u8; 3]); impl From for RemoteRefType { fn from(value: RefType) -> Self { - unsafe { std::mem::transmute(value) } + unsafe { std::mem::transmute::(value) } } } impl From for RefType { fn from(value: RemoteRefType) -> Self { - unsafe { std::mem::transmute(value) } + unsafe { std::mem::transmute::(value) } } } diff --git a/arbitrator/prover/src/value.rs b/arbitrator/prover/src/value.rs index 4ec02f5463..6afffdf7a0 100644 --- a/arbitrator/prover/src/value.rs +++ b/arbitrator/prover/src/value.rs @@ -133,11 +133,7 @@ pub struct ProgramCounter { pub inst: u32, } -#[cfg(not(any( - target_pointer_width = "32", - target_pointer_width = "64", - target_pointer_width = "128" -)))] +#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64",)))] compile_error!("Architectures with less than a 32 bit pointer width are not supported"); impl ProgramCounter { diff --git a/arbitrator/prover/test-cases/rust/.cargo/config.toml b/arbitrator/prover/test-cases/rust/.cargo/config.toml new file mode 100644 index 0000000000..23ffbe67c1 --- /dev/null +++ b/arbitrator/prover/test-cases/rust/.cargo/config.toml @@ -0,0 +1,10 @@ +[build] +target = "wasm32-wasi" + +[target.wasm32-wasi] +rustflags = [ + "-C", "target-cpu=mvp", +] + +[unstable] +build-std = ["core", "panic_abort", "alloc", "std"] diff --git a/arbitrator/stylus/src/cache.rs b/arbitrator/stylus/src/cache.rs index 06739f2219..fa38d45419 100644 --- a/arbitrator/stylus/src/cache.rs +++ b/arbitrator/stylus/src/cache.rs @@ -10,6 +10,8 @@ use prover::programs::config::CompileConfig; use std::{collections::HashMap, num::NonZeroUsize}; use wasmer::{Engine, Module, Store}; +use crate::target_cache::target_native; + lazy_static! { static ref INIT_CACHE: Mutex = Mutex::new(InitCache::new(256)); } @@ -120,7 +122,7 @@ impl InitCache { } drop(cache); - let engine = CompileConfig::version(version, debug).engine(); + let engine = CompileConfig::version(version, debug).engine(target_native()); let module = unsafe { Module::deserialize_unchecked(&engine, module)? }; let item = CacheItem::new(module, engine); diff --git a/arbitrator/stylus/src/host.rs b/arbitrator/stylus/src/host.rs index 7854386e23..1afc1b4e51 100644 --- a/arbitrator/stylus/src/host.rs +++ b/arbitrator/stylus/src/host.rs @@ -64,7 +64,7 @@ where unsafe { data.set_len(len); self.view().read_uninit(ptr.into(), &mut data)?; - Ok(mem::transmute(data)) + Ok(mem::transmute::>, Vec>(data)) } } diff --git a/arbitrator/stylus/src/lib.rs b/arbitrator/stylus/src/lib.rs index 3c53359f8b..a252b60a01 100644 --- a/arbitrator/stylus/src/lib.rs +++ b/arbitrator/stylus/src/lib.rs @@ -18,6 +18,7 @@ use native::NativeInstance; use prover::programs::{prelude::*, StylusData}; use run::RunProgram; use std::{marker::PhantomData, mem, ptr}; +use target_cache::{target_cache_get, target_cache_set}; pub use brotli; pub use prover; @@ -29,6 +30,7 @@ pub mod run; mod cache; mod evm_api; +mod target_cache; mod util; #[cfg(test)] @@ -122,9 +124,9 @@ impl RustBytes { } } -/// Instruments and "activates" a user wasm. +/// "activates" a user wasm. /// -/// The `output` is either the serialized asm & module pair or an error string. +/// The `output` is either the module 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. @@ -140,7 +142,6 @@ pub unsafe extern "C" fn stylus_activate( version: u16, debug: bool, output: *mut RustBytes, - asm_len: *mut usize, codehash: *const Bytes32, module_hash: *mut Bytes32, stylus_data: *mut StylusData, @@ -152,18 +153,97 @@ pub unsafe extern "C" fn stylus_activate( 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(); + let (module, info) = match native::activate(wasm, codehash, version, page_limit, debug, gas) { + Ok(val) => val, + Err(err) => return output.write_err(err), + }; + *module_hash = module.hash(); *stylus_data = info; - let mut data = asm; - data.extend(&*module.into_bytes()); - output.write(data); + output.write(module.into_bytes()); + UserOutcomeKind::Success +} + +/// "compiles" a user wasm. +/// +/// The `output` is either the asm or an error string. +/// Returns consensus info such as the module hash and footprint on success. +/// +/// # Safety +/// +/// `output` must not be null. +#[no_mangle] +pub unsafe extern "C" fn stylus_compile( + wasm: GoSliceData, + version: u16, + debug: bool, + name: GoSliceData, + output: *mut RustBytes, +) -> UserOutcomeKind { + let wasm = wasm.slice(); + let output = &mut *output; + let name = match String::from_utf8(name.slice().to_vec()) { + Ok(val) => val, + Err(err) => return output.write_err(err.into()), + }; + let target = match target_cache_get(&name) { + Ok(val) => val, + Err(err) => return output.write_err(err), + }; + + let asm = match native::compile(wasm, version, debug, target) { + Ok(val) => val, + Err(err) => return output.write_err(err), + }; + + output.write(asm); + UserOutcomeKind::Success +} + +#[no_mangle] +/// # Safety +/// +/// `output` must not be null. +pub unsafe extern "C" fn wat_to_wasm(wat: GoSliceData, output: *mut RustBytes) -> UserOutcomeKind { + let output = &mut *output; + let wasm = match wasmer::wat2wasm(wat.slice()) { + Ok(val) => val, + Err(err) => return output.write_err(err.into()), + }; + output.write(wasm.into_owned()); + UserOutcomeKind::Success +} + +/// sets target index to a string +/// +/// String format is: Triple+CpuFeature+CpuFeature.. +/// +/// # Safety +/// +/// `output` must not be null. +#[no_mangle] +pub unsafe extern "C" fn stylus_target_set( + name: GoSliceData, + description: GoSliceData, + output: *mut RustBytes, + native: bool, +) -> UserOutcomeKind { + let output = &mut *output; + let name = match String::from_utf8(name.slice().to_vec()) { + Ok(val) => val, + Err(err) => return output.write_err(err.into()), + }; + + let desc_str = match String::from_utf8(description.slice().to_vec()) { + Ok(val) => val, + Err(err) => return output.write_err(err.into()), + }; + + if let Err(err) = target_cache_set(name, desc_str, native) { + return output.write_err(err); + }; + UserOutcomeKind::Success } diff --git a/arbitrator/stylus/src/native.rs b/arbitrator/stylus/src/native.rs index 2858d59fdc..cc1d191fe2 100644 --- a/arbitrator/stylus/src/native.rs +++ b/arbitrator/stylus/src/native.rs @@ -4,7 +4,7 @@ use crate::{ cache::InitCache, env::{MeterData, WasmEnv}, - host, util, + host, }; use arbutil::{ evm::{ @@ -33,11 +33,13 @@ use std::{ ops::{Deref, DerefMut}, }; use wasmer::{ - imports, AsStoreMut, Function, FunctionEnv, Instance, Memory, Module, Pages, Store, + imports, AsStoreMut, Function, FunctionEnv, Instance, Memory, Module, Pages, Store, Target, TypedFunction, Value, WasmTypeList, }; use wasmer_vm::VMExtern; +use crate::target_cache::target_native; + #[derive(Debug)] pub struct NativeInstance> { pub instance: Instance, @@ -98,7 +100,7 @@ impl> NativeInstance { evm_data: EvmData, ) -> Result { let env = WasmEnv::new(compile, None, evm, evm_data); - let store = env.compile.store(); + let store = env.compile.store(target_native()); let module = unsafe { Module::deserialize_unchecked(&store, module)? }; Self::from_module(module, store, env) } @@ -137,9 +139,10 @@ impl> NativeInstance { evm_data: EvmData, compile: &CompileConfig, config: StylusConfig, + target: Target, ) -> Result { let env = WasmEnv::new(compile.clone(), Some(config), evm_api, evm_data); - let store = env.compile.store(); + let store = env.compile.store(target); let wat_or_wasm = std::fs::read(path)?; let module = Module::new(&store, wat_or_wasm)?; Self::from_module(module, store, env) @@ -347,8 +350,8 @@ impl> StartlessMachine for NativeInstance { } } -pub fn module(wasm: &[u8], compile: CompileConfig) -> Result> { - let mut store = compile.store(); +pub fn module(wasm: &[u8], compile: CompileConfig, target: Target) -> Result> { + let mut store = compile.store(target); let module = Module::new(&store, wasm)?; macro_rules! stub { (u8 <- $($types:tt)+) => { @@ -367,7 +370,7 @@ pub fn module(wasm: &[u8], compile: CompileConfig) -> Result> { Function::new_typed(&mut store, $($types)+ -> f64 { panic!("incomplete import") }) }; ($($types:tt)+) => { - Function::new_typed(&mut store, $($types)+ panic!("incomplete import")) + Function::new_typed(&mut store, $($types)+ -> () { panic!("incomplete import") }) }; } let mut imports = imports! { @@ -428,7 +431,6 @@ pub fn module(wasm: &[u8], compile: CompileConfig) -> Result> { 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()) @@ -441,14 +443,14 @@ pub fn activate( page_limit: u16, debug: bool, gas: &mut u64, -) -> Result<(Vec, ProverModule, StylusData)> { - let compile = CompileConfig::version(version, debug); +) -> Result<(ProverModule, StylusData)> { 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)) + Ok((module, stylus_data)) +} + +pub fn compile(wasm: &[u8], version: u16, debug: bool, target: Target) -> Result> { + let compile = CompileConfig::version(version, debug); + self::module(wasm, compile, target) } diff --git a/arbitrator/stylus/src/target_cache.rs b/arbitrator/stylus/src/target_cache.rs new file mode 100644 index 0000000000..a1d63829d6 --- /dev/null +++ b/arbitrator/stylus/src/target_cache.rs @@ -0,0 +1,81 @@ +// Copyright 2022-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +use eyre::{eyre, OptionExt, Result}; +use lazy_static::lazy_static; +use parking_lot::RwLock; +use std::{collections::HashMap, str::FromStr}; +use wasmer_types::{CpuFeature, Target, Triple}; + +lazy_static! { + static ref TARGET_CACHE: RwLock> = RwLock::new(HashMap::new()); + static ref TARGET_NATIVE: RwLock = RwLock::new(Target::default()); +} + +fn target_from_string(input: String) -> Result { + if input.is_empty() { + return Ok(Target::default()); + } + let mut parts = input.split('+'); + + let Some(triple_string) = parts.next() else { + return Err(eyre!("no architecture")); + }; + + let triple = match Triple::from_str(triple_string) { + Ok(val) => val, + Err(e) => return Err(eyre!(e)), + }; + + let mut features = CpuFeature::set(); + for flag in parts { + features.insert(CpuFeature::from_str(flag)?); + } + + Ok(Target::new(triple, features)) +} + +/// Populates `TARGET_CACHE` inserting target specified by `description` under `name` key. +/// Additionally, if `native` is set it sets `TARGET_NATIVE` to the specified target. +pub fn target_cache_set(name: String, description: String, native: bool) -> Result<()> { + let target = target_from_string(description)?; + + if native { + if !target.is_native() { + return Err(eyre!("arch not native")); + } + let flags_not_supported = Target::default() + .cpu_features() + .complement() + .intersection(*target.cpu_features()); + if !flags_not_supported.is_empty() { + let mut err_message = String::new(); + err_message.push_str("cpu flags not supported on local cpu for: "); + for item in flags_not_supported.iter() { + err_message.push('+'); + err_message.push_str(&item.to_string()); + } + return Err(eyre!(err_message)); + } + *TARGET_NATIVE.write() = target.clone(); + } + + TARGET_CACHE.write().insert(name, target); + + Ok(()) +} + +pub fn target_native() -> Target { + TARGET_NATIVE.read().clone() +} + +pub fn target_cache_get(name: &str) -> Result { + if name.is_empty() { + return Ok(TARGET_NATIVE.read().clone()); + } + TARGET_CACHE + .read() + .get(name) + .cloned() + .ok_or_eyre("arch not set") +} diff --git a/arbitrator/stylus/src/test/api.rs b/arbitrator/stylus/src/test/api.rs index 92d7317918..5d9f625e5e 100644 --- a/arbitrator/stylus/src/test/api.rs +++ b/arbitrator/stylus/src/test/api.rs @@ -14,6 +14,7 @@ use eyre::Result; use parking_lot::Mutex; use prover::programs::{memory::MemoryModel, prelude::*}; use std::{collections::HashMap, sync::Arc}; +use wasmer::Target; use super::TestInstance; @@ -53,7 +54,7 @@ impl TestEvmApi { 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())?; + let module = native::module(&wasm, self.compile.clone(), Target::default())?; self.contracts.lock().insert(address, module); self.configs.lock().insert(address, config); Ok(()) diff --git a/arbitrator/stylus/src/test/misc.rs b/arbitrator/stylus/src/test/misc.rs index ae44a885f0..92c4394ae3 100644 --- a/arbitrator/stylus/src/test/misc.rs +++ b/arbitrator/stylus/src/test/misc.rs @@ -9,12 +9,12 @@ use crate::{ }; use eyre::Result; use prover::programs::{prelude::*, start::StartMover}; -use wasmer::{imports, Function}; +use wasmer::{imports, Function, Target}; #[test] fn test_bulk_memory() -> Result<()> { let (compile, config, ink) = test_configs(); - let mut store = compile.store(); + let mut store = compile.store(Target::default()); let filename = "../prover/test-cases/bulk-memory.wat"; let imports = imports! { "env" => { diff --git a/arbitrator/stylus/src/test/mod.rs b/arbitrator/stylus/src/test/mod.rs index d7f3248d31..00c9c62ae4 100644 --- a/arbitrator/stylus/src/test/mod.rs +++ b/arbitrator/stylus/src/test/mod.rs @@ -16,7 +16,7 @@ use rand::prelude::*; use std::{collections::HashMap, path::Path, sync::Arc}; use wasmer::{ imports, wasmparser::Operator, CompilerConfig, Function, FunctionEnv, Imports, Instance, - Module, Store, + Module, Store, Target, }; use wasmer_compiler_singlepass::Singlepass; @@ -33,7 +33,7 @@ type TestInstance = NativeInstance; impl TestInstance { fn new_test(path: &str, compile: CompileConfig) -> Result { - let mut store = compile.store(); + let mut store = compile.store(Target::default()); let imports = imports! { "test" => { "noop" => Function::new_typed(&mut store, || {}), @@ -86,7 +86,14 @@ impl TestInstance { 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 native = Self::from_path( + path, + evm.clone(), + evm_data, + compile, + config, + Target::default(), + )?; let footprint = native.memory().ty(&native.store).minimum.0 as u16; evm.set_pages(footprint); Ok((native, evm)) @@ -150,7 +157,6 @@ fn new_test_machine(path: &str, compile: &CompileConfig) -> Result { &[lib], bin, false, - false, true, compile.debug.debug_funcs, true, diff --git a/arbitrator/stylus/tests/.cargo/config.toml b/arbitrator/stylus/tests/.cargo/config.toml index ff01b66852..702a5c04b3 100644 --- a/arbitrator/stylus/tests/.cargo/config.toml +++ b/arbitrator/stylus/tests/.cargo/config.toml @@ -3,6 +3,7 @@ target = "wasm32-unknown-unknown" [target.wasm32-unknown-unknown] rustflags = [ + "-C", "target-cpu=mvp", "-C", "link-arg=-zstack-size=8192", # "-C", "link-arg=--export=__heap_base", # "-C", "link-arg=--export=__data_end", diff --git a/arbitrator/stylus/tests/create/.cargo/config b/arbitrator/stylus/tests/create/.cargo/config deleted file mode 100644 index f4e8c002fc..0000000000 --- a/arbitrator/stylus/tests/create/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/create/.cargo/config.toml b/arbitrator/stylus/tests/create/.cargo/config.toml new file mode 100644 index 0000000000..ea2ec38b12 --- /dev/null +++ b/arbitrator/stylus/tests/create/.cargo/config.toml @@ -0,0 +1,7 @@ +[build] +target = "wasm32-unknown-unknown" + +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "target-cpu=mvp", +] diff --git a/arbitrator/stylus/tests/evm-data/.cargo/config b/arbitrator/stylus/tests/evm-data/.cargo/config deleted file mode 100644 index f4e8c002fc..0000000000 --- a/arbitrator/stylus/tests/evm-data/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/evm-data/.cargo/config.toml b/arbitrator/stylus/tests/evm-data/.cargo/config.toml new file mode 100644 index 0000000000..ea2ec38b12 --- /dev/null +++ b/arbitrator/stylus/tests/evm-data/.cargo/config.toml @@ -0,0 +1,7 @@ +[build] +target = "wasm32-unknown-unknown" + +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "target-cpu=mvp", +] diff --git a/arbitrator/stylus/tests/fallible/.cargo/config b/arbitrator/stylus/tests/fallible/.cargo/config deleted file mode 100644 index f4e8c002fc..0000000000 --- a/arbitrator/stylus/tests/fallible/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/fallible/.cargo/config.toml b/arbitrator/stylus/tests/fallible/.cargo/config.toml new file mode 100644 index 0000000000..ea2ec38b12 --- /dev/null +++ b/arbitrator/stylus/tests/fallible/.cargo/config.toml @@ -0,0 +1,7 @@ +[build] +target = "wasm32-unknown-unknown" + +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "target-cpu=mvp", +] diff --git a/arbitrator/stylus/tests/log/.cargo/config b/arbitrator/stylus/tests/log/.cargo/config deleted file mode 100644 index f4e8c002fc..0000000000 --- a/arbitrator/stylus/tests/log/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/log/.cargo/config.toml b/arbitrator/stylus/tests/log/.cargo/config.toml new file mode 100644 index 0000000000..ea2ec38b12 --- /dev/null +++ b/arbitrator/stylus/tests/log/.cargo/config.toml @@ -0,0 +1,7 @@ +[build] +target = "wasm32-unknown-unknown" + +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "target-cpu=mvp", +] diff --git a/arbitrator/stylus/tests/multicall/.cargo/config b/arbitrator/stylus/tests/multicall/.cargo/config deleted file mode 100644 index f4e8c002fc..0000000000 --- a/arbitrator/stylus/tests/multicall/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/multicall/.cargo/config.toml b/arbitrator/stylus/tests/multicall/.cargo/config.toml new file mode 100644 index 0000000000..ea2ec38b12 --- /dev/null +++ b/arbitrator/stylus/tests/multicall/.cargo/config.toml @@ -0,0 +1,7 @@ +[build] +target = "wasm32-unknown-unknown" + +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "target-cpu=mvp", +] diff --git a/arbitrator/stylus/tests/read-return-data/.cargo/config b/arbitrator/stylus/tests/read-return-data/.cargo/config deleted file mode 100644 index aa59d2ee1c..0000000000 --- a/arbitrator/stylus/tests/read-return-data/.cargo/config +++ /dev/null @@ -1,3 +0,0 @@ -[build] -target = "wasm32-unknown-unknown" - diff --git a/arbitrator/stylus/tests/read-return-data/.cargo/config.toml b/arbitrator/stylus/tests/read-return-data/.cargo/config.toml new file mode 100644 index 0000000000..ea2ec38b12 --- /dev/null +++ b/arbitrator/stylus/tests/read-return-data/.cargo/config.toml @@ -0,0 +1,7 @@ +[build] +target = "wasm32-unknown-unknown" + +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "target-cpu=mvp", +] diff --git a/arbitrator/stylus/tests/sdk-storage/.cargo/config b/arbitrator/stylus/tests/sdk-storage/.cargo/config deleted file mode 100644 index f4e8c002fc..0000000000 --- a/arbitrator/stylus/tests/sdk-storage/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/sdk-storage/.cargo/config.toml b/arbitrator/stylus/tests/sdk-storage/.cargo/config.toml new file mode 100644 index 0000000000..ea2ec38b12 --- /dev/null +++ b/arbitrator/stylus/tests/sdk-storage/.cargo/config.toml @@ -0,0 +1,7 @@ +[build] +target = "wasm32-unknown-unknown" + +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "target-cpu=mvp", +] diff --git a/arbitrator/stylus/tests/storage/.cargo/config b/arbitrator/stylus/tests/storage/.cargo/config deleted file mode 100644 index f4e8c002fc..0000000000 --- a/arbitrator/stylus/tests/storage/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[build] -target = "wasm32-unknown-unknown" diff --git a/arbitrator/stylus/tests/storage/.cargo/config.toml b/arbitrator/stylus/tests/storage/.cargo/config.toml new file mode 100644 index 0000000000..ea2ec38b12 --- /dev/null +++ b/arbitrator/stylus/tests/storage/.cargo/config.toml @@ -0,0 +1,7 @@ +[build] +target = "wasm32-unknown-unknown" + +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "target-cpu=mvp", +] diff --git a/arbitrator/wasm-libraries/.cargo/config.toml b/arbitrator/wasm-libraries/.cargo/config.toml new file mode 100644 index 0000000000..797b856fbf --- /dev/null +++ b/arbitrator/wasm-libraries/.cargo/config.toml @@ -0,0 +1,12 @@ +[target.wasm32-unknown-unknown] +rustflags = [ + "-C", "target-cpu=mvp", +] + +[target.wasm32-wasi] +rustflags = [ + "-C", "target-cpu=mvp", +] + +[unstable] +build-std = ["core", "panic_abort", "alloc", "std"] diff --git a/arbitrator/wasm-libraries/Cargo.lock b/arbitrator/wasm-libraries/Cargo.lock index bea50f6592..7620ff538b 100644 --- a/arbitrator/wasm-libraries/Cargo.lock +++ b/arbitrator/wasm-libraries/Cargo.lock @@ -27,9 +27,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "ansi_term" @@ -87,9 +87,9 @@ dependencies = [ [[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 = "bincode" @@ -108,9 +108,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitvec" @@ -120,6 +120,7 @@ checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", + "serde", "tap", "wyz", ] @@ -160,9 +161,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytecheck" @@ -188,9 +189,9 @@ dependencies = [ [[package]] name = "bytes" -version = "1.5.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9" [[package]] name = "caller-env" @@ -272,12 +273,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.20.8", - "darling_macro 0.20.8", + "darling_core 0.20.10", + "darling_macro 0.20.10", ] [[package]] @@ -296,15 +297,15 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.72", ] [[package]] @@ -320,13 +321,13 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.20.8", + "darling_core 0.20.10", "quote", - "syn 2.0.52", + "syn 2.0.72", ] [[package]] @@ -342,15 +343,15 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.72", ] [[package]] @@ -374,9 +375,9 @@ dependencies = [ [[package]] name = "either" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "enum-iterator" @@ -384,7 +385,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" dependencies = [ - "enum-iterator-derive", + "enum-iterator-derive 0.7.0", +] + +[[package]] +name = "enum-iterator" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c280b9e6b3ae19e152d8e31cf47f18389781e119d4013a2a2bb0180e5facc635" +dependencies = [ + "enum-iterator-derive 1.4.0", ] [[package]] @@ -398,25 +408,36 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "enum-iterator-derive" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "enumset" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226c0da7462c13fb57e5cc9e0dc8f0635e7d27f276a3a7fd30054647f669007d" +checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.8.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08b6c6ab82d70f08844964ba10c7babb716de2ecaeab9be5717918a5177d3af" +checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" dependencies = [ - "darling 0.20.8", + "darling 0.20.10", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.72", ] [[package]] @@ -467,9 +488,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if 1.0.0", "libc", @@ -487,9 +508,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash 0.8.11", "allocator-api2", @@ -551,12 +572,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.5" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] @@ -570,9 +591,9 @@ dependencies = [ [[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 = "keccak" @@ -585,9 +606,9 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "leb128" @@ -597,15 +618,15 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -613,18 +634,18 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.14.5", ] [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memory_units" @@ -667,9 +688,9 @@ dependencies = [ [[package]] name = "num" -version = "0.4.1" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -681,20 +702,19 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] @@ -707,7 +727,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.72", ] [[package]] @@ -721,9 +741,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -732,11 +752,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -744,32 +763,32 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_enum" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02339744ee7253741199f897151b38e72257d13802d4ee837285cc2990a90845" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.72", ] [[package]] @@ -786,9 +805,9 @@ 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", @@ -796,9 +815,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if 1.0.0", "libc", @@ -809,9 +828,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "proc-macro-crate" @@ -848,9 +867,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] @@ -865,9 +884,11 @@ version = "0.1.0" dependencies = [ "arbutil", "bincode", + "bitvec", "brotli", "derivative", "digest 0.9.0", + "enum-iterator 2.1.0", "eyre", "fnv", "hex", @@ -918,9 +939,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -957,11 +978,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] @@ -1023,9 +1044,9 @@ checksum = "89dc553bc0cf4512a8b96caa2e21ed5f6e4b66bf28a1bd08fd9eb07c0b39b28c" [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustc_version" @@ -1038,9 +1059,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" @@ -1056,37 +1077,38 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.72", ] [[package]] name = "serde_json" -version = "1.0.114" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -1173,9 +1195,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" dependencies = [ "serde", ] @@ -1235,9 +1257,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -1252,9 +1274,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.14" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "textwrap" @@ -1267,22 +1289,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.72", ] [[package]] @@ -1296,9 +1318,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -1311,9 +1333,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" [[package]] name = "toml_edit" @@ -1321,7 +1343,7 @@ version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ - "indexmap 2.2.5", + "indexmap 2.3.0", "toml_datetime", "winnow", ] @@ -1346,9 +1368,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "user-host" @@ -1392,9 +1414,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" [[package]] name = "vec_map" @@ -1404,9 +1426,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1425,9 +1447,9 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.201.0" +version = "0.215.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9c7d2731df60006819b013f64ccc2019691deccf6e11a1804bc850cd6748f1a" +checksum = "4fb56df3e06b8e6b77e37d2969a50ba51281029a9aeb3855e76b7f49b6418847" dependencies = [ "leb128", ] @@ -1437,7 +1459,7 @@ name = "wasmer-types" version = "4.2.8" dependencies = [ "bytecheck", - "enum-iterator", + "enum-iterator 0.7.0", "enumset", "indexmap 1.9.3", "more-asserts", @@ -1452,16 +1474,16 @@ 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", + "bitflags 2.6.0", + "indexmap 2.3.0", "semver", ] [[package]] name = "wast" -version = "201.0.0" +version = "215.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ef6e1ef34d7da3e2b374fd2b1a9c0227aff6cad596e1b24df9b58d0f6222faa" +checksum = "1ff1d00d893593249e60720be04a7c1f42f1c4dc3806a2869f4e66ab61eb54cb" dependencies = [ "bumpalo", "leb128", @@ -1472,9 +1494,9 @@ dependencies = [ [[package]] name = "wat" -version = "1.201.0" +version = "1.215.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453d5b37a45b98dee4f4cb68015fc73634d7883bbef1c65e6e9c78d454cf3f32" +checksum = "670bf4d9c8cf76ae242d70ded47c546525b6dafaa6871f9bcb065344bf2b4e3d" dependencies = [ "wast", ] @@ -1515,13 +1537,14 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", @@ -1530,45 +1553,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -1590,20 +1619,20 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.72", ] diff --git a/arbitrator/wasm-libraries/user-host-trait/src/lib.rs b/arbitrator/wasm-libraries/user-host-trait/src/lib.rs index 0191718dcc..37af85c382 100644 --- a/arbitrator/wasm-libraries/user-host-trait/src/lib.rs +++ b/arbitrator/wasm-libraries/user-host-trait/src/lib.rs @@ -534,7 +534,7 @@ pub trait UserHost: GasMeteredMachine { 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) + 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 @@ -629,7 +629,8 @@ pub trait UserHost: GasMeteredMachine { self.buy_gas(gas_cost)?; let code = code.slice(); - trace!("account_code_size", self, address, &[], code.len() as u32) + let len = code.len() as u32; + trace!("account_code_size", self, address, be!(len), len) } /// Gets the code hash of the account at the given address. The semantics are equivalent @@ -735,7 +736,7 @@ pub trait UserHost: GasMeteredMachine { 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) + trace!("evm_gas_left", self, &[], be!(gas), gas) } /// Gets the amount of ink remaining after paying for the cost of this hostio. The semantics @@ -747,7 +748,7 @@ pub trait UserHost: GasMeteredMachine { 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) + trace!("evm_ink_left", self, &[], be!(ink), ink) } /// Computes `value ÷ exponent` using 256-bit math, writing the result to the first. diff --git a/arbitrator/wasm-testsuite/Cargo.lock b/arbitrator/wasm-testsuite/Cargo.lock index c6f946b8ea..465464479f 100644 --- a/arbitrator/wasm-testsuite/Cargo.lock +++ b/arbitrator/wasm-testsuite/Cargo.lock @@ -4,11 +4,11 @@ version = 3 [[package]] name = "addr2line" -version = "0.19.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ - "gimli 0.27.0", + "gimli 0.29.0", ] [[package]] @@ -19,15 +19,33 @@ 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 = "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" @@ -41,15 +59,26 @@ dependencies = [ name = "arbutil" version = "0.1.0" dependencies = [ - "sha3 0.10.6", + "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.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" [[package]] name = "atty" @@ -57,26 +86,26 @@ 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.67" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", - "cfg-if", + "cfg-if 1.0.0", "libc", "miniz_oxide", "object", @@ -98,6 +127,25 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "serde", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.9.0" @@ -110,9 +158,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ "generic-array", ] @@ -124,69 +172,92 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" [[package]] -name = "brotli-sys" -version = "0.3.2" +name = "blst" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" +checksum = "4378725facc195f1a538864863f6de233b500a8862747e7f165078a419d5e874" dependencies = [ "cc", - "libc", + "glob", + "threadpool", + "zeroize", ] [[package]] -name = "brotli2" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" +name = "brotli" +version = "0.1.0" dependencies = [ - "brotli-sys", - "libc", + "lazy_static", + "num_enum", + "wasmer", + "wee_alloc", ] [[package]] name = "bumpalo" -version = "3.11.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +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", + "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.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fca2be1d5c43812bae364ee3f30b3afcb7877cf59f4aeb94c66f313a41d2fac9" + +[[package]] +name = "c-kzg" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" +checksum = "94a4bc5367b6284358d2a6a6a1dc2d92ec4b86034561c3b9d3341909752fd848" +dependencies = [ + "blst", + "cc", + "glob", + "hex", + "libc", + "serde", +] [[package]] name = "cc" -version = "1.0.73" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" + +[[package]] +name = "cfg-if" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" @@ -202,44 +273,62 @@ checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" dependencies = [ "ansi_term", "atty", - "bitflags", + "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.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", ] +[[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.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 0.26.2", @@ -251,30 +340,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 = "2b3a63ae57498c3eb495360944a33571754241e15e47e3bcae6082f40fec5866" +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.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", @@ -284,54 +387,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.4" +name = "crossbeam-deque" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" +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.8" +name = "crossbeam-queue" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "autocfg", - "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" @@ -355,12 +453,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.14.2" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ - "darling_core 0.14.2", - "darling_macro 0.14.2", + "darling_core 0.20.10", + "darling_macro 0.20.10", ] [[package]] @@ -374,20 +472,20 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn", + "syn 1.0.109", ] [[package]] name = "darling_core" -version = "0.14.2" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] @@ -398,18 +496,55 @@ checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core 0.13.4", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "darling_macro" -version = "0.14.2" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ - "darling_core 0.14.2", + "darling_core 0.20.10", "quote", - "syn", + "syn 2.0.72", +] + +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.72", ] [[package]] @@ -423,11 +558,11 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.3", + "block-buffer 0.10.4", "crypto-common", ] @@ -437,13 +572,13 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "byteorder", "lazy_static", "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -454,14 +589,14 @@ checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" dependencies = [ "byteorder", "dynasm", - "memmap2", + "memmap2 0.5.10", ] [[package]] name = "either" -version = "1.6.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "enum-iterator" @@ -469,7 +604,16 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" dependencies = [ - "enum-iterator-derive", + "enum-iterator-derive 0.7.0", +] + +[[package]] +name = "enum-iterator" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c280b9e6b3ae19e152d8e31cf47f18389781e119d4013a2a2bb0180e5facc635" +dependencies = [ + "enum-iterator-derive 1.4.0", ] [[package]] @@ -480,35 +624,52 @@ checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", ] [[package]] name = "enumset" -version = "1.0.12" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" +checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" dependencies = [ "enumset_derive", ] [[package]] name = "enumset_derive" -version = "0.6.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" +checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" dependencies = [ - "darling 0.14.2", + "darling 0.20.10", "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] +[[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.8" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" dependencies = [ "indenter", "once_cell", @@ -526,6 +687,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" @@ -537,9 +704,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.5" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -547,11 +714,11 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.8" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "wasi", ] @@ -563,21 +730,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" dependencies = [ "fallible-iterator", - "indexmap", + "indexmap 1.9.3", "stable_deref_trait", ] [[package]] name = "gimli" -version = "0.27.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dec7af912d60cdbd3677c1af9352ebae6fb8394d165568a2234df0fa00f87793" +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" @@ -585,7 +752,17 @@ 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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash 0.8.11", + "allocator-api2", ] [[package]] @@ -606,6 +783,12 @@ 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" @@ -626,40 +809,62 @@ 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.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" +dependencies = [ + "equivalent", + "hashbrown 0.14.5", +] + +[[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.1" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[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.0" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "leb128" @@ -669,15 +874,15 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -685,11 +890,17 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lru" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "cfg-if", + "hashbrown 0.14.5", ] [[package]] @@ -701,30 +912,54 @@ 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.5.0" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] [[package]] name = "memmap2" -version = "0.5.8" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" +checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" dependencies = [ "libc", ] [[package]] name = "memoffset" -version = "0.6.5" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +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" @@ -733,9 +968,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -748,9 +983,9 @@ checksum = "7843ec2de400bcbc6a6328c958dc38e5359da6e93e72e37bc5246bf1ae776389" [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", @@ -769,9 +1004,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", @@ -783,39 +1018,48 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "autocfg", "num-integer", "num-traits", ] [[package]] name = "num-complex" -version = "0.4.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fbc387afefefd5e9e39493299f3069e14a140dd34dc19b4c1c1a8fddb6a790" +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.72", +] + [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-iter" -version = "0.1.43" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -824,11 +1068,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", @@ -836,49 +1079,70 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.72", +] + [[package]] name = "object" -version = "0.30.0" +version = "0.36.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239da7f290cfa979f43f85a8efeee9a8a76d0827c356d37f9d3d7254d6b537fb" +checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" 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" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +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", @@ -886,22 +1150,31 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.5" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ - "cfg-if", + "cfg-if 1.0.0", "libc", "redox_syscall", "smallvec", - "windows-sys 0.42.0", + "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 = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "proc-macro-crate" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" +dependencies = [ + "toml_edit", +] [[package]] name = "proc-macro-error" @@ -912,7 +1185,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "version_check", ] @@ -929,11 +1202,11 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.38" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] @@ -942,22 +1215,32 @@ version = "0.1.0" dependencies = [ "arbutil", "bincode", - "brotli2", + "bitvec", + "brotli", + "c-kzg", + "derivative", "digest 0.9.0", + "enum-iterator 2.1.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", "serde_json", "serde_with", + "sha2 0.9.9", "sha3 0.9.1", "smallvec", "static_assertions", @@ -966,6 +1249,7 @@ dependencies = [ "wasmer-compiler-singlepass", "wasmer-types", "wasmparser", + "wat", ] [[package]] @@ -985,56 +1269,58 @@ checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "quote" -version = "1.0.18" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +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 = "rayon" -version = "1.5.2" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ - "crossbeam-channel", "crossbeam-deque", "crossbeam-utils", - "num_cpus", ] [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags", + "bitflags 2.6.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", @@ -1044,74 +1330,99 @@ dependencies = [ [[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", + "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", + "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", + "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.21" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] -name = "rustversion" -version = "1.0.6" +name = "rustc_version" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] [[package]] name = "ryu" -version = "1.0.9" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" +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" @@ -1119,11 +1430,23 @@ version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" +[[package]] +name = "self_cell" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d369a96f978623eb3dc28807c4852d6cc617fed53da5d3c400feff1ef34a714a" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "serde" -version = "1.0.137" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] @@ -1141,33 +1464,33 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", ] [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_with" -version = "1.13.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b827f2113224f3f19a665136f006709194bdfdcb1fdc1e4b2b5cbac8e0cced54" +checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff" dependencies = [ - "rustversion", "serde", "serde_with_macros", ] @@ -1181,7 +1504,31 @@ dependencies = [ "darling 0.13.4", "proc-macro2", "quote", - "syn", + "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]] @@ -1198,31 +1545,47 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.6" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "digest 0.10.6", + "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.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +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", ] @@ -1272,25 +1635,42 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "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 = "1.0.94" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "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.5" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9410d0f6853b1d94f0e519fb95df60f29d2c1eff2d921ffdf01a4c8a3b54f12d" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "textwrap" @@ -1303,31 +1683,80 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.38" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.38" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.72", +] + +[[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.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" +dependencies = [ + "indexmap 2.3.0", + "toml_datetime", + "winnow", ] [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -1335,47 +1764,53 @@ 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", + "syn 2.0.72", ] [[package]] name = "tracing-core" -version = "0.1.30" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", ] [[package]] name = "typenum" -version = "1.15.0" +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 = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] -name = "unicode-xid" -version = "0.2.3" +name = "uuid" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" [[package]] name = "vec_map" @@ -1385,9 +1820,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -1397,57 +1832,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", + "syn 2.0.72", "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" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1455,28 +1867,28 @@ 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", + "syn 2.0.72", "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.20.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05632e0a66a6ed8cca593c24223aabd6262f256c3693ad9822c315285f010614" +checksum = "1ba64e81215916eaeb48fee292f29401d69235d62d8b8fd92a7b2844ec5ae5f7" dependencies = [ "leb128", ] @@ -1496,19 +1908,22 @@ dependencies = [ [[package]] name = "wasmer" -version = "3.1.0" +version = "4.2.8" dependencies = [ "bytes", - "cfg-if", - "indexmap", + "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", @@ -1520,18 +1935,21 @@ dependencies = [ [[package]] name = "wasmer-compiler" -version = "3.1.0" +version = "4.2.8" dependencies = [ "backtrace", - "cfg-if", - "enum-iterator", + "bytes", + "cfg-if 1.0.0", + "enum-iterator 0.7.0", "enumset", "lazy_static", "leb128", - "memmap2", + "memmap2 0.5.10", "more-asserts", "region", - "rustc-demangle", + "rkyv", + "self_cell", + "shared-buffer", "smallvec", "thiserror", "wasmer-types", @@ -1542,7 +1960,7 @@ dependencies = [ [[package]] name = "wasmer-compiler-cranelift" -version = "3.1.0" +version = "4.2.8" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1559,7 +1977,7 @@ dependencies = [ [[package]] name = "wasmer-compiler-singlepass" -version = "3.1.0" +version = "4.2.8" dependencies = [ "byteorder", "dynasm", @@ -1576,21 +1994,22 @@ dependencies = [ [[package]] name = "wasmer-derive" -version = "3.1.0" +version = "4.2.8" dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] name = "wasmer-types" -version = "3.1.0" +version = "4.2.8" dependencies = [ - "enum-iterator", + "bytecheck", + "enum-iterator 0.7.0", "enumset", - "indexmap", + "indexmap 1.9.3", "more-asserts", "rkyv", "target-lexicon", @@ -1599,14 +2018,18 @@ dependencies = [ [[package]] name = "wasmer-vm" -version = "3.1.0" +version = "4.2.8" dependencies = [ "backtrace", "cc", - "cfg-if", + "cfg-if 1.0.0", "corosensei", - "enum-iterator", - "indexmap", + "crossbeam-queue", + "dashmap", + "derivative", + "enum-iterator 0.7.0", + "fnv", + "indexmap 1.9.3", "lazy_static", "libc", "mach", @@ -1621,15 +2044,20 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.83.0" +version = "0.121.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" +checksum = "9dbe55c8f9d0dbd25d9447a5a889ff90c0cc3feaa7395310d3d826b2c703eaab" +dependencies = [ + "bitflags 2.6.0", + "indexmap 2.3.0", + "semver", +] [[package]] name = "wast" -version = "50.0.0" +version = "64.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2cbb59d4ac799842791fe7e806fa5dbbf6b5554d538e51cc8e176db6ff0ae34" +checksum = "a259b226fd6910225aa7baeba82f9d9933b6d00f2ce1b49b80fa4214328237cc" dependencies = [ "leb128", "memchr", @@ -1639,13 +2067,25 @@ dependencies = [ [[package]] name = "wat" -version = "1.0.52" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584aaf7a1ecf4d383bbe1a25eeab0cbb8ff96acc6796707ff65cde48f4632f15" +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" @@ -1683,24 +2123,34 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.42.0" +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.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 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_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc 0.52.6", ] [[package]] name = "windows_aarch64_gnullvm" -version = "0.42.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -1710,9 +2160,9 @@ checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -1722,9 +2172,15 @@ checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -1734,9 +2190,9 @@ checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -1746,15 +2202,15 @@ checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -1764,6 +2220,64 @@ checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[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 = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.72", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +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.72", +] diff --git a/arbnode/batch_poster.go b/arbnode/batch_poster.go index 2617a9a629..b7eee5cc47 100644 --- a/arbnode/batch_poster.go +++ b/arbnode/batch_poster.go @@ -6,6 +6,7 @@ package arbnode import ( "bytes" "context" + "encoding/binary" "encoding/hex" "errors" "fmt" @@ -105,6 +106,7 @@ type BatchPoster struct { gasRefunderAddr common.Address building *buildingBatch dapWriter daprovider.Writer + dapReaders []daprovider.Reader dataPoster *dataposter.DataPoster redisLock *redislock.Simple messagesPerBatch *arbmath.MovingAverage[uint64] @@ -112,7 +114,7 @@ type BatchPoster struct { // This is an atomic variable that should only be accessed atomically. // An estimate of the number of batches we want to post but haven't yet. // This doesn't include batches which we don't want to post yet due to the L1 bounds. - backlog uint64 + backlog atomic.Uint64 lastHitL1Bounds time.Time // The last time we wanted to post a message but hit the L1 bounds batchReverted atomic.Bool // indicates whether data poster batch was reverted @@ -169,6 +171,7 @@ type BatchPosterConfig struct { GasEstimateBaseFeeMultipleBips arbmath.Bips `koanf:"gas-estimate-base-fee-multiple-bips"` Dangerous BatchPosterDangerousConfig `koanf:"dangerous"` ReorgResistanceMargin time.Duration `koanf:"reorg-resistance-margin" reload:"hot"` + CheckBatchCorrectness bool `koanf:"check-batch-correctness"` gasRefunder common.Address l1BlockBound l1BlockBound @@ -221,6 +224,7 @@ func BatchPosterConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Bool(prefix+".use-access-lists", DefaultBatchPosterConfig.UseAccessLists, "post batches with access lists to reduce gas usage (disabled for L3s)") f.Uint64(prefix+".gas-estimate-base-fee-multiple-bips", uint64(DefaultBatchPosterConfig.GasEstimateBaseFeeMultipleBips), "for gas estimation, use this multiple of the basefee (measured in basis points) as the max fee per gas") f.Duration(prefix+".reorg-resistance-margin", DefaultBatchPosterConfig.ReorgResistanceMargin, "do not post batch if its within this duration from layer 1 minimum bounds. Requires l1-block-bound option not be set to \"ignore\"") + f.Bool(prefix+".check-batch-correctness", DefaultBatchPosterConfig.CheckBatchCorrectness, "setting this to true will run the batch against an inbox multiplexer and verifies that it produces the correct set of messages") redislock.AddConfigOptions(prefix+".redis-lock", f) dataposter.DataPosterConfigAddOptions(prefix+".data-poster", f, dataposter.DefaultDataPosterConfig) genericconf.WalletConfigAddOptions(prefix+".parent-chain-wallet", f, DefaultBatchPosterConfig.ParentChainWallet.Pathname) @@ -238,7 +242,7 @@ var DefaultBatchPosterConfig = BatchPosterConfig{ MaxDelay: time.Hour, WaitForMaxDelay: false, CompressionLevel: brotli.BestCompression, - DASRetentionPeriod: time.Hour * 24 * 15, + DASRetentionPeriod: daprovider.DefaultDASRetentionPeriod, GasRefunderAddress: "", ExtraBatchGas: 50_000, Post4844Blobs: false, @@ -251,6 +255,7 @@ var DefaultBatchPosterConfig = BatchPosterConfig{ RedisLock: redislock.DefaultCfg, GasEstimateBaseFeeMultipleBips: arbmath.OneInBips * 3 / 2, ReorgResistanceMargin: 10 * time.Minute, + CheckBatchCorrectness: true, } var DefaultBatchPosterL1WalletConfig = genericconf.WalletConfig{ @@ -270,7 +275,7 @@ var TestBatchPosterConfig = BatchPosterConfig{ MaxDelay: 0, WaitForMaxDelay: false, CompressionLevel: 2, - DASRetentionPeriod: time.Hour * 24 * 15, + DASRetentionPeriod: daprovider.DefaultDASRetentionPeriod, GasRefunderAddress: "", ExtraBatchGas: 10_000, Post4844Blobs: true, @@ -281,6 +286,7 @@ var TestBatchPosterConfig = BatchPosterConfig{ L1BlockBoundBypass: time.Hour, UseAccessLists: true, GasEstimateBaseFeeMultipleBips: arbmath.OneInBips * 3 / 2, + CheckBatchCorrectness: true, } type BatchPosterOpts struct { @@ -295,6 +301,7 @@ type BatchPosterOpts struct { TransactOpts *bind.TransactOpts DAPWriter daprovider.Writer ParentChainID *big.Int + DAPReaders []daprovider.Reader } func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, error) { @@ -341,6 +348,7 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e bridgeAddr: opts.DeployInfo.Bridge, dapWriter: opts.DAPWriter, redisLock: redisLock, + dapReaders: opts.DAPReaders, } b.messagesPerBatch, err = arbmath.NewMovingAverage[uint64](20) if err != nil { @@ -384,6 +392,42 @@ func NewBatchPoster(ctx context.Context, opts *BatchPosterOpts) (*BatchPoster, e return b, nil } +type simulatedBlobReader struct { + blobs []kzg4844.Blob +} + +func (b *simulatedBlobReader) GetBlobs(ctx context.Context, batchBlockHash common.Hash, versionedHashes []common.Hash) ([]kzg4844.Blob, error) { + return b.blobs, nil +} + +func (b *simulatedBlobReader) Initialize(ctx context.Context) error { return nil } + +type simulatedMuxBackend struct { + batchSeqNum uint64 + positionWithinMessage uint64 + seqMsg []byte + allMsgs map[arbutil.MessageIndex]*arbostypes.MessageWithMetadata + delayedInboxStart uint64 + delayedInbox []*arbostypes.MessageWithMetadata +} + +func (b *simulatedMuxBackend) PeekSequencerInbox() ([]byte, common.Hash, error) { + return b.seqMsg, common.Hash{}, nil +} + +func (b *simulatedMuxBackend) GetSequencerInboxPosition() uint64 { return b.batchSeqNum } +func (b *simulatedMuxBackend) AdvanceSequencerInbox() {} +func (b *simulatedMuxBackend) GetPositionWithinMessage() uint64 { return b.positionWithinMessage } +func (b *simulatedMuxBackend) SetPositionWithinMessage(pos uint64) { b.positionWithinMessage = pos } + +func (b *simulatedMuxBackend) ReadDelayedInbox(seqNum uint64) (*arbostypes.L1IncomingMessage, error) { + pos := arbmath.SaturatingUSub(seqNum, b.delayedInboxStart) + if pos < uint64(len(b.delayedInbox)) { + return b.delayedInbox[pos].Message, nil + } + return nil, fmt.Errorf("error serving ReadDelayedInbox, all delayed messages were read. Requested delayed message position:%d, Total delayed messages: %d", pos, len(b.delayedInbox)) +} + type AccessListOpts struct { SequencerInboxAddr common.Address BridgeAddr common.Address @@ -673,6 +717,7 @@ type buildingBatch struct { msgCount arbutil.MessageIndex haveUsefulMessage bool use4844 bool + muxBackend *simulatedMuxBackend } func newBatchSegments(firstDelayed uint64, config *BatchPosterConfig, backlog uint64, use4844 bool) *batchSegments { @@ -1086,7 +1131,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) if config.IgnoreBlobPrice { use4844 = true } else { - backlog := atomic.LoadUint64(&b.backlog) + backlog := b.backlog.Load() // Logic to prevent switching from non-4844 batches to 4844 batches too often, // so that blocks can be filled efficiently. The geth txpool rejects txs for // accounts that already have the other type of txs in the pool with @@ -1112,6 +1157,12 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) startMsgCount: batchPosition.MessageCount, use4844: use4844, } + if b.config().CheckBatchCorrectness { + b.building.muxBackend = &simulatedMuxBackend{ + batchSeqNum: batchPosition.NextSeqNum, + allMsgs: make(map[arbutil.MessageIndex]*arbostypes.MessageWithMetadata), + } + } } msgCount, err := b.streamer.GetMessageCount() if err != nil { @@ -1230,6 +1281,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) ) break } + isDelayed := msg.DelayedMessagesRead > b.building.segments.delayedMsg success, err := b.building.segments.AddMessage(msg) if err != nil { // Clear our cache @@ -1244,6 +1296,12 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) b.building.haveUsefulMessage = true break } + if config.CheckBatchCorrectness { + b.building.muxBackend.allMsgs[b.building.msgCount] = msg + if isDelayed { + b.building.muxBackend.delayedInbox = append(b.building.muxBackend.delayedInbox, msg) + } + } if msg.Message.Header.Kind != arbostypes.L1MessageType_BatchPostingReport { b.building.haveUsefulMessage = true } @@ -1365,6 +1423,40 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) if err != nil { return false, err } + + if config.CheckBatchCorrectness { + dapReaders := b.dapReaders + if b.building.use4844 { + dapReaders = append(dapReaders, daprovider.NewReaderForBlobReader(&simulatedBlobReader{kzgBlobs})) + } + seqMsg := binary.BigEndian.AppendUint64([]byte{}, l1BoundMinTimestamp) + seqMsg = binary.BigEndian.AppendUint64(seqMsg, l1BoundMaxTimestamp) + seqMsg = binary.BigEndian.AppendUint64(seqMsg, l1BoundMinBlockNumber) + seqMsg = binary.BigEndian.AppendUint64(seqMsg, l1BoundMaxBlockNumber) + seqMsg = binary.BigEndian.AppendUint64(seqMsg, b.building.segments.delayedMsg) + seqMsg = append(seqMsg, sequencerMsg...) + b.building.muxBackend.seqMsg = seqMsg + b.building.muxBackend.delayedInboxStart = batchPosition.DelayedMessageCount + b.building.muxBackend.SetPositionWithinMessage(0) + simMux := arbstate.NewInboxMultiplexer(b.building.muxBackend, batchPosition.DelayedMessageCount, dapReaders, daprovider.KeysetValidate) + log.Debug("Begin checking the correctness of batch against inbox multiplexer", "startMsgSeqNum", batchPosition.MessageCount, "endMsgSeqNum", b.building.msgCount-1) + for i := batchPosition.MessageCount; i < b.building.msgCount; i++ { + msg, err := simMux.Pop(ctx) + if err != nil { + return false, fmt.Errorf("error getting message from simulated inbox multiplexer (Pop) when testing correctness of batch: %w", err) + } + if msg.DelayedMessagesRead != b.building.muxBackend.allMsgs[i].DelayedMessagesRead { + b.building = nil + return false, fmt.Errorf("simulated inbox multiplexer failed to produce correct delayedMessagesRead field for msg with seqNum: %d. Got: %d, Want: %d", i, msg.DelayedMessagesRead, b.building.muxBackend.allMsgs[i].DelayedMessagesRead) + } + if !msg.Message.Equals(b.building.muxBackend.allMsgs[i].Message) { + b.building = nil + return false, fmt.Errorf("simulated inbox multiplexer failed to produce correct message field for msg with seqNum: %d", i) + } + } + log.Debug("Successfully checked that the batch produces correct messages when ran through inbox multiplexer", "sequenceNumber", batchPosition.NextSeqNum) + } + tx, err := b.dataPoster.PostTransaction(ctx, firstMsgTime, nonce, @@ -1437,7 +1529,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) // Setting the backlog to 0 here ensures that we don't lower compression as a result. backlog = 0 } - atomic.StoreUint64(&b.backlog, backlog) + b.backlog.Store(backlog) b.building = nil // If we aren't queueing up transactions, wait for the receipt before moving on to the next batch. @@ -1453,7 +1545,7 @@ func (b *BatchPoster) maybePostSequencerBatch(ctx context.Context) (bool, error) } func (b *BatchPoster) GetBacklogEstimate() uint64 { - return atomic.LoadUint64(&b.backlog) + return b.backlog.Load() } func (b *BatchPoster) Start(ctxIn context.Context) { diff --git a/arbnode/dataposter/data_poster.go b/arbnode/dataposter/data_poster.go index 1229d9f7a6..5630a52947 100644 --- a/arbnode/dataposter/data_poster.go +++ b/arbnode/dataposter/data_poster.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/crypto/kzg4844" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" @@ -50,6 +51,14 @@ import ( redisstorage "github.com/offchainlabs/nitro/arbnode/dataposter/redis" ) +var ( + latestFinalizedNonceGauge = metrics.NewRegisteredGauge("arb/dataposter/nonce/finalized", nil) + latestSoftConfirmedNonceGauge = metrics.NewRegisteredGauge("arb/dataposter/nonce/softconfirmed", nil) + latestUnconfirmedNonceGauge = metrics.NewRegisteredGauge("arb/dataposter/nonce/unconfirmed", nil) + totalQueueLengthGauge = metrics.NewRegisteredGauge("arb/dataposter/queue/length", nil) + totalQueueWeightGauge = metrics.NewRegisteredGauge("arb/dataposter/queue/weight", nil) +) + // Dataposter implements functionality to post transactions on the chain. It // is initialized with specified sender/signer and keeps nonce of that address // as it posts transactions. @@ -59,18 +68,16 @@ import ( // DataPoster must be RLP serializable and deserializable type DataPoster struct { stopwaiter.StopWaiter - headerReader *headerreader.HeaderReader - client arbutil.L1Interface - auth *bind.TransactOpts - signer signerFn - config ConfigFetcher - usingNoOpStorage bool - replacementTimes []time.Duration - blobTxReplacementTimes []time.Duration - metadataRetriever func(ctx context.Context, blockNum *big.Int) ([]byte, error) - extraBacklog func() uint64 - parentChainID *big.Int - parentChainID256 *uint256.Int + headerReader *headerreader.HeaderReader + client arbutil.L1Interface + auth *bind.TransactOpts + signer signerFn + config ConfigFetcher + usingNoOpStorage bool + metadataRetriever func(ctx context.Context, blockNum *big.Int) ([]byte, error) + extraBacklog func() uint64 + parentChainID *big.Int + parentChainID256 *uint256.Int // These fields are protected by the mutex. // TODO: factor out these fields into separate structure, since now one @@ -92,27 +99,6 @@ type DataPoster struct { // This can be local or external, hence the context parameter. type signerFn func(context.Context, common.Address, *types.Transaction) (*types.Transaction, error) -func parseReplacementTimes(val string) ([]time.Duration, error) { - var res []time.Duration - var lastReplacementTime time.Duration - for _, s := range strings.Split(val, ",") { - t, err := time.ParseDuration(s) - if err != nil { - return nil, fmt.Errorf("parsing durations: %w", err) - } - if t <= lastReplacementTime { - return nil, errors.New("replacement times must be increasing") - } - res = append(res, t) - lastReplacementTime = t - } - if len(res) == 0 { - log.Warn("Disabling replace-by-fee for data poster") - } - // To avoid special casing "don't replace again", replace in 10 years. - return append(res, time.Hour*24*365*10), nil -} - type DataPosterOpts struct { Database ethdb.Database HeaderReader *headerreader.HeaderReader @@ -127,14 +113,6 @@ type DataPosterOpts struct { func NewDataPoster(ctx context.Context, opts *DataPosterOpts) (*DataPoster, error) { cfg := opts.Config() - replacementTimes, err := parseReplacementTimes(cfg.ReplacementTimes) - if err != nil { - return nil, err - } - blobTxReplacementTimes, err := parseReplacementTimes(cfg.BlobTxReplacementTimes) - if err != nil { - return nil, err - } useNoOpStorage := cfg.UseNoOpStorage if opts.HeaderReader.IsParentChainArbitrum() && !cfg.UseNoOpStorage { useNoOpStorage = true @@ -178,16 +156,14 @@ func NewDataPoster(ctx context.Context, opts *DataPosterOpts) (*DataPoster, erro signer: func(_ context.Context, addr common.Address, tx *types.Transaction) (*types.Transaction, error) { return opts.Auth.Signer(addr, tx) }, - config: opts.Config, - usingNoOpStorage: useNoOpStorage, - replacementTimes: replacementTimes, - blobTxReplacementTimes: blobTxReplacementTimes, - metadataRetriever: opts.MetadataRetriever, - queue: queue, - errorCount: make(map[uint64]int), - maxFeeCapExpression: expression, - extraBacklog: opts.ExtraBacklog, - parentChainID: opts.ParentChainID, + config: opts.Config, + usingNoOpStorage: useNoOpStorage, + metadataRetriever: opts.MetadataRetriever, + queue: queue, + errorCount: make(map[uint64]int), + maxFeeCapExpression: expression, + extraBacklog: opts.ExtraBacklog, + parentChainID: opts.ParentChainID, } var overflow bool dp.parentChainID256, overflow = uint256.FromBig(opts.ParentChainID) @@ -383,6 +359,7 @@ func (p *DataPoster) canPostWithNonce(ctx context.Context, nextNonce uint64, thi if err != nil { return fmt.Errorf("getting nonce of a dataposter sender: %w", err) } + latestUnconfirmedNonceGauge.Update(int64(unconfirmedNonce)) if nextNonce >= cfg.MaxMempoolTransactions+unconfirmedNonce { return fmt.Errorf("%w: transaction nonce: %d, unconfirmed nonce: %d, max mempool size: %d", ErrExceedsMaxMempoolSize, nextNonce, unconfirmedNonce, cfg.MaxMempoolTransactions) } @@ -394,6 +371,7 @@ func (p *DataPoster) canPostWithNonce(ctx context.Context, nextNonce uint64, thi if err != nil { return fmt.Errorf("getting nonce of a dataposter sender: %w", err) } + latestUnconfirmedNonceGauge.Update(int64(unconfirmedNonce)) if unconfirmedNonce > nextNonce { return fmt.Errorf("latest on-chain nonce %v is greater than to next nonce %v", unconfirmedNonce, nextNonce) } @@ -547,6 +525,7 @@ func (p *DataPoster) feeAndTipCaps(ctx context.Context, nonce uint64, gasLimit u if err != nil { return nil, nil, nil, fmt.Errorf("failed to get latest nonce %v blocks ago (block %v): %w", config.NonceRbfSoftConfs, softConfBlock, err) } + latestSoftConfirmedNonceGauge.Update(int64(softConfNonce)) suggestedTip, err := p.client.SuggestGasTipCap(ctx) if err != nil { @@ -769,9 +748,9 @@ func (p *DataPoster) PostTransaction(ctx context.Context, dataCreatedAt time.Tim var deprecatedData types.DynamicFeeTx var inner types.TxData - replacementTimes := p.replacementTimes + replacementTimes := p.config().ReplacementTimes if len(kzgBlobs) > 0 { - replacementTimes = p.blobTxReplacementTimes + replacementTimes = p.config().BlobTxReplacementTimes value256, overflow := uint256.FromBig(value) if overflow { return nil, fmt.Errorf("blob transaction callvalue %v overflows uint256", value) @@ -1014,9 +993,9 @@ func (p *DataPoster) replaceTx(ctx context.Context, prevTx *storage.QueuedTransa return p.sendTx(ctx, prevTx, &newTx) } - replacementTimes := p.replacementTimes + replacementTimes := p.config().ReplacementTimes if len(prevTx.FullTx.BlobHashes()) > 0 { - replacementTimes = p.blobTxReplacementTimes + replacementTimes = p.config().BlobTxReplacementTimes } elapsed := time.Since(prevTx.Created) @@ -1073,6 +1052,7 @@ func (p *DataPoster) updateNonce(ctx context.Context) error { } return nil } + latestFinalizedNonceGauge.Update(int64(nonce)) log.Info("Data poster transactions confirmed", "previousNonce", p.nonce, "newNonce", nonce, "previousL1Block", p.lastBlock, "newL1Block", header.Number) if len(p.errorCount) > 0 { for x := p.nonce; x < nonce; x++ { @@ -1142,7 +1122,7 @@ func (p *DataPoster) Start(ctxIn context.Context) { log.Warn("failed to update tx poster nonce", "err", err) } now := time.Now() - nextCheck := now.Add(arbmath.MinInt(p.replacementTimes[0], p.blobTxReplacementTimes[0])) + nextCheck := now.Add(arbmath.MinInt(p.config().ReplacementTimes[0], p.config().BlobTxReplacementTimes[0])) maxTxsToRbf := p.config().MaxMempoolTransactions if maxTxsToRbf == 0 { maxTxsToRbf = 512 @@ -1152,6 +1132,7 @@ func (p *DataPoster) Start(ctxIn context.Context) { log.Warn("Failed to get latest nonce", "err", err) return minWait } + latestUnconfirmedNonceGauge.Update(int64(unconfirmedNonce)) // We use unconfirmedNonce here to replace-by-fee transactions that aren't in a block, // excluding those that are in an unconfirmed block. If a reorg occurs, we'll continue // replacing them by fee. @@ -1162,43 +1143,47 @@ func (p *DataPoster) Start(ctxIn context.Context) { } latestQueued, err := p.queue.FetchLast(ctx) if err != nil { - log.Error("Failed to fetch lastest queued tx", "err", err) + log.Error("Failed to fetch last queued tx", "err", err) return minWait } var latestCumulativeWeight, latestNonce uint64 if latestQueued != nil { latestCumulativeWeight = latestQueued.CumulativeWeight() latestNonce = latestQueued.FullTx.Nonce() + + confirmedNonce := unconfirmedNonce - 1 + confirmedMeta, err := p.queue.Get(ctx, confirmedNonce) + if err == nil && confirmedMeta != nil { + totalQueueWeightGauge.Update(int64(arbmath.SaturatingUSub(latestCumulativeWeight, confirmedMeta.CumulativeWeight()))) + totalQueueLengthGauge.Update(int64(arbmath.SaturatingUSub(latestNonce, confirmedNonce))) + } else { + log.Error("Failed to fetch latest confirmed tx from queue", "confirmedNonce", confirmedNonce, "err", err, "confirmedMeta", confirmedMeta) + } + } + for _, tx := range queueContents { - previouslyUnsent := !tx.Sent - sendAttempted := false if now.After(tx.NextReplacement) { - nonceBacklog := arbmath.SaturatingUSub(latestNonce, tx.FullTx.Nonce()) weightBacklog := arbmath.SaturatingUSub(latestCumulativeWeight, tx.CumulativeWeight()) + nonceBacklog := arbmath.SaturatingUSub(latestNonce, tx.FullTx.Nonce()) err := p.replaceTx(ctx, tx, arbmath.MaxInt(nonceBacklog, weightBacklog)) - sendAttempted = true p.maybeLogError(err, tx, "failed to replace-by-fee transaction") + } else { + err := p.sendTx(ctx, tx, tx) + p.maybeLogError(err, tx, "failed to re-send transaction") + } + tx, err = p.queue.Get(ctx, tx.FullTx.Nonce()) + if err != nil { + log.Error("Failed to fetch tx from queue to check updated status", "nonce", tx.FullTx.Nonce(), "err", err) + return minWait } if nextCheck.After(tx.NextReplacement) { nextCheck = tx.NextReplacement } - 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) - if nextCheck.After(nextSend) { - nextCheck = nextSend - } - } - } - 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 + if !tx.Sent { + // We can't progress any further if we failed to send this tx + // Retry sending this tx soon + return minWait } } wait := time.Until(nextCheck) @@ -1235,8 +1220,8 @@ type QueueStorage interface { type DataPosterConfig struct { RedisSigner signature.SimpleHmacConfig `koanf:"redis-signer"` - ReplacementTimes string `koanf:"replacement-times"` - BlobTxReplacementTimes string `koanf:"blob-tx-replacement-times"` + ReplacementTimes []time.Duration `koanf:"replacement-times"` + BlobTxReplacementTimes []time.Duration `koanf:"blob-tx-replacement-times"` // This is forcibly disabled if the parent chain is an Arbitrum chain, // so you should probably use DataPoster's waitForL1Finality method instead of reading this field directly. WaitForL1Finality bool `koanf:"wait-for-l1-finality" reload:"hot"` @@ -1297,8 +1282,8 @@ type DangerousConfig struct { type ConfigFetcher func() *DataPosterConfig func DataPosterConfigAddOptions(prefix string, f *pflag.FlagSet, defaultDataPosterConfig DataPosterConfig) { - f.String(prefix+".replacement-times", defaultDataPosterConfig.ReplacementTimes, "comma-separated list of durations since first posting to attempt a replace-by-fee") - f.String(prefix+".blob-tx-replacement-times", defaultDataPosterConfig.BlobTxReplacementTimes, "comma-separated list of durations since first posting a blob transaction to attempt a replace-by-fee") + f.DurationSlice(prefix+".replacement-times", defaultDataPosterConfig.ReplacementTimes, "comma-separated list of durations since first posting to attempt a replace-by-fee") + f.DurationSlice(prefix+".blob-tx-replacement-times", defaultDataPosterConfig.BlobTxReplacementTimes, "comma-separated list of durations since first posting a blob transaction to attempt a replace-by-fee") f.Bool(prefix+".wait-for-l1-finality", defaultDataPosterConfig.WaitForL1Finality, "only treat a transaction as confirmed after L1 finality has been achieved (recommended)") f.Uint64(prefix+".max-mempool-transactions", defaultDataPosterConfig.MaxMempoolTransactions, "the maximum number of transactions to have queued in the mempool at once (0 = unlimited)") f.Uint64(prefix+".max-mempool-weight", defaultDataPosterConfig.MaxMempoolWeight, "the maximum number of weight (weight = min(1, tx.blobs)) to have queued in the mempool at once (0 = unlimited)") @@ -1342,8 +1327,8 @@ func addExternalSignerOptions(prefix string, f *pflag.FlagSet) { } var DefaultDataPosterConfig = DataPosterConfig{ - ReplacementTimes: "5m,10m,20m,30m,1h,2h,4h,6h,8h,12h,16h,18h,20h,22h", - BlobTxReplacementTimes: "5m,10m,30m,1h,4h,8h,16h,22h", + ReplacementTimes: []time.Duration{5 * time.Minute, 10 * time.Minute, 20 * time.Minute, 30 * time.Minute, time.Hour, 2 * time.Hour, 4 * time.Hour, 6 * time.Hour, 8 * time.Hour, 12 * time.Hour, 16 * time.Hour, 18 * time.Hour, 20 * time.Hour, 22 * time.Hour}, + BlobTxReplacementTimes: []time.Duration{5 * time.Minute, 10 * time.Minute, 30 * time.Minute, time.Hour, 4 * time.Hour, 8 * time.Hour, 16 * time.Hour, 22 * time.Hour}, WaitForL1Finality: true, TargetPriceGwei: 60., UrgencyGwei: 2., @@ -1351,7 +1336,7 @@ var DefaultDataPosterConfig = DataPosterConfig{ MaxMempoolWeight: 18, MinTipCapGwei: 0.05, MinBlobTxTipCapGwei: 1, // default geth minimum, and relays aren't likely to accept lower values given propagation time - MaxTipCapGwei: 5, + MaxTipCapGwei: 1.2, MaxBlobTxTipCapGwei: 1, // lower than normal because 4844 rbf is a minimum of a 2x MaxFeeBidMultipleBips: arbmath.OneInBips * 10, NonceRbfSoftConfs: 1, @@ -1376,8 +1361,8 @@ var DefaultDataPosterConfigForValidator = func() DataPosterConfig { }() var TestDataPosterConfig = DataPosterConfig{ - ReplacementTimes: "1s,2s,5s,10s,20s,30s,1m,5m", - BlobTxReplacementTimes: "1s,10s,30s,5m", + ReplacementTimes: []time.Duration{1 * time.Second, 2 * time.Second, 5 * time.Second, 10 * time.Second, 20 * time.Second, 30 * time.Second, time.Minute, 5 * time.Minute}, + BlobTxReplacementTimes: []time.Duration{1 * time.Second, 10 * time.Second, 30 * time.Second, 5 * time.Minute}, RedisSigner: signature.TestSimpleHmacConfig, WaitForL1Finality: false, TargetPriceGwei: 60., diff --git a/arbnode/dataposter/dataposter_test.go b/arbnode/dataposter/dataposter_test.go index f840d8c84e..7f2f61c07e 100644 --- a/arbnode/dataposter/dataposter_test.go +++ b/arbnode/dataposter/dataposter_test.go @@ -22,41 +22,6 @@ import ( "github.com/offchainlabs/nitro/util/arbmath" ) -func TestParseReplacementTimes(t *testing.T) { - for _, tc := range []struct { - desc, replacementTimes string - want []time.Duration - wantErr bool - }{ - { - desc: "valid case", - replacementTimes: "1s,2s,1m,5m", - want: []time.Duration{ - time.Duration(time.Second), - time.Duration(2 * time.Second), - time.Duration(time.Minute), - time.Duration(5 * time.Minute), - time.Duration(time.Hour * 24 * 365 * 10), - }, - }, - { - desc: "non-increasing replacement times", - replacementTimes: "1s,2s,1m,5m,1s", - wantErr: true, - }, - } { - t.Run(tc.desc, func(t *testing.T) { - got, err := parseReplacementTimes(tc.replacementTimes) - if gotErr := (err != nil); gotErr != tc.wantErr { - t.Fatalf("Got error: %t, want: %t", gotErr, tc.wantErr) - } - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("parseReplacementTimes(%s) unexpected diff:\n%s", tc.replacementTimes, diff) - } - }) - } -} - func signerTestCfg(addr common.Address, url string) (*ExternalSignerCfg, error) { cp, err := externalsignertest.CertPaths() if err != nil { @@ -212,6 +177,10 @@ func (c *stubL1Client) CallContractAtHash(ctx context.Context, msg ethereum.Call return []byte{}, nil } +func (c *stubL1Client) CodeAtHash(ctx context.Context, address common.Address, blockHash common.Hash) ([]byte, error) { + return []byte{}, nil +} + func (c *stubL1Client) ChainID(ctx context.Context) (*big.Int, error) { return nil, nil } diff --git a/arbnode/dataposter/dbstorage/storage.go b/arbnode/dataposter/dbstorage/storage.go index 2cfda5d779..97055193a6 100644 --- a/arbnode/dataposter/dbstorage/storage.go +++ b/arbnode/dataposter/dbstorage/storage.go @@ -7,15 +7,12 @@ import ( "bytes" "context" "encoding/hex" - "errors" "fmt" "strconv" - "github.com/cockroachdb/pebble" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/offchainlabs/nitro/arbnode/dataposter/storage" - "github.com/syndtr/goleveldb/leveldb" + "github.com/offchainlabs/nitro/util/dbutil" ) // Storage implements db based storage for batch poster. @@ -62,7 +59,7 @@ func (s *Storage) Get(_ context.Context, index uint64) (*storage.QueuedTransacti key := idxToKey(index) value, err := s.db.Get(key) if err != nil { - if errors.Is(err, leveldb.ErrNotFound) { + if dbutil.IsErrNotFound(err) { return nil, nil } return nil, err @@ -134,7 +131,7 @@ func (s *Storage) Prune(ctx context.Context, until uint64) error { func (s *Storage) valueAt(_ context.Context, key []byte) ([]byte, error) { val, err := s.db.Get(key) if err != nil { - if isErrNotFound(err) { + if dbutil.IsErrNotFound(err) { return s.encDec().Encode((*storage.QueuedTransaction)(nil)) } return nil, err @@ -168,10 +165,10 @@ func (s *Storage) Put(ctx context.Context, index uint64, prev, new *storage.Queu return fmt.Errorf("updating value at: %v: %w", key, err) } lastItemIdx, err := s.lastItemIdx(ctx) - if err != nil && !isErrNotFound(err) { + if err != nil && !dbutil.IsErrNotFound(err) { return err } - if isErrNotFound(err) { + if dbutil.IsErrNotFound(err) { lastItemIdx = []byte{} } if cnt == 0 || bytes.Compare(key, lastItemIdx) > 0 { @@ -188,7 +185,7 @@ func (s *Storage) Put(ctx context.Context, index uint64, prev, new *storage.Queu func (s *Storage) Length(context.Context) (int, error) { val, err := s.db.Get(countKey) if err != nil { - if isErrNotFound(err) { + if dbutil.IsErrNotFound(err) { return 0, nil } return 0, err @@ -199,7 +196,3 @@ func (s *Storage) Length(context.Context) (int, error) { func (s *Storage) IsPersistent() bool { return true } - -func isErrNotFound(err error) bool { - return errors.Is(err, leveldb.ErrNotFound) || errors.Is(err, pebble.ErrNotFound) || errors.Is(err, memorydb.ErrMemorydbNotFound) -} diff --git a/arbnode/delayed_sequencer.go b/arbnode/delayed_sequencer.go index 8cbb094c16..4f18531a76 100644 --- a/arbnode/delayed_sequencer.go +++ b/arbnode/delayed_sequencer.go @@ -26,6 +26,7 @@ type DelayedSequencer struct { l1Reader *headerreader.HeaderReader bridge *DelayedBridge inbox *InboxTracker + reader *InboxReader exec execution.ExecutionSequencer coordinator *SeqCoordinator waitingForFinalizedBlock uint64 @@ -68,6 +69,7 @@ func NewDelayedSequencer(l1Reader *headerreader.HeaderReader, reader *InboxReade l1Reader: l1Reader, bridge: reader.DelayedBridge(), inbox: reader.Tracker(), + reader: reader, coordinator: coordinator, exec: exec, config: config, @@ -144,7 +146,7 @@ func (d *DelayedSequencer) sequenceWithoutLockout(ctx context.Context, lastBlock var lastDelayedAcc common.Hash var messages []*arbostypes.L1IncomingMessage for pos < dbDelayedCount { - msg, acc, parentChainBlockNumber, err := d.inbox.GetDelayedMessageAccumulatorAndParentChainBlockNumber(pos) + msg, acc, parentChainBlockNumber, err := d.inbox.GetDelayedMessageAccumulatorAndParentChainBlockNumber(ctx, pos) if err != nil { return err } @@ -165,6 +167,13 @@ func (d *DelayedSequencer) sequenceWithoutLockout(ctx context.Context, lastBlock } } lastDelayedAcc = acc + err = msg.FillInBatchGasCost(func(batchNum uint64) ([]byte, error) { + data, _, err := d.reader.GetSequencerMessageBytes(ctx, batchNum) + return data, err + }) + if err != nil { + return err + } messages = append(messages, msg) pos++ } diff --git a/arbnode/inbox_reader.go b/arbnode/inbox_reader.go index 3ba9aa78f3..77a0b6e7a2 100644 --- a/arbnode/inbox_reader.go +++ b/arbnode/inbox_reader.go @@ -97,8 +97,8 @@ type InboxReader struct { l1Reader *headerreader.HeaderReader // Atomic - lastSeenBatchCount uint64 - lastReadBatchCount uint64 + lastSeenBatchCount atomic.Uint64 + lastReadBatchCount atomic.Uint64 } func NewInboxReader(tracker *InboxTracker, client arbutil.L1Interface, l1Reader *headerreader.HeaderReader, firstMessageBlock *big.Int, delayedBridge *DelayedBridge, sequencerInbox *SequencerInbox, config InboxReaderConfigFetcher) (*InboxReader, error) { @@ -143,7 +143,11 @@ func (r *InboxReader) Start(ctxIn context.Context) error { break } // Validate the init message matches our L2 blockchain - message, err := r.tracker.GetDelayedMessage(0) + ctx, err := r.StopWaiter.GetContextSafe() + if err != nil { + return err + } + message, err := r.tracker.GetDelayedMessage(ctx, 0) if err != nil { return err } @@ -226,7 +230,7 @@ func (r *InboxReader) CaughtUp() chan struct{} { func (r *InboxReader) run(ctx context.Context, hadError bool) error { readMode := r.config().ReadMode - from, err := r.getNextBlockToRead() + from, err := r.getNextBlockToRead(ctx) if err != nil { return err } @@ -240,7 +244,7 @@ func (r *InboxReader) run(ctx context.Context, hadError bool) error { seenBatchCountStored := uint64(math.MaxUint64) storeSeenBatchCount := func() { if seenBatchCountStored != seenBatchCount { - atomic.StoreUint64(&r.lastSeenBatchCount, seenBatchCount) + r.lastSeenBatchCount.Store(seenBatchCount) seenBatchCountStored = seenBatchCount } } @@ -394,7 +398,7 @@ func (r *InboxReader) run(ctx context.Context, hadError bool) error { // There's nothing to do from = arbmath.BigAddByUint(currentHeight, 1) blocksToFetch = config.DefaultBlocksToRead - atomic.StoreUint64(&r.lastReadBatchCount, checkingBatchCount) + r.lastReadBatchCount.Store(checkingBatchCount) storeSeenBatchCount() if !r.caughtUp && readMode == "latest" { r.caughtUp = true @@ -526,7 +530,7 @@ func (r *InboxReader) run(ctx context.Context, hadError bool) error { } if len(sequencerBatches) > 0 { readAnyBatches = true - atomic.StoreUint64(&r.lastReadBatchCount, sequencerBatches[len(sequencerBatches)-1].SequenceNumber+1) + r.lastReadBatchCount.Store(sequencerBatches[len(sequencerBatches)-1].SequenceNumber + 1) storeSeenBatchCount() } } @@ -553,7 +557,7 @@ func (r *InboxReader) run(ctx context.Context, hadError bool) error { } if !readAnyBatches { - atomic.StoreUint64(&r.lastReadBatchCount, checkingBatchCount) + r.lastReadBatchCount.Store(checkingBatchCount) storeSeenBatchCount() } } @@ -584,7 +588,7 @@ func (r *InboxReader) getPrevBlockForReorg(from *big.Int) (*big.Int, error) { return newFrom, nil } -func (r *InboxReader) getNextBlockToRead() (*big.Int, error) { +func (r *InboxReader) getNextBlockToRead(ctx context.Context) (*big.Int, error) { delayedCount, err := r.tracker.GetDelayedCount() if err != nil { return nil, err @@ -592,7 +596,7 @@ func (r *InboxReader) getNextBlockToRead() (*big.Int, error) { if delayedCount == 0 { return new(big.Int).Set(r.firstMessageBlock), nil } - _, _, parentChainBlockNumber, err := r.tracker.GetDelayedMessageAccumulatorAndParentChainBlockNumber(delayedCount - 1) + _, _, parentChainBlockNumber, err := r.tracker.GetDelayedMessageAccumulatorAndParentChainBlockNumber(ctx, delayedCount-1) if err != nil { return nil, err } @@ -625,7 +629,7 @@ func (r *InboxReader) GetSequencerMessageBytes(ctx context.Context, seqNum uint6 } func (r *InboxReader) GetLastReadBatchCount() uint64 { - return atomic.LoadUint64(&r.lastReadBatchCount) + return r.lastReadBatchCount.Load() } // GetLastSeenBatchCount returns how many sequencer batches the inbox reader has read in from L1. @@ -633,7 +637,7 @@ func (r *InboxReader) GetLastReadBatchCount() uint64 { // >0 - last batchcount seen in run() - only written after lastReadBatchCount updated // 0 - no batch seen, error func (r *InboxReader) GetLastSeenBatchCount() uint64 { - return atomic.LoadUint64(&r.lastSeenBatchCount) + return r.lastSeenBatchCount.Load() } func (r *InboxReader) GetDelayBlocks() uint64 { diff --git a/arbnode/inbox_test.go b/arbnode/inbox_test.go index 3cc9c3dacb..cc8bebab5c 100644 --- a/arbnode/inbox_test.go +++ b/arbnode/inbox_test.go @@ -19,6 +19,7 @@ import ( "github.com/offchainlabs/nitro/util/arbmath" "github.com/offchainlabs/nitro/util/testhelpers" + "github.com/offchainlabs/nitro/util/testhelpers/env" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" @@ -59,7 +60,8 @@ 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, nil) + cacheConfig := core.DefaultCacheConfigWithScheme(env.GetTestStateScheme()) + bc, err := gethexec.WriteOrTestBlockChain(chainDb, cacheConfig, initReader, chainConfig, arbostypes.TestInitMessage, gethexec.ConfigDefault.TxLookupLimit, 0, nil) if err != nil { Fail(t, err) @@ -70,7 +72,9 @@ func NewTransactionStreamerForTest(t *testing.T, ownerAddress common.Address) (* if err != nil { Fail(t, err) } - execEngine.Initialize(gethexec.DefaultCachingConfig.StylusLRUCache) + if err := execEngine.Initialize(gethexec.DefaultCachingConfig.StylusLRUCache, &gethexec.DefaultStylusTargetConfig); err != nil { + Fail(t, err) + } execSeq := &execClientWrapper{execEngine, t} inbox, err := NewTransactionStreamer(arbDb, bc.Config(), execSeq, nil, make(chan error, 1), transactionStreamerConfigFetcher, &DefaultSnapSyncConfig) if err != nil { diff --git a/arbnode/inbox_tracker.go b/arbnode/inbox_tracker.go index b950c1e1ef..23b81bde62 100644 --- a/arbnode/inbox_tracker.go +++ b/arbnode/inbox_tracker.go @@ -300,7 +300,7 @@ func (t *InboxTracker) PopulateFeedBacklog(broadcastServer *broadcaster.Broadcas return fmt.Errorf("error getting message %v: %w", seqNum, err) } - msgResult, err := t.txStreamer.ResultAtCount(seqNum) + msgResult, err := t.txStreamer.ResultAtCount(seqNum + 1) var blockHash *common.Hash if err == nil { blockHash = &msgResult.BlockHash @@ -316,7 +316,7 @@ func (t *InboxTracker) PopulateFeedBacklog(broadcastServer *broadcaster.Broadcas return nil } -func (t *InboxTracker) legacyGetDelayedMessageAndAccumulator(seqNum uint64) (*arbostypes.L1IncomingMessage, common.Hash, error) { +func (t *InboxTracker) legacyGetDelayedMessageAndAccumulator(ctx context.Context, seqNum uint64) (*arbostypes.L1IncomingMessage, common.Hash, error) { key := dbKey(legacyDelayedMessagePrefix, seqNum) data, err := t.db.Get(key) if err != nil { @@ -328,17 +328,26 @@ func (t *InboxTracker) legacyGetDelayedMessageAndAccumulator(seqNum uint64) (*ar var acc common.Hash copy(acc[:], data[:32]) msg, err := arbostypes.ParseIncomingL1Message(bytes.NewReader(data[32:]), nil) + if err != nil { + return nil, common.Hash{}, err + } + + err = msg.FillInBatchGasCost(func(batchNum uint64) ([]byte, error) { + data, _, err := t.txStreamer.inboxReader.GetSequencerMessageBytes(ctx, batchNum) + return data, err + }) + return msg, acc, err } -func (t *InboxTracker) GetDelayedMessageAccumulatorAndParentChainBlockNumber(seqNum uint64) (*arbostypes.L1IncomingMessage, common.Hash, uint64, error) { +func (t *InboxTracker) GetDelayedMessageAccumulatorAndParentChainBlockNumber(ctx context.Context, seqNum uint64) (*arbostypes.L1IncomingMessage, common.Hash, uint64, error) { delayedMessageKey := dbKey(rlpDelayedMessagePrefix, seqNum) exists, err := t.db.Has(delayedMessageKey) if err != nil { return nil, common.Hash{}, 0, err } if !exists { - msg, acc, err := t.legacyGetDelayedMessageAndAccumulator(seqNum) + msg, acc, err := t.legacyGetDelayedMessageAndAccumulator(ctx, seqNum) return msg, acc, 0, err } data, err := t.db.Get(delayedMessageKey) @@ -356,6 +365,14 @@ func (t *InboxTracker) GetDelayedMessageAccumulatorAndParentChainBlockNumber(seq return msg, acc, 0, err } + err = msg.FillInBatchGasCost(func(batchNum uint64) ([]byte, error) { + data, _, err := t.txStreamer.inboxReader.GetSequencerMessageBytes(ctx, batchNum) + return data, err + }) + if err != nil { + return msg, acc, 0, err + } + parentChainBlockNumberKey := dbKey(parentChainBlockNumberPrefix, seqNum) exists, err = t.db.Has(parentChainBlockNumberKey) if err != nil { @@ -373,13 +390,13 @@ func (t *InboxTracker) GetDelayedMessageAccumulatorAndParentChainBlockNumber(seq } -func (t *InboxTracker) GetDelayedMessage(seqNum uint64) (*arbostypes.L1IncomingMessage, error) { - msg, _, _, err := t.GetDelayedMessageAccumulatorAndParentChainBlockNumber(seqNum) +func (t *InboxTracker) GetDelayedMessage(ctx context.Context, seqNum uint64) (*arbostypes.L1IncomingMessage, error) { + msg, _, _, err := t.GetDelayedMessageAccumulatorAndParentChainBlockNumber(ctx, seqNum) return msg, err } -func (t *InboxTracker) GetDelayedMessageBytes(seqNum uint64) ([]byte, error) { - msg, err := t.GetDelayedMessage(seqNum) +func (t *InboxTracker) GetDelayedMessageBytes(ctx context.Context, seqNum uint64) ([]byte, error) { + msg, err := t.GetDelayedMessage(ctx, seqNum) if err != nil { return nil, err } @@ -617,7 +634,7 @@ func (b *multiplexerBackend) ReadDelayedInbox(seqNum uint64) (*arbostypes.L1Inco if len(b.batches) == 0 || seqNum >= b.batches[0].AfterDelayedCount { return nil, errors.New("attempted to read past end of sequencer batch delayed messages") } - return b.inbox.GetDelayedMessage(seqNum) + return b.inbox.GetDelayedMessage(b.ctx, seqNum) } var delayedMessagesMismatch = errors.New("sequencer batch delayed messages missing or different") diff --git a/arbnode/message_pruner.go b/arbnode/message_pruner.go index 5d18341a27..e1bc72632b 100644 --- a/arbnode/message_pruner.go +++ b/arbnode/message_pruner.go @@ -30,6 +30,7 @@ type MessagePruner struct { lastPruneDone time.Time cachedPrunedMessages uint64 cachedPrunedBlockHashesInputFeed uint64 + cachedPrunedMessageResult uint64 cachedPrunedDelayedMessages uint64 } @@ -116,7 +117,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, blockHashInputFeedPrefix, &m.cachedPrunedBlockHashesInputFeed, uint64(messageCount)) + prunedKeysRange, err := deleteFromLastPrunedUptoEndKey(ctx, m.transactionStreamer.db, messageResultPrefix, &m.cachedPrunedMessageResult, uint64(messageCount)) + if err != nil { + return fmt.Errorf("error deleting message results: %w", err) + } + if len(prunedKeysRange) > 0 { + log.Info("Pruned message results:", "first pruned key", prunedKeysRange[0], "last pruned key", prunedKeysRange[len(prunedKeysRange)-1]) + } + + 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) } diff --git a/arbnode/message_pruner_test.go b/arbnode/message_pruner_test.go index ed85c0ebce..e64bb4f838 100644 --- a/arbnode/message_pruner_test.go +++ b/arbnode/message_pruner_test.go @@ -23,6 +23,7 @@ func TestMessagePrunerWithPruningEligibleMessagePresent(t *testing.T) { checkDbKeys(t, messagesCount, transactionStreamerDb, messagePrefix) checkDbKeys(t, messagesCount, transactionStreamerDb, blockHashInputFeedPrefix) + checkDbKeys(t, messagesCount, transactionStreamerDb, messageResultPrefix) checkDbKeys(t, messagesCount, inboxTrackerDb, rlpDelayedMessagePrefix) } @@ -72,8 +73,8 @@ func TestMessagePrunerWithNoPruningEligibleMessagePresent(t *testing.T) { checkDbKeys(t, uint64(messagesCount), transactionStreamerDb, messagePrefix) checkDbKeys(t, uint64(messagesCount), transactionStreamerDb, blockHashInputFeedPrefix) + checkDbKeys(t, uint64(messagesCount), transactionStreamerDb, messageResultPrefix) checkDbKeys(t, messagesCount, inboxTrackerDb, rlpDelayedMessagePrefix) - } func setupDatabase(t *testing.T, messageCount, delayedMessageCount uint64) (ethdb.Database, ethdb.Database, *MessagePruner) { @@ -83,6 +84,8 @@ func setupDatabase(t *testing.T, messageCount, delayedMessageCount uint64) (ethd Require(t, err) err = transactionStreamerDb.Put(dbKey(blockHashInputFeedPrefix, i), []byte{}) Require(t, err) + err = transactionStreamerDb.Put(dbKey(messageResultPrefix, i), []byte{}) + Require(t, err) } inboxTrackerDb := rawdb.NewMemoryDatabase() diff --git a/arbnode/node.go b/arbnode/node.go index 51826a6370..d9013cab52 100644 --- a/arbnode/node.go +++ b/arbnode/node.go @@ -663,7 +663,7 @@ func createNodeImpl( confirmedNotifiers = append(confirmedNotifiers, messagePruner) } - stakerObj, err = staker.NewStaker(l1Reader, wallet, bind.CallOpts{}, config.Staker, blockValidator, statelessBlockValidator, nil, confirmedNotifiers, deployInfo.ValidatorUtils, fatalErrChan) + stakerObj, err = staker.NewStaker(l1Reader, wallet, bind.CallOpts{}, func() *staker.L1ValidatorConfig { return &configFetcher.Get().Staker }, blockValidator, statelessBlockValidator, nil, confirmedNotifiers, deployInfo.ValidatorUtils, fatalErrChan) if err != nil { return nil, err } @@ -702,6 +702,7 @@ func createNodeImpl( TransactOpts: txOptsBatchPoster, DAPWriter: dapWriter, ParentChainID: parentChainID, + DAPReaders: dapReaders, }) if err != nil { return nil, err @@ -782,7 +783,7 @@ func CreateNode( } if currentNode.StatelessBlockValidator != nil { apis = append(apis, rpc.API{ - Namespace: "arbvalidator", + Namespace: "arbdebug", Version: "1.0", Service: &BlockValidatorDebugAPI{ val: currentNode.StatelessBlockValidator, @@ -993,10 +994,6 @@ func (n *Node) StopAndWait() { } } -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) } diff --git a/arbnode/redislock/redis.go b/arbnode/redislock/redis.go index 09296e3c79..7e26010cae 100644 --- a/arbnode/redislock/redis.go +++ b/arbnode/redislock/redis.go @@ -21,7 +21,7 @@ type Simple struct { stopwaiter.StopWaiter client redis.UniversalClient config SimpleCfgFetcher - lockedUntil int64 + lockedUntil atomic.Int64 mutex sync.Mutex stopping bool readyToLock func() bool @@ -239,12 +239,12 @@ func execTestPipe(pipe redis.Pipeliner, ctx context.Context) error { } // notice: It is possible for two consecutive reads to get decreasing values. That shouldn't matter. -func atomicTimeRead(addr *int64) time.Time { - asint64 := atomic.LoadInt64(addr) +func atomicTimeRead(addr *atomic.Int64) time.Time { + asint64 := addr.Load() return time.UnixMilli(asint64) } -func atomicTimeWrite(addr *int64, t time.Time) { +func atomicTimeWrite(addr *atomic.Int64, t time.Time) { asint64 := t.UnixMilli() - atomic.StoreInt64(addr, asint64) + addr.Store(asint64) } diff --git a/arbnode/schema.go b/arbnode/schema.go index 2854b7e785..1aaded2b95 100644 --- a/arbnode/schema.go +++ b/arbnode/schema.go @@ -6,6 +6,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 + messageResultPrefix []byte = []byte("r") // maps a message sequence number to a message result 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/seq_coordinator.go b/arbnode/seq_coordinator.go index cdf1011b11..a582b64ffa 100644 --- a/arbnode/seq_coordinator.go +++ b/arbnode/seq_coordinator.go @@ -39,6 +39,7 @@ type SeqCoordinator struct { redisutil.RedisCoordinator + sync *SyncMonitor streamer *TransactionStreamer sequencer execution.ExecutionSequencer delayedSequencer *DelayedSequencer @@ -48,7 +49,7 @@ type SeqCoordinator struct { prevChosenSequencer string reportedWantsLockout bool - lockoutUntil int64 // atomic + lockoutUntil atomic.Int64 // atomic wantsLockoutMutex sync.Mutex // manages access to acquireLockoutAndWriteMessage and generally the wants lockout key avoidLockout int // If > 0, prevents acquiring the lockout but not extending the lockout if no alternative sequencer wants the lockout. Protected by chosenUpdateMutex. @@ -69,9 +70,10 @@ type SeqCoordinatorConfig struct { SafeShutdownDelay time.Duration `koanf:"safe-shutdown-delay"` ReleaseRetries int `koanf:"release-retries"` // Max message per poll. - MsgPerPoll arbutil.MessageIndex `koanf:"msg-per-poll"` - MyUrl string `koanf:"my-url"` - Signer signature.SignVerifyConfig `koanf:"signer"` + MsgPerPoll arbutil.MessageIndex `koanf:"msg-per-poll"` + MyUrl string `koanf:"my-url"` + DeleteFinalizedMsgs bool `koanf:"delete-finalized-msgs"` + Signer signature.SignVerifyConfig `koanf:"signer"` } func (c *SeqCoordinatorConfig) Url() string { @@ -95,6 +97,7 @@ func SeqCoordinatorConfigAddOptions(prefix string, f *flag.FlagSet) { f.Int(prefix+".release-retries", DefaultSeqCoordinatorConfig.ReleaseRetries, "the number of times to retry releasing the wants lockout and chosen one status on shutdown") f.Uint64(prefix+".msg-per-poll", uint64(DefaultSeqCoordinatorConfig.MsgPerPoll), "will only be marked as wanting the lockout if not too far behind") f.String(prefix+".my-url", DefaultSeqCoordinatorConfig.MyUrl, "url for this sequencer if it is the chosen") + f.Bool(prefix+".delete-finalized-msgs", DefaultSeqCoordinatorConfig.DeleteFinalizedMsgs, "enable deleting of finalized messages from redis") signature.SignVerifyConfigAddOptions(prefix+".signer", f) } @@ -104,7 +107,7 @@ var DefaultSeqCoordinatorConfig = SeqCoordinatorConfig{ RedisUrl: "", LockoutDuration: time.Minute, LockoutSpare: 30 * time.Second, - SeqNumDuration: 24 * time.Hour, + SeqNumDuration: 10 * 24 * time.Hour, UpdateInterval: 250 * time.Millisecond, HandoffTimeout: 30 * time.Second, SafeShutdownDelay: 5 * time.Second, @@ -112,23 +115,25 @@ var DefaultSeqCoordinatorConfig = SeqCoordinatorConfig{ RetryInterval: 50 * time.Millisecond, MsgPerPoll: 2000, MyUrl: redisutil.INVALID_URL, + DeleteFinalizedMsgs: true, Signer: signature.DefaultSignVerifyConfig, } var TestSeqCoordinatorConfig = SeqCoordinatorConfig{ - Enable: false, - RedisUrl: "", - LockoutDuration: time.Second * 2, - LockoutSpare: time.Millisecond * 10, - SeqNumDuration: time.Minute * 10, - UpdateInterval: time.Millisecond * 10, - HandoffTimeout: time.Millisecond * 200, - SafeShutdownDelay: time.Millisecond * 100, - ReleaseRetries: 4, - RetryInterval: time.Millisecond * 3, - MsgPerPoll: 20, - MyUrl: redisutil.INVALID_URL, - Signer: signature.DefaultSignVerifyConfig, + Enable: false, + RedisUrl: "", + LockoutDuration: time.Second * 2, + LockoutSpare: time.Millisecond * 10, + SeqNumDuration: time.Minute * 10, + UpdateInterval: time.Millisecond * 10, + HandoffTimeout: time.Millisecond * 200, + SafeShutdownDelay: time.Millisecond * 100, + ReleaseRetries: 4, + RetryInterval: time.Millisecond * 3, + MsgPerPoll: 20, + MyUrl: redisutil.INVALID_URL, + DeleteFinalizedMsgs: true, + Signer: signature.DefaultSignVerifyConfig, } func NewSeqCoordinator( @@ -149,6 +154,7 @@ func NewSeqCoordinator( } coordinator := &SeqCoordinator{ RedisCoordinator: *redisCoordinator, + sync: sync, streamer: streamer, sequencer: sequencer, config: config, @@ -191,14 +197,14 @@ func StandaloneSeqCoordinatorInvalidateMsgIndex(ctx context.Context, redisClient return nil } -func atomicTimeWrite(addr *int64, t time.Time) { +func atomicTimeWrite(addr *atomic.Int64, t time.Time) { asint64 := t.UnixMilli() - atomic.StoreInt64(addr, asint64) + addr.Store(asint64) } // notice: It is possible for two consecutive reads to get decreasing values. That shouldn't matter. -func atomicTimeRead(addr *int64) time.Time { - asint64 := atomic.LoadInt64(addr) +func atomicTimeRead(addr *atomic.Int64) time.Time { + asint64 := addr.Load() return time.UnixMilli(asint64) } @@ -338,6 +344,14 @@ func (c *SeqCoordinator) acquireLockoutAndWriteMessage(ctx context.Context, msgC return nil } +func (c *SeqCoordinator) getRemoteFinalizedMsgCount(ctx context.Context) (arbutil.MessageIndex, error) { + resStr, err := c.Client.Get(ctx, redisutil.FINALIZED_MSG_COUNT_KEY).Result() + if err != nil { + return 0, err + } + return c.signedBytesToMsgCount(ctx, []byte(resStr)) +} + func (c *SeqCoordinator) getRemoteMsgCountImpl(ctx context.Context, r redis.Cmdable) (arbutil.MessageIndex, error) { resStr, err := r.Get(ctx, redisutil.MSG_COUNT_KEY).Result() if errors.Is(err, redis.Nil) { @@ -473,6 +487,17 @@ func (c *SeqCoordinator) updateWithLockout(ctx context.Context, nextChosen strin return c.noRedisError() } // Was, and still is, the active sequencer + if c.config.DeleteFinalizedMsgs { + // Before proceeding, first try deleting finalized messages from redis and setting the finalizedMsgCount key + finalized, err := c.sync.GetFinalizedMsgCount(ctx) + if err != nil { + log.Warn("Error getting finalizedMessageCount from syncMonitor: %w", err) + } else if finalized == 0 { + log.Warn("SyncMonitor returned zero finalizedMessageCount") + } else if err := c.deleteFinalizedMsgsFromRedis(ctx, finalized); err != nil { + log.Warn("Coordinator failed to delete finalized messages from redis", "err", err) + } + } // We leave a margin of error of either a five times the update interval or a fifth of the lockout duration, whichever is greater. marginOfError := arbmath.MaxInt(c.config.LockoutDuration/5, c.config.UpdateInterval*5) if time.Now().Add(marginOfError).Before(atomicTimeRead(&c.lockoutUntil)) { @@ -492,6 +517,62 @@ func (c *SeqCoordinator) updateWithLockout(ctx context.Context, nextChosen strin return c.noRedisError() } +func (c *SeqCoordinator) deleteFinalizedMsgsFromRedis(ctx context.Context, finalized arbutil.MessageIndex) error { + deleteMsgsAndUpdateFinalizedMsgCount := func(keys []string) error { + if len(keys) > 0 { + // To support cases during init we delete keys from reverse (i.e lowest seq num first), so that even if deletion fails in one of the iterations + // next time deleteFinalizedMsgsFromRedis is called we dont miss undeleted messages, as exists is checked from higher seqnum to lower. + // In non-init cases it doesn't matter how we delete as we always try to delete from prevFinalized to finalized + batchDeleteCount := 1000 + for i := len(keys); i > 0; i -= batchDeleteCount { + if err := c.Client.Del(ctx, keys[max(0, i-batchDeleteCount):i]...).Err(); err != nil { + return fmt.Errorf("error deleting finalized messages and their signatures from redis: %w", err) + } + } + } + finalizedBytes, err := c.msgCountToSignedBytes(finalized) + if err != nil { + return err + } + if err = c.Client.Set(ctx, redisutil.FINALIZED_MSG_COUNT_KEY, finalizedBytes, c.config.SeqNumDuration).Err(); err != nil { + return fmt.Errorf("couldn't set %s key to current finalizedMsgCount in redis: %w", redisutil.FINALIZED_MSG_COUNT_KEY, err) + } + return nil + } + prevFinalized, err := c.getRemoteFinalizedMsgCount(ctx) + if errors.Is(err, redis.Nil) { + var keys []string + for msg := finalized - 1; msg > 0; msg-- { + exists, err := c.Client.Exists(ctx, redisutil.MessageKeyFor(msg), redisutil.MessageSigKeyFor(msg)).Result() + if err != nil { + // If there is an error deleting finalized messages during init, we retry later either from this sequencer or from another + return err + } + if exists == 0 { + break + } + keys = append(keys, redisutil.MessageKeyFor(msg), redisutil.MessageSigKeyFor(msg)) + } + log.Info("Initializing finalizedMsgCount and deleting finalized messages from redis", "finalizedMsgCount", finalized) + return deleteMsgsAndUpdateFinalizedMsgCount(keys) + } else if err != nil { + return fmt.Errorf("error getting finalizedMsgCount value from redis: %w", err) + } + remoteMsgCount, err := c.getRemoteMsgCountImpl(ctx, c.Client) + if err != nil { + return fmt.Errorf("cannot get remote message count: %w", err) + } + msgToDelete := min(finalized, remoteMsgCount) + if prevFinalized < msgToDelete { + var keys []string + for msg := prevFinalized; msg < msgToDelete; msg++ { + keys = append(keys, redisutil.MessageKeyFor(msg), redisutil.MessageSigKeyFor(msg)) + } + return deleteMsgsAndUpdateFinalizedMsgCount(keys) + } + return nil +} + func (c *SeqCoordinator) update(ctx context.Context) time.Duration { chosenSeq, err := c.RecommendSequencerWantingLockout(ctx) if err != nil { @@ -522,19 +603,24 @@ func (c *SeqCoordinator) update(ctx context.Context) time.Duration { log.Error("cannot read message count", "err", err) return c.config.UpdateInterval } + remoteFinalizedMsgCount, err := c.getRemoteFinalizedMsgCount(ctx) + if err != nil { + loglevel := log.Error + if errors.Is(err, redis.Nil) { + loglevel = log.Debug + } + loglevel("Cannot get remote finalized message count, might encounter failed to read message warnings later", "err", err) + } remoteMsgCount, err := c.GetRemoteMsgCount() if err != nil { log.Warn("cannot get remote message count", "err", err) return c.retryAfterRedisError() } - readUntil := remoteMsgCount - if readUntil > localMsgCount+c.config.MsgPerPoll { - readUntil = localMsgCount + c.config.MsgPerPoll - } + readUntil := min(localMsgCount+c.config.MsgPerPoll, remoteMsgCount) var messages []arbostypes.MessageWithMetadata msgToRead := localMsgCount var msgReadErr error - for msgToRead < readUntil { + for msgToRead < readUntil && localMsgCount >= remoteFinalizedMsgCount { var resString string resString, msgReadErr = c.Client.Get(ctx, redisutil.MessageKeyFor(msgToRead)).Result() if msgReadErr != nil { @@ -692,7 +778,7 @@ func (c *SeqCoordinator) DebugPrint() string { return fmt.Sprint("Url:", c.config.Url(), " prevChosenSequencer:", c.prevChosenSequencer, " reportedWantsLockout:", c.reportedWantsLockout, - " lockoutUntil:", c.lockoutUntil, + " lockoutUntil:", c.lockoutUntil.Load(), " redisErrors:", c.redisErrors) } diff --git a/arbnode/seq_coordinator_atomic_test.go b/arbnode/seq_coordinator_test.go similarity index 53% rename from arbnode/seq_coordinator_atomic_test.go rename to arbnode/seq_coordinator_test.go index 61468a3adb..6498543f3a 100644 --- a/arbnode/seq_coordinator_atomic_test.go +++ b/arbnode/seq_coordinator_test.go @@ -21,21 +21,21 @@ import ( const messagesPerRound = 20 type CoordinatorTestData struct { - messageCount uint64 + messageCount atomic.Uint64 sequencer []string err error mutex sync.Mutex waitForCoords sync.WaitGroup - testStartRound int32 + testStartRound atomic.Int32 } func coordinatorTestThread(ctx context.Context, coord *SeqCoordinator, data *CoordinatorTestData) { nextRound := int32(0) for { sequenced := make([]bool, messagesPerRound) - for atomic.LoadInt32(&data.testStartRound) < nextRound { + for data.testStartRound.Load() < nextRound { if ctx.Err() != nil { return } @@ -44,7 +44,7 @@ func coordinatorTestThread(ctx context.Context, coord *SeqCoordinator, data *Coo nextRound++ var execError error for { - messageCount := atomic.LoadUint64(&data.messageCount) + messageCount := data.messageCount.Load() if messageCount >= messagesPerRound { break } @@ -53,7 +53,7 @@ func coordinatorTestThread(ctx context.Context, coord *SeqCoordinator, data *Coo err := coord.acquireLockoutAndWriteMessage(ctx, asIndex, asIndex+1, &arbostypes.EmptyTestMessageWithMetadata) if err == nil { sequenced[messageCount] = true - atomic.StoreUint64(&data.messageCount, messageCount+1) + data.messageCount.Store(messageCount + 1) randNr := rand.Intn(20) if randNr > 15 { execError = coord.chosenOneRelease(ctx) @@ -105,9 +105,9 @@ func TestRedisSeqCoordinatorAtomic(t *testing.T) { coordConfig.Signer.Symmetric.Dangerous.DisableSignatureVerification = true coordConfig.Signer.Symmetric.SigningKey = "" testData := CoordinatorTestData{ - testStartRound: -1, - sequencer: make([]string, messagesPerRound), + sequencer: make([]string, messagesPerRound), } + testData.testStartRound.Store(-1) nullSigner, err := signature.NewSignVerify(&coordConfig.Signer, nil, nil) Require(t, err) @@ -134,12 +134,12 @@ func TestRedisSeqCoordinatorAtomic(t *testing.T) { for round := int32(0); round < 10; round++ { redisClient.Del(ctx, redisutil.CHOSENSEQ_KEY, redisutil.MSG_COUNT_KEY) - testData.messageCount = 0 + testData.messageCount.Store(0) for i := 0; i < messagesPerRound; i++ { testData.sequencer[i] = "" } testData.waitForCoords.Add(NumOfThreads) - atomic.StoreInt32(&testData.testStartRound, round) + testData.testStartRound.Store(round) testData.waitForCoords.Wait() Require(t, testData.err) seqList := "" @@ -156,3 +156,94 @@ func TestRedisSeqCoordinatorAtomic(t *testing.T) { } } + +func TestSeqCoordinatorDeletesFinalizedMessages(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + coordConfig := TestSeqCoordinatorConfig + coordConfig.LockoutDuration = time.Millisecond * 100 + coordConfig.LockoutSpare = time.Millisecond * 10 + coordConfig.Signer.ECDSA.AcceptSequencer = false + coordConfig.Signer.SymmetricFallback = true + coordConfig.Signer.SymmetricSign = true + coordConfig.Signer.Symmetric.Dangerous.DisableSignatureVerification = true + coordConfig.Signer.Symmetric.SigningKey = "" + + nullSigner, err := signature.NewSignVerify(&coordConfig.Signer, nil, nil) + Require(t, err) + + redisUrl := redisutil.CreateTestRedis(ctx, t) + coordConfig.RedisUrl = redisUrl + + config := coordConfig + config.MyUrl = "test" + redisCoordinator, err := redisutil.NewRedisCoordinator(config.RedisUrl) + Require(t, err) + coordinator := &SeqCoordinator{ + RedisCoordinator: *redisCoordinator, + config: config, + signer: nullSigner, + } + + // Add messages to redis + var keys []string + msgBytes, err := coordinator.msgCountToSignedBytes(0) + Require(t, err) + for i := arbutil.MessageIndex(1); i <= 10; i++ { + err = coordinator.Client.Set(ctx, redisutil.MessageKeyFor(i), msgBytes, time.Hour).Err() + Require(t, err) + err = coordinator.Client.Set(ctx, redisutil.MessageSigKeyFor(i), msgBytes, time.Hour).Err() + Require(t, err) + keys = append(keys, redisutil.MessageKeyFor(i), redisutil.MessageSigKeyFor(i)) + } + // Set msgCount key + msgCountBytes, err := coordinator.msgCountToSignedBytes(11) + Require(t, err) + err = coordinator.Client.Set(ctx, redisutil.MSG_COUNT_KEY, msgCountBytes, time.Hour).Err() + Require(t, err) + exists, err := coordinator.Client.Exists(ctx, keys...).Result() + Require(t, err) + if exists != 20 { + t.Fatal("couldn't find all messages and signatures in redis") + } + + // Set finalizedMsgCount and delete finalized messages + err = coordinator.deleteFinalizedMsgsFromRedis(ctx, 5) + Require(t, err) + + // Check if messages and signatures were deleted successfully + exists, err = coordinator.Client.Exists(ctx, keys[:8]...).Result() + Require(t, err) + if exists != 0 { + t.Fatal("finalized messages and signatures in range 1 to 4 were not deleted") + } + + // Check if finalizedMsgCount was set to correct value + finalized, err := coordinator.getRemoteFinalizedMsgCount(ctx) + Require(t, err) + if finalized != 5 { + t.Fatalf("incorrect finalizedMsgCount, want: 5, have: %d", finalized) + } + + // Try deleting finalized messages when theres already a finalizedMsgCount + err = coordinator.deleteFinalizedMsgsFromRedis(ctx, 7) + Require(t, err) + exists, err = coordinator.Client.Exists(ctx, keys[8:12]...).Result() + Require(t, err) + if exists != 0 { + t.Fatal("finalized messages and signatures in range 5 to 6 were not deleted") + } + finalized, err = coordinator.getRemoteFinalizedMsgCount(ctx) + Require(t, err) + if finalized != 7 { + t.Fatalf("incorrect finalizedMsgCount, want: 7, have: %d", finalized) + } + + // Check that non-finalized messages are still available in redis + exists, err = coordinator.Client.Exists(ctx, keys[12:]...).Result() + Require(t, err) + if exists != 8 { + t.Fatal("non-finalized messages and signatures in range 7 to 10 are not fully available") + } +} diff --git a/arbnode/sequencer_inbox.go b/arbnode/sequencer_inbox.go index 46e1edb78b..73e52ded53 100644 --- a/arbnode/sequencer_inbox.go +++ b/arbnode/sequencer_inbox.go @@ -232,7 +232,7 @@ func (i *SequencerInbox) LookupBatchesInRange(ctx context.Context, from, to *big seqNum := parsedLog.BatchSequenceNumber.Uint64() if lastSeqNum != nil { if seqNum != *lastSeqNum+1 { - return nil, fmt.Errorf("sequencer batches out of order; after batch %v got batch %v", lastSeqNum, seqNum) + return nil, fmt.Errorf("sequencer batches out of order; after batch %v got batch %v", *lastSeqNum, seqNum) } } lastSeqNum = &seqNum diff --git a/arbnode/simple_redis_lock_test.go b/arbnode/simple_redis_lock_test.go index c9dd576749..72f3dfa837 100644 --- a/arbnode/simple_redis_lock_test.go +++ b/arbnode/simple_redis_lock_test.go @@ -21,11 +21,11 @@ const test_release_frac = 5 const test_delay = time.Millisecond const test_redisKey_prefix = "__TEMP_SimpleRedisLockTest__" -func attemptLock(ctx context.Context, s *redislock.Simple, flag *int32, wg *sync.WaitGroup) { +func attemptLock(ctx context.Context, s *redislock.Simple, flag *atomic.Int32, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < test_attempts; i++ { if s.AttemptLock(ctx) { - atomic.AddInt32(flag, 1) + flag.Add(1) } else if rand.Intn(test_release_frac) == 0 { s.Release(ctx) } @@ -76,17 +76,17 @@ func simpleRedisLockTest(t *testing.T, redisKeySuffix string, chosen int, backgo <-time.After(time.Second) } wg := sync.WaitGroup{} - counters := make([]int32, test_threads) + counters := make([]atomic.Int32, test_threads) for i, lock := range locks { wg.Add(1) go attemptLock(ctx, lock, &counters[i], &wg) } wg.Wait() successful := -1 - for i, counter := range counters { - if counter != 0 { - if counter != test_attempts { - t.Fatalf("counter %d value %d", i, counter) + for i := range counters { + if counters[i].Load() != 0 { + if counters[i].Load() != test_attempts { + t.Fatalf("counter %d value %d", i, counters[i].Load()) } if successful > 0 { t.Fatalf("counter %d and %d both positive", i, successful) diff --git a/arbnode/sync_monitor.go b/arbnode/sync_monitor.go index d3b9a7e1c6..5ab1ede2d6 100644 --- a/arbnode/sync_monitor.go +++ b/arbnode/sync_monitor.go @@ -72,6 +72,13 @@ func (s *SyncMonitor) SyncTargetMessageCount() arbutil.MessageIndex { return s.syncTarget } +func (s *SyncMonitor) GetFinalizedMsgCount(ctx context.Context) (arbutil.MessageIndex, error) { + if s.inboxReader != nil && s.inboxReader.l1Reader != nil { + return s.inboxReader.GetFinalizedMsgCount(ctx) + } + return 0, nil +} + func (s *SyncMonitor) maxMessageCount() (arbutil.MessageIndex, error) { msgCount, err := s.txStreamer.GetMessageCount() if err != nil { diff --git a/arbnode/transaction_streamer.go b/arbnode/transaction_streamer.go index 5c02129ee6..90e7feddc6 100644 --- a/arbnode/transaction_streamer.go +++ b/arbnode/transaction_streamer.go @@ -19,9 +19,7 @@ import ( "errors" - "github.com/cockroachdb/pebble" flag "github.com/spf13/pflag" - "github.com/syndtr/goleveldb/leveldb" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethdb" @@ -36,6 +34,7 @@ import ( "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/staker" "github.com/offchainlabs/nitro/util/arbmath" + "github.com/offchainlabs/nitro/util/dbutil" "github.com/offchainlabs/nitro/util/sharedmetrics" "github.com/offchainlabs/nitro/util/stopwaiter" ) @@ -62,7 +61,7 @@ type TransactionStreamer struct { nextAllowedFeedReorgLog time.Time broadcasterQueuedMessages []arbostypes.MessageWithMetadataAndBlockHash - broadcasterQueuedMessagesPos uint64 + broadcasterQueuedMessagesPos atomic.Uint64 broadcasterQueuedMessagesActiveReorg bool coordinator *SeqCoordinator @@ -130,7 +129,8 @@ type blockHashDBValue struct { } const ( - BlockHashMismatchLogMsg = "BlockHash from feed doesn't match locally computed hash. Check feed source." + BlockHashMismatchLogMsg = "BlockHash from feed doesn't match locally computed hash. Check feed source." + FailedToGetMsgResultFromDB = "Reading message result remotely." ) // Encodes a uint64 as bytes in a lexically sortable manner for database iteration. @@ -374,6 +374,10 @@ func (s *TransactionStreamer) reorg(batch ethdb.Batch, count arbutil.MessageInde } } + err = deleteStartingAt(s.db, batch, messageResultPrefix, uint64ToKey(uint64(count))) + if err != nil { + return err + } err = deleteStartingAt(s.db, batch, blockHashInputFeedPrefix, uint64ToKey(uint64(count))) if err != nil { return err @@ -383,6 +387,14 @@ func (s *TransactionStreamer) reorg(batch ethdb.Batch, count arbutil.MessageInde return err } + for i := 0; i < len(messagesResults); i++ { + pos := count + arbutil.MessageIndex(i) + err = s.storeResult(pos, *messagesResults[i], batch) + if err != nil { + return err + } + } + return setMessageCount(batch, count) } @@ -407,10 +419,6 @@ 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)) @@ -424,6 +432,18 @@ func (s *TransactionStreamer) GetMessage(seqNum arbutil.MessageIndex) (*arbostyp return nil, err } + err = message.Message.FillInBatchGasCost(func(batchNum uint64) ([]byte, error) { + ctx, err := s.GetContextSafe() + if err != nil { + return nil, err + } + data, _, err := s.inboxReader.GetSequencerMessageBytes(ctx, batchNum) + return data, err + }) + if err != nil { + return nil, err + } + return &message, nil } @@ -446,7 +466,7 @@ func (s *TransactionStreamer) getMessageWithMetadataAndBlockHash(seqNum arbutil. return nil, err } blockHash = blockHashDBVal.BlockHash - } else if !isErrNotFound(err) { + } else if !dbutil.IsErrNotFound(err) { return nil, err } @@ -491,14 +511,14 @@ func (s *TransactionStreamer) AddMessages(pos arbutil.MessageIndex, messagesAreC } func (s *TransactionStreamer) FeedPendingMessageCount() arbutil.MessageIndex { - pos := atomic.LoadUint64(&s.broadcasterQueuedMessagesPos) + pos := s.broadcasterQueuedMessagesPos.Load() if pos == 0 { return 0 } s.insertionMutex.Lock() defer s.insertionMutex.Unlock() - pos = atomic.LoadUint64(&s.broadcasterQueuedMessagesPos) + pos = s.broadcasterQueuedMessagesPos.Load() if pos == 0 { return 0 } @@ -552,14 +572,14 @@ func (s *TransactionStreamer) AddBroadcastMessages(feedMessages []*m.BroadcastFe if len(s.broadcasterQueuedMessages) == 0 || (feedReorg && !s.broadcasterQueuedMessagesActiveReorg) { // Empty cache or feed different from database, save current feed messages until confirmed L1 messages catch up. s.broadcasterQueuedMessages = messages - atomic.StoreUint64(&s.broadcasterQueuedMessagesPos, uint64(broadcastStartPos)) + s.broadcasterQueuedMessagesPos.Store(uint64(broadcastStartPos)) s.broadcasterQueuedMessagesActiveReorg = feedReorg } else { - broadcasterQueuedMessagesPos := arbutil.MessageIndex(atomic.LoadUint64(&s.broadcasterQueuedMessagesPos)) + broadcasterQueuedMessagesPos := arbutil.MessageIndex(s.broadcasterQueuedMessagesPos.Load()) if broadcasterQueuedMessagesPos >= broadcastStartPos { // Feed messages older than cache s.broadcasterQueuedMessages = messages - atomic.StoreUint64(&s.broadcasterQueuedMessagesPos, uint64(broadcastStartPos)) + s.broadcasterQueuedMessagesPos.Store(uint64(broadcastStartPos)) s.broadcasterQueuedMessagesActiveReorg = feedReorg } else if broadcasterQueuedMessagesPos+arbutil.MessageIndex(len(s.broadcasterQueuedMessages)) == broadcastStartPos { // Feed messages can be added directly to end of cache @@ -579,7 +599,7 @@ func (s *TransactionStreamer) AddBroadcastMessages(feedMessages []*m.BroadcastFe ) } s.broadcasterQueuedMessages = messages - atomic.StoreUint64(&s.broadcasterQueuedMessagesPos, uint64(broadcastStartPos)) + s.broadcasterQueuedMessagesPos.Store(uint64(broadcastStartPos)) s.broadcasterQueuedMessagesActiveReorg = feedReorg } } @@ -592,7 +612,7 @@ func (s *TransactionStreamer) AddBroadcastMessages(feedMessages []*m.BroadcastFe if broadcastStartPos > 0 { _, err := s.GetMessage(broadcastStartPos - 1) if err != nil { - if !isErrNotFound(err) { + if !dbutil.IsErrNotFound(err) { return err } // Message before current message doesn't exist in database, so don't add current messages yet @@ -795,7 +815,7 @@ func (s *TransactionStreamer) addMessagesAndEndBatchImpl(messageStartPos arbutil var cacheClearLen int messagesAfterPos := messageStartPos + arbutil.MessageIndex(len(messages)) - broadcastStartPos := arbutil.MessageIndex(atomic.LoadUint64(&s.broadcasterQueuedMessagesPos)) + broadcastStartPos := arbutil.MessageIndex(s.broadcasterQueuedMessagesPos.Load()) if messagesAreConfirmed { var duplicates int @@ -903,10 +923,10 @@ func (s *TransactionStreamer) addMessagesAndEndBatchImpl(messageStartPos arbutil // Check if new messages were added at the end of cache, if they were, then dont remove those particular messages if len(s.broadcasterQueuedMessages) > cacheClearLen { s.broadcasterQueuedMessages = s.broadcasterQueuedMessages[cacheClearLen:] - atomic.StoreUint64(&s.broadcasterQueuedMessagesPos, uint64(broadcastStartPos)+uint64(cacheClearLen)) + s.broadcasterQueuedMessagesPos.Store(uint64(broadcastStartPos) + uint64(cacheClearLen)) } else { s.broadcasterQueuedMessages = s.broadcasterQueuedMessages[:0] - atomic.StoreUint64(&s.broadcasterQueuedMessagesPos, 0) + s.broadcasterQueuedMessagesPos.Store(0) } s.broadcasterQueuedMessagesActiveReorg = false } @@ -1046,12 +1066,43 @@ func (s *TransactionStreamer) writeMessages(pos arbutil.MessageIndex, messages [ return nil } -// TODO: eventually there will be a table maintained by txStreamer itself func (s *TransactionStreamer) ResultAtCount(count arbutil.MessageIndex) (*execution.MessageResult, error) { if count == 0 { return &execution.MessageResult{}, nil } - return s.exec.ResultAtPos(count - 1) + pos := count - 1 + + key := dbKey(messageResultPrefix, uint64(pos)) + data, err := s.db.Get(key) + if err == nil { + var msgResult execution.MessageResult + err = rlp.DecodeBytes(data, &msgResult) + if err == nil { + return &msgResult, nil + } + } else if !dbutil.IsErrNotFound(err) { + return nil, err + } + log.Info(FailedToGetMsgResultFromDB, "count", count) + + msgResult, err := s.exec.ResultAtPos(pos) + if err != nil { + return nil, err + } + // Stores result in Consensus DB in a best-effort manner + batch := s.db.NewBatch() + err = s.storeResult(pos, *msgResult, batch) + if err != nil { + log.Warn("Failed to store result at ResultAtCount", "err", err) + return msgResult, nil + } + err = batch.Write() + if err != nil { + log.Warn("Failed to store result at ResultAtCount", "err", err) + return msgResult, nil + } + + return msgResult, nil } func (s *TransactionStreamer) checkResult(msgResult *execution.MessageResult, expectedBlockHash *common.Hash) { @@ -1068,6 +1119,19 @@ func (s *TransactionStreamer) checkResult(msgResult *execution.MessageResult, ex } } +func (s *TransactionStreamer) storeResult( + pos arbutil.MessageIndex, + msgResult execution.MessageResult, + batch ethdb.Batch, +) error { + msgResultBytes, err := rlp.EncodeToBytes(msgResult) + if err != nil { + return err + } + key := dbKey(messageResultPrefix, uint64(pos)) + return batch.Put(key, msgResultBytes) +} + // exposed for testing // return value: true if should be called again immediately func (s *TransactionStreamer) ExecuteNextMsg(ctx context.Context, exec execution.ExecutionSequencer) bool { @@ -1120,6 +1184,18 @@ func (s *TransactionStreamer) ExecuteNextMsg(ctx context.Context, exec execution s.checkResult(msgResult, msgAndBlockHash.BlockHash) + batch := s.db.NewBatch() + err = s.storeResult(pos, *msgResult, batch) + if err != nil { + log.Error("feedOneMsg failed to store result", "err", err) + return false + } + err = batch.Write() + if err != nil { + log.Error("feedOneMsg failed to store result", "err", err) + return false + } + msgWithBlockHash := arbostypes.MessageWithMetadataAndBlockHash{ MessageWithMeta: msgAndBlockHash.MessageWithMeta, BlockHash: &msgResult.BlockHash, diff --git a/arbos/arbosState/arbosstate.go b/arbos/arbosState/arbosstate.go index 9ff3dd3aa5..91c2207aae 100644 --- a/arbos/arbosState/arbosstate.go +++ b/arbos/arbosState/arbosstate.go @@ -15,6 +15,9 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/hashdb" + "github.com/ethereum/go-ethereum/triedb/pathdb" "github.com/offchainlabs/nitro/arbcompress" "github.com/offchainlabs/nitro/arbos/addressSet" @@ -29,6 +32,7 @@ import ( "github.com/offchainlabs/nitro/arbos/retryables" "github.com/offchainlabs/nitro/arbos/storage" "github.com/offchainlabs/nitro/arbos/util" + "github.com/offchainlabs/nitro/util/testhelpers/env" ) // ArbosState contains ArbOS-related state. It is backed by ArbOS's storage in the persistent stateDB. @@ -115,7 +119,11 @@ func OpenSystemArbosStateOrPanic(stateDB vm.StateDB, tracingInfo *util.TracingIn // NewArbosMemoryBackedArbOSState creates and initializes a memory-backed ArbOS state (for testing only) func NewArbosMemoryBackedArbOSState() (*ArbosState, *state.StateDB) { raw := rawdb.NewMemoryDatabase() - db := state.NewDatabase(raw) + trieConfig := &triedb.Config{Preimages: false, PathDB: pathdb.Defaults} + if env.GetTestStateScheme() == rawdb.HashScheme { + trieConfig = &triedb.Config{Preimages: false, HashDB: hashdb.Defaults} + } + db := state.NewDatabaseWithConfig(raw, trieConfig) statedb, err := state.New(common.Hash{}, db, nil) if err != nil { log.Crit("failed to init empty statedb", "error", err) diff --git a/arbos/arbosState/initialization_test.go b/arbos/arbosState/initialization_test.go index 0ef9cea4c5..34802392fe 100644 --- a/arbos/arbosState/initialization_test.go +++ b/arbos/arbosState/initialization_test.go @@ -10,6 +10,7 @@ import ( "testing" "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/params" @@ -17,6 +18,7 @@ import ( "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/testhelpers" + "github.com/offchainlabs/nitro/util/testhelpers/env" ) func TestJsonMarshalUnmarshal(t *testing.T) { @@ -60,10 +62,13 @@ func tryMarshalUnmarshal(input *statetransfer.ArbosInitializationInfo, t *testin initReader := statetransfer.NewMemoryInitDataReader(&initData) chainConfig := params.ArbitrumDevTestChainConfig() - stateroot, err := InitializeArbosInDatabase(raw, initReader, chainConfig, arbostypes.TestInitMessage, 0, 0) + + cacheConfig := core.DefaultCacheConfigWithScheme(env.GetTestStateScheme()) + stateroot, err := InitializeArbosInDatabase(raw, cacheConfig, initReader, chainConfig, arbostypes.TestInitMessage, 0, 0) Require(t, err) - stateDb, err := state.New(stateroot, state.NewDatabase(raw), nil) + triedbConfig := cacheConfig.TriedbConfig() + stateDb, err := state.New(stateroot, state.NewDatabaseWithConfig(raw, triedbConfig), nil) Require(t, err) arbState, err := OpenArbosState(stateDb, &burn.SystemBurner{}) diff --git a/arbos/arbosState/initialize.go b/arbos/arbosState/initialize.go index 1cc2f63c4f..0cb6cc6f82 100644 --- a/arbos/arbosState/initialize.go +++ b/arbos/arbosState/initialize.go @@ -6,9 +6,12 @@ package arbosState import ( "errors" "math/big" + "regexp" "sort" "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/types" "github.com/ethereum/go-ethereum/ethdb" @@ -51,13 +54,22 @@ func MakeGenesisBlock(parentHash common.Hash, blockNumber uint64, timestamp uint return types.NewBlock(head, nil, nil, nil, trie.NewStackTrie(nil)) } -func InitializeArbosInDatabase(db ethdb.Database, initData statetransfer.InitDataReader, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage, timestamp uint64, accountsPerSync uint) (common.Hash, error) { - stateDatabase := state.NewDatabase(db) +func InitializeArbosInDatabase(db ethdb.Database, cacheConfig *core.CacheConfig, initData statetransfer.InitDataReader, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage, timestamp uint64, accountsPerSync uint) (root common.Hash, err error) { + triedbConfig := cacheConfig.TriedbConfig() + triedbConfig.Preimages = false + stateDatabase := state.NewDatabaseWithConfig(db, triedbConfig) + defer func() { + err = errors.Join(err, stateDatabase.TrieDB().Close()) + }() statedb, err := state.New(common.Hash{}, stateDatabase, nil) if err != nil { log.Crit("failed to init empty statedb", "error", err) } + noStateTrieChangesToCommitError := regexp.MustCompile("^triedb layer .+ is disk layer$") + + // commit avoids keeping the entire state in memory while importing the state. + // At some time it was also used to avoid reprocessing the whole import in case of a crash. commit := func() (common.Hash, error) { root, err := statedb.Commit(chainConfig.ArbitrumChainParams.GenesisBlockNum, true) if err != nil { @@ -65,7 +77,11 @@ func InitializeArbosInDatabase(db ethdb.Database, initData statetransfer.InitDat } err = stateDatabase.TrieDB().Commit(root, true) if err != nil { - return common.Hash{}, err + // pathdb returns an error when there are no state trie changes to commit and we try to commit. + // This checks if the error is the expected one and ignores it. + if (cacheConfig.StateScheme != rawdb.PathScheme) || !noStateTrieChangesToCommitError.MatchString(err.Error()) { + return common.Hash{}, err + } } statedb, err = state.New(root, stateDatabase, nil) if err != nil { diff --git a/arbos/block_processor.go b/arbos/block_processor.go index c85f548fcc..d2778e02a9 100644 --- a/arbos/block_processor.go +++ b/arbos/block_processor.go @@ -22,7 +22,6 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/trie" @@ -144,27 +143,10 @@ func ProduceBlock( statedb *state.StateDB, chainContext core.ChainContext, chainConfig *params.ChainConfig, - batchFetcher arbostypes.FallibleBatchFetcher, isMsgForPrefetch bool, logger core.BlockchainLogger, ) (*types.Block, types.Receipts, error) { - var batchFetchErr error - txes, err := ParseL2Transactions(message, chainConfig.ChainID, func(batchNum uint64, batchHash common.Hash) []byte { - data, err := batchFetcher(batchNum) - if err != nil { - batchFetchErr = err - return nil - } - dataHash := crypto.Keccak256Hash(data) - if dataHash != batchHash { - batchFetchErr = fmt.Errorf("expecting batch %v hash %v but got data with hash %v", batchNum, batchHash, dataHash) - return nil - } - return data - }) - if batchFetchErr != nil { - return nil, nil, batchFetchErr - } + txes, err := ParseL2Transactions(message, chainConfig.ChainID) if err != nil { log.Warn("error parsing incoming message", "err", err) txes = types.Transactions{} diff --git a/arbos/incomingmessage_test.go b/arbos/incomingmessage_test.go index 14726a419f..2933f6a719 100644 --- a/arbos/incomingmessage_test.go +++ b/arbos/incomingmessage_test.go @@ -36,7 +36,7 @@ func TestSerializeAndParseL1Message(t *testing.T) { if err != nil { t.Error(err) } - txes, err := ParseL2Transactions(newMsg, chainId, nil) + txes, err := ParseL2Transactions(newMsg, chainId) if err != nil { t.Error(err) } diff --git a/arbos/parse_l2.go b/arbos/parse_l2.go index d2df3bdf89..06722e4063 100644 --- a/arbos/parse_l2.go +++ b/arbos/parse_l2.go @@ -17,9 +17,7 @@ import ( "github.com/offchainlabs/nitro/util/arbmath" ) -type InfallibleBatchFetcher func(batchNum uint64, batchHash common.Hash) []byte - -func ParseL2Transactions(msg *arbostypes.L1IncomingMessage, chainId *big.Int, batchFetcher InfallibleBatchFetcher) (types.Transactions, error) { +func ParseL2Transactions(msg *arbostypes.L1IncomingMessage, chainId *big.Int) (types.Transactions, error) { if len(msg.L2msg) > arbostypes.MaxL2MessageSize { // ignore the message if l2msg is too large return nil, errors.New("message too large") @@ -71,7 +69,7 @@ func ParseL2Transactions(msg *arbostypes.L1IncomingMessage, chainId *big.Int, ba log.Debug("ignoring rollup event message") return types.Transactions{}, nil case arbostypes.L1MessageType_BatchPostingReport: - tx, err := parseBatchPostingReportMessage(bytes.NewReader(msg.L2msg), chainId, msg.BatchGasCost, batchFetcher) + tx, err := parseBatchPostingReportMessage(bytes.NewReader(msg.L2msg), chainId, msg.BatchGasCost) if err != nil { return nil, err } @@ -370,8 +368,8 @@ func parseSubmitRetryableMessage(rd io.Reader, header *arbostypes.L1IncomingMess return types.NewTx(tx), err } -func parseBatchPostingReportMessage(rd io.Reader, chainId *big.Int, msgBatchGasCost *uint64, batchFetcher InfallibleBatchFetcher) (*types.Transaction, error) { - batchTimestamp, batchPosterAddr, batchHash, batchNum, l1BaseFee, extraGas, err := arbostypes.ParseBatchPostingReportMessageFields(rd) +func parseBatchPostingReportMessage(rd io.Reader, chainId *big.Int, msgBatchGasCost *uint64) (*types.Transaction, error) { + batchTimestamp, batchPosterAddr, _, batchNum, l1BaseFee, extraGas, err := arbostypes.ParseBatchPostingReportMessageFields(rd) if err != nil { return nil, err } @@ -379,8 +377,7 @@ func parseBatchPostingReportMessage(rd io.Reader, chainId *big.Int, msgBatchGasC if msgBatchGasCost != nil { batchDataGas = *msgBatchGasCost } else { - batchData := batchFetcher(batchNum, batchHash) - batchDataGas = arbostypes.ComputeBatchGasCost(batchData) + return nil, errors.New("cannot compute batch gas cost") } batchDataGas = arbmath.SaturatingUAdd(batchDataGas, extraGas) diff --git a/arbos/programs/api.go b/arbos/programs/api.go index 65a58a47c2..504289322f 100644 --- a/arbos/programs/api.go +++ b/arbos/programs/api.go @@ -4,8 +4,6 @@ package programs import ( - "errors" - "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -65,14 +63,10 @@ func newApiClosures( 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 } @@ -82,9 +76,6 @@ func newApiClosures( value := common.BytesToHash(data[32:64]) data = data[64:] - if tracingInfo != nil { - tracingInfo.RecordStorageSet(key, value) - } if readOnly { return WriteProtection } @@ -137,16 +128,16 @@ func newApiClosures( 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) } + // Tracing: emit the call (value transfer is done later in evm.Call) + if tracingInfo != nil { + tracingInfo.CaptureStylusCall(opcode, contract, value, input, gas, startGas, baseCost) + } + var ret []byte var returnGas uint64 @@ -202,11 +193,6 @@ func newApiClosures( 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 @@ -220,7 +206,10 @@ func newApiClosures( if suberr != nil { addr = zeroAddr } - if !errors.Is(vm.ErrExecutionReverted, suberr) { + // This matches geth behavior of doing an exact error comparison instead of errors.Is + // See e.g. EVM's create method or the opCreate function for references of how geth checks this + //nolint:errorlint + if suberr != vm.ErrExecutionReverted { res = nil // returnData is only provided in the revert case (opCreate) } interpreter.SetReturnData(res) @@ -228,9 +217,6 @@ func newApiClosures( return addr, res, cost, nil } emitLog := func(topics []common.Hash, data []byte) error { - if tracingInfo != nil { - tracingInfo.RecordEmitLog(topics, data) - } if readOnly { return vm.ErrWriteProtection } @@ -269,10 +255,7 @@ func newApiClosures( } captureHostio := func(name string, args, outs []byte, startInk, endInk uint64) { tracingInfo.Tracer.CaptureStylusHostio(name, args, outs, startInk, endInk) - if name == "evm_gas_left" || name == "evm_ink_left" { - tracingInfo.Tracer.CaptureState(0, vm.GAS, 0, 0, scope, []byte{}, depth, nil) - tracingInfo.Tracer.CaptureState(0, vm.POP, 0, 0, scope, []byte{}, depth, nil) - } + tracingInfo.CaptureEVMTraceForHostio(name, args, outs, startInk, endInk) } return func(req RequestType, input []byte) ([]byte, []byte, uint64) { diff --git a/arbos/programs/cgo_test.go b/arbos/programs/cgo_test.go new file mode 100644 index 0000000000..c0e146d98d --- /dev/null +++ b/arbos/programs/cgo_test.go @@ -0,0 +1,44 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +//go:build !wasm +// +build !wasm + +package programs + +import ( + "fmt" + "os" + "strings" + "testing" +) + +func TestConstants(t *testing.T) { + err := testConstants() + if err != nil { + t.Fatal(err) + } +} + +// normal test will not write anything to disk +// to test cross-compilation: +// * run test with TEST_COMPILE=STORE on one machine +// * copy target/testdata to the other machine +// * run test with TEST_COMPILE=LOAD on the other machine +func TestCompileArch(t *testing.T) { + compile_env := os.Getenv("TEST_COMPILE") + if compile_env == "" { + fmt.Print("use TEST_COMPILE=[STORE|LOAD] to allow store/load in compile test") + } + store := strings.Contains(compile_env, "STORE") + err := testCompileArch(store) + if err != nil { + t.Fatal(err) + } + if store || strings.Contains(compile_env, "LOAD") { + err = testCompileLoad() + if err != nil { + t.Fatal(err) + } + } +} diff --git a/arbos/programs/constant_test.go b/arbos/programs/constant_test.go deleted file mode 100644 index fe29bcf3d9..0000000000 --- a/arbos/programs/constant_test.go +++ /dev/null @@ -1,13 +0,0 @@ -// 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/native.go b/arbos/programs/native.go index f8e2696aad..fd3dec25a0 100644 --- a/arbos/programs/native.go +++ b/arbos/programs/native.go @@ -54,11 +54,11 @@ func activateProgram( debug bool, burner burn.Burner, ) (*activationInfo, error) { - info, asm, module, err := activateProgramInternal(db, program, codehash, wasm, page_limit, version, debug, burner.GasLeft()) + info, asmMap, err := activateProgramInternal(db, program, codehash, wasm, page_limit, version, debug, burner.GasLeft()) if err != nil { return nil, err } - db.ActivateWasm(info.moduleHash, asm, module) + db.ActivateWasm(info.moduleHash, asmMap) return info, nil } @@ -71,41 +71,52 @@ func activateProgramInternal( version uint16, debug bool, gasLeft *uint64, -) (*activationInfo, []byte, []byte, error) { +) (*activationInfo, map[rawdb.Target][]byte, error) { output := &rustBytes{} - asmLen := usize(0) moduleHash := &bytes32{} stylusData := &C.StylusData{} codeHash := hashToBytes32(codehash) - status := userStatus(C.stylus_activate( + status_mod := 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) + module, msg, err := status_mod.toResult(output.intoBytes(), debug) if err != nil { if debug { log.Warn("activation failed", "err", err, "msg", msg, "program", addressForLogging) } if errors.Is(err, vm.ErrExecutionReverted) { - return nil, nil, nil, fmt.Errorf("%w: %s", ErrProgramActivation, msg) + return nil, nil, fmt.Errorf("%w: %s", ErrProgramActivation, msg) } - return nil, nil, nil, err + return nil, nil, err + } + target := rawdb.LocalTarget() + status_asm := C.stylus_compile( + goSlice(wasm), + u16(version), + cbool(debug), + goSlice([]byte(target)), + output, + ) + asm := output.intoBytes() + if status_asm != 0 { + return nil, nil, fmt.Errorf("%w: %s", ErrProgramActivation, string(asm)) + } + asmMap := map[rawdb.Target][]byte{ + rawdb.TargetWavm: module, + target: asm, } hash := moduleHash.toHash() - split := int(asmLen) - asm := data[:split] - module := data[split:] info := &activationInfo{ moduleHash: hash, @@ -114,11 +125,12 @@ func activateProgramInternal( asmEstimate: uint32(stylusData.asm_estimate), footprint: uint16(stylusData.footprint), } - return info, asm, module, err + return info, asmMap, err } func getLocalAsm(statedb vm.StateDB, moduleHash common.Hash, addressForLogging common.Address, code []byte, codeHash common.Hash, pagelimit uint16, time uint64, debugMode bool, program Program) ([]byte, error) { - localAsm, err := statedb.TryGetActivatedAsm(moduleHash) + localTarget := rawdb.LocalTarget() + localAsm, err := statedb.TryGetActivatedAsm(localTarget, moduleHash) if err == nil && len(localAsm) > 0 { return localAsm, nil } @@ -132,7 +144,7 @@ func getLocalAsm(statedb vm.StateDB, moduleHash common.Hash, addressForLogging c 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, addressForLogging, codeHash, wasm, pagelimit, program.version, debugMode, &unlimitedGas) + info, asmMap, err := activateProgramInternal(statedb, addressForLogging, codeHash, wasm, pagelimit, program.version, debugMode, &unlimitedGas) if err != nil { log.Error("failed to reactivate program", "address", addressForLogging, "expected moduleHash", moduleHash, "err", err) return nil, fmt.Errorf("failed to reactivate program address: %v err: %w", addressForLogging, err) @@ -148,14 +160,23 @@ func getLocalAsm(statedb vm.StateDB, moduleHash common.Hash, addressForLogging c // 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) + rawdb.WriteActivation(batch, moduleHash, asmMap) if err := batch.Write(); err != nil { log.Error("failed writing re-activation to state", "address", addressForLogging, "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) + statedb.ActivateWasm(info.moduleHash, asmMap) + } + asm, exists := asmMap[localTarget] + if !exists { + var availableTargets []rawdb.Target + for target := range asmMap { + availableTargets = append(availableTargets, target) + } + log.Error("failed to reactivate program - missing asm for local target", "address", addressForLogging, "local target", localTarget, "available targets", availableTargets) + return nil, fmt.Errorf("failed to reactivate program - missing asm for local target, address: %v, local target: %v, available targets: %v", addressForLogging, localTarget, availableTargets) } return asm, nil } @@ -182,7 +203,11 @@ func callProgram( } if db, ok := db.(*state.StateDB); ok { - db.RecordProgram(moduleHash) + targets := []rawdb.Target{ + rawdb.TargetWavm, + rawdb.LocalTarget(), + } + db.RecordProgram(targets, moduleHash) } evmApi := newApi(interpreter, tracingInfo, scope, memoryModel) @@ -206,6 +231,9 @@ func callProgram( if status == userFailure && debug { log.Warn("program failure", "err", err, "msg", msg, "program", address, "depth", depth) } + if tracingInfo != nil { + tracingInfo.CaptureStylusExit(uint8(status), data, err, scope.Contract.Gas) + } return data, err } @@ -260,6 +288,25 @@ func ResizeWasmLruCache(size uint32) { C.stylus_cache_lru_resize(u32(size)) } +const DefaultTargetDescriptionArm = "arm64-linux-unknown+neon" +const DefaultTargetDescriptionX86 = "x86_64-linux-unknown+sse4.2" + +func SetTarget(name rawdb.Target, description string, native bool) error { + output := &rustBytes{} + status := userStatus(C.stylus_target_set( + goSlice([]byte(name)), + goSlice([]byte(description)), + output, + cbool(native), + )) + if status != userSuccess { + msg := arbutil.ToStringOrHex(output.intoBytes()) + log.Error("failed to set stylus compilation target", "status", status, "msg", msg) + return fmt.Errorf("failed to set stylus compilation target, status %v: %v", status, msg) + } + return nil +} + func (value bytes32) toHash() common.Hash { hash := common.Hash{} for index, b := range value.bytes { diff --git a/arbos/programs/native_api.go b/arbos/programs/native_api.go index 136f74c964..6fbb630ef3 100644 --- a/arbos/programs/native_api.go +++ b/arbos/programs/native_api.go @@ -34,7 +34,7 @@ import ( ) var apiObjects sync.Map -var apiIds uintptr // atomic and sequential +var apiIds atomic.Uintptr // atomic and sequential type NativeApi struct { handler RequestHandler @@ -49,7 +49,7 @@ func newApi( memoryModel *MemoryModel, ) NativeApi { handler := newApiClosures(interpreter, tracingInfo, scope, memoryModel) - apiId := atomic.AddUintptr(&apiIds, 1) + apiId := apiIds.Add(1) id := usize(apiId) api := NativeApi{ handler: handler, diff --git a/arbos/programs/testcompile.go b/arbos/programs/testcompile.go new file mode 100644 index 0000000000..a16bae52c0 --- /dev/null +++ b/arbos/programs/testcompile.go @@ -0,0 +1,246 @@ +// 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" + +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; +typedef size_t usize; + +void handleReqWrap(usize api, u32 req_type, RustSlice *data, u64 *out_cost, GoSliceData *out_result, GoSliceData *out_raw_data); +*/ +import "C" +import ( + "fmt" + "os" + "runtime" + + "github.com/ethereum/go-ethereum/core/rawdb" +) + +func Wat2Wasm(wat []byte) ([]byte, error) { + output := &rustBytes{} + + status := C.wat_to_wasm(goSlice(wat), output) + + if status != 0 { + return nil, fmt.Errorf("failed reading wat file: %v", string(output.intoBytes())) + } + + return output.intoBytes(), nil +} + +func testCompileArch(store bool) error { + + localTarget := rawdb.LocalTarget() + nativeArm64 := localTarget == rawdb.TargetArm64 + nativeAmd64 := localTarget == rawdb.TargetAmd64 + + arm64CompileName := []byte(rawdb.TargetArm64) + amd64CompileName := []byte(rawdb.TargetAmd64) + + arm64TargetString := []byte(DefaultTargetDescriptionArm) + amd64TargetString := []byte(DefaultTargetDescriptionX86) + + output := &rustBytes{} + + _, err := fmt.Print("starting test.. native arm? ", nativeArm64, " amd? ", nativeAmd64, " GOARCH/GOOS: ", runtime.GOARCH+"/"+runtime.GOOS, "\n") + if err != nil { + return err + } + + status := C.stylus_target_set(goSlice(arm64CompileName), + goSlice(arm64TargetString), + output, + cbool(nativeArm64)) + + if status != 0 { + return fmt.Errorf("failed setting compilation target arm: %v", string(output.intoBytes())) + } + + status = C.stylus_target_set(goSlice(amd64CompileName), + goSlice(amd64TargetString), + output, + cbool(nativeAmd64)) + + if status != 0 { + return fmt.Errorf("failed setting compilation target amd: %v", string(output.intoBytes())) + } + + source, err := os.ReadFile("../../arbitrator/stylus/tests/add.wat") + if err != nil { + return fmt.Errorf("failed reading stylus contract: %w", err) + } + + wasm, err := Wat2Wasm(source) + if err != nil { + return err + } + + if store { + _, err := fmt.Print("storing compiled files to ../../target/testdata/\n") + if err != nil { + return err + } + err = os.MkdirAll("../../target/testdata", 0755) + if err != nil { + return err + } + } + + status = C.stylus_compile( + goSlice(wasm), + u16(1), + cbool(true), + goSlice([]byte("booga")), + output, + ) + if status == 0 { + return fmt.Errorf("succeeded compiling non-existent arch: %v", string(output.intoBytes())) + } + + status = C.stylus_compile( + goSlice(wasm), + u16(1), + cbool(true), + goSlice([]byte{}), + output, + ) + if status != 0 { + return fmt.Errorf("failed compiling native: %v", string(output.intoBytes())) + } + if store && !nativeAmd64 && !nativeArm64 { + _, err := fmt.Printf("writing host file\n") + if err != nil { + return err + } + + err = os.WriteFile("../../target/testdata/host.bin", output.intoBytes(), 0644) + if err != nil { + return err + } + } + + status = C.stylus_compile( + goSlice(wasm), + u16(1), + cbool(true), + goSlice(arm64CompileName), + output, + ) + if status != 0 { + return fmt.Errorf("failed compiling arm: %v", string(output.intoBytes())) + } + if store { + _, err := fmt.Printf("writing arm file\n") + if err != nil { + return err + } + + err = os.WriteFile("../../target/testdata/arm64.bin", output.intoBytes(), 0644) + if err != nil { + return err + } + } + + status = C.stylus_compile( + goSlice(wasm), + u16(1), + cbool(true), + goSlice(amd64CompileName), + output, + ) + if status != 0 { + return fmt.Errorf("failed compiling amd: %v", string(output.intoBytes())) + } + if store { + _, err := fmt.Printf("writing amd64 file\n") + if err != nil { + return err + } + + err = os.WriteFile("../../target/testdata/amd64.bin", output.intoBytes(), 0644) + if err != nil { + return err + } + } + + return nil +} + +func testCompileLoad() error { + filePath := "../../target/testdata/host.bin" + localTarget := rawdb.LocalTarget() + if localTarget == rawdb.TargetArm64 { + filePath = "../../target/testdata/arm64.bin" + } + if localTarget == rawdb.TargetAmd64 { + filePath = "../../target/testdata/amd64.bin" + } + + _, err := fmt.Print("starting load test. FilePath: ", filePath, " GOARCH/GOOS: ", runtime.GOARCH+"/"+runtime.GOOS, "\n") + if err != nil { + return err + } + + localAsm, err := os.ReadFile(filePath) + if err != nil { + return err + } + + calldata := []byte{} + + evmData := EvmData{} + progParams := ProgParams{ + MaxDepth: 10000, + InkPrice: 1, + DebugMode: true, + } + reqHandler := C.NativeRequestHandler{ + handle_request_fptr: (*[0]byte)(C.handleReqWrap), + id: 0, + } + + inifiniteGas := u64(0xfffffffffffffff) + + output := &rustBytes{} + + _, err = fmt.Print("launching program..\n") + if err != nil { + return err + } + + status := userStatus(C.stylus_call( + goSlice(localAsm), + goSlice(calldata), + progParams.encode(), + reqHandler, + evmData.encode(), + cbool(true), + output, + &inifiniteGas, + u32(0), + )) + + _, err = fmt.Print("returned: ", status, "\n") + if err != nil { + return err + } + + _, msg, err := status.toResult(output.intoBytes(), true) + if status == userFailure { + err = fmt.Errorf("%w: %v", err, msg) + } + + return err +} diff --git a/arbos/programs/wasmstorehelper.go b/arbos/programs/wasmstorehelper.go index 9e69178694..4f82d80282 100644 --- a/arbos/programs/wasmstorehelper.go +++ b/arbos/programs/wasmstorehelper.go @@ -44,8 +44,8 @@ func (p Programs) SaveActiveProgramToWasmStore(statedb *state.StateDB, codeHash } // If already in wasm store then return early - localAsm, err := statedb.TryGetActivatedAsm(moduleHash) - if err == nil && len(localAsm) > 0 { + _, err = statedb.TryGetActivatedAsmMap([]rawdb.Target{rawdb.TargetWavm, rawdb.LocalTarget()}, moduleHash) + if err == nil { return nil } @@ -58,7 +58,7 @@ func (p Programs) SaveActiveProgramToWasmStore(statedb *state.StateDB, codeHash unlimitedGas := uint64(0xffffffffffff) // We know program is activated, so it must be in correct version and not use too much memory // Empty program address is supplied because we dont have access to this during rebuilding of wasm store - info, asm, module, err := activateProgramInternal(statedb, common.Address{}, codeHash, wasm, params.PageLimit, program.version, debugMode, &unlimitedGas) + info, asmMap, err := activateProgramInternal(statedb, common.Address{}, codeHash, wasm, params.PageLimit, program.version, debugMode, &unlimitedGas) if err != nil { log.Error("failed to reactivate program while rebuilding wasm store", "expected moduleHash", moduleHash, "err", err) return fmt.Errorf("failed to reactivate program while rebuilding wasm store: %w", err) @@ -70,7 +70,7 @@ func (p Programs) SaveActiveProgramToWasmStore(statedb *state.StateDB, codeHash } batch := statedb.Database().WasmStore().NewBatch() - rawdb.WriteActivation(batch, moduleHash, asm, module) + rawdb.WriteActivation(batch, moduleHash, asmMap) if err := batch.Write(); err != nil { log.Error("failed writing re-activation to state while rebuilding wasm store", "err", err) return err diff --git a/arbos/storage/storage.go b/arbos/storage/storage.go index 158b8896c1..6e6c976644 100644 --- a/arbos/storage/storage.go +++ b/arbos/storage/storage.go @@ -18,9 +18,13 @@ import ( "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/hashdb" + "github.com/ethereum/go-ethereum/triedb/pathdb" "github.com/offchainlabs/nitro/arbos/burn" "github.com/offchainlabs/nitro/arbos/util" "github.com/offchainlabs/nitro/util/arbmath" + "github.com/offchainlabs/nitro/util/testhelpers/env" ) // Storage allows ArbOS to store data persistently in the Ethereum-compatible stateDB. This is represented in @@ -73,15 +77,21 @@ func NewGeth(statedb vm.StateDB, burner burn.Burner) *Storage { } } -// NewMemoryBacked uses Geth's memory-backed database to create an evm key-value store +// NewMemoryBacked uses Geth's memory-backed database to create an evm key-value store. +// Only used for testing. func NewMemoryBacked(burner burn.Burner) *Storage { return NewGeth(NewMemoryBackedStateDB(), burner) } // NewMemoryBackedStateDB uses Geth's memory-backed database to create a statedb +// Only used for testing. func NewMemoryBackedStateDB() vm.StateDB { raw := rawdb.NewMemoryDatabase() - db := state.NewDatabase(raw) + trieConfig := &triedb.Config{Preimages: false, PathDB: pathdb.Defaults} + if env.GetTestStateScheme() == rawdb.HashScheme { + trieConfig = &triedb.Config{Preimages: false, HashDB: hashdb.Defaults} + } + db := state.NewDatabaseWithConfig(raw, trieConfig) statedb, err := state.New(common.Hash{}, db, nil) if err != nil { panic("failed to init empty statedb") diff --git a/arbos/util/storage_cache.go b/arbos/util/storage_cache.go new file mode 100644 index 0000000000..bf05a5824d --- /dev/null +++ b/arbos/util/storage_cache.go @@ -0,0 +1,76 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package util + +import ( + "github.com/ethereum/go-ethereum/common" +) + +type storageCacheEntry struct { + Value common.Hash + Known *common.Hash +} + +func (e storageCacheEntry) dirty() bool { + return e.Known == nil || e.Value != *e.Known +} + +type storageCacheStores struct { + Key common.Hash + Value common.Hash +} + +// storageCache mirrors the stylus storage cache on arbos when tracing a call. +// This is useful for correctly reporting the SLOAD and SSTORE opcodes. +type storageCache struct { + cache map[common.Hash]storageCacheEntry +} + +func newStorageCache() *storageCache { + return &storageCache{ + cache: make(map[common.Hash]storageCacheEntry), + } +} + +// Load adds a value to the cache and returns true if the logger should emit a load opcode. +func (s *storageCache) Load(key, value common.Hash) bool { + _, ok := s.cache[key] + if !ok { + // The value was not in cache, so it came from EVM + s.cache[key] = storageCacheEntry{ + Value: value, + Known: &value, + } + } + return !ok +} + +// Store updates the value on the cache. +func (s *storageCache) Store(key, value common.Hash) { + entry := s.cache[key] + entry.Value = value // Do not change known value + s.cache[key] = entry +} + +// Flush returns the store operations that should be logged. +func (s *storageCache) Flush() []storageCacheStores { + stores := []storageCacheStores{} + for key, entry := range s.cache { + if entry.dirty() { + v := entry.Value // Create new var to avoid alliasing + entry.Known = &v + s.cache[key] = entry + stores = append(stores, storageCacheStores{ + Key: key, + Value: entry.Value, + }) + } + } + return stores +} + +// Clear clears the cache. +func (s *storageCache) Clear() { + s.cache = make(map[common.Hash]storageCacheEntry) +} diff --git a/arbos/util/storage_cache_test.go b/arbos/util/storage_cache_test.go new file mode 100644 index 0000000000..1cc4ea14ec --- /dev/null +++ b/arbos/util/storage_cache_test.go @@ -0,0 +1,110 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package util + +import ( + "bytes" + "slices" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/google/go-cmp/cmp" + "github.com/offchainlabs/nitro/util/testhelpers" +) + +func TestStorageCache(t *testing.T) { + keys := make([]common.Hash, 3) + values := make([]common.Hash, len(keys)) + for i := range keys { + keys[i] = testhelpers.RandomHash() + values[i] = testhelpers.RandomHash() + } + + cache := newStorageCache() + + t.Run("load then load", func(t *testing.T) { + emitLog := cache.Load(keys[0], values[0]) + if !emitLog { + t.Fatal("unexpected value in cache") + } + emitLog = cache.Load(keys[0], values[0]) + if emitLog { + t.Fatal("expected value in cache") + } + }) + + t.Run("load another value", func(t *testing.T) { + emitLog := cache.Load(keys[1], values[1]) + if !emitLog { + t.Fatal("unexpected value in cache") + } + }) + + t.Run("load then store", func(t *testing.T) { + _ = cache.Load(keys[2], values[0]) + cache.Store(keys[2], values[2]) + if !cache.cache[keys[2]].dirty() { + t.Fatal("expected value to be dirty") + } + if cache.cache[keys[2]].Value != values[2] { + t.Fatal("wrong value in cache") + } + }) + + t.Run("clear", func(t *testing.T) { + cache.Clear() + if len(cache.cache) != 0 { + t.Fatal("expected to be empty") + } + }) + + t.Run("store then load", func(t *testing.T) { + cache.Store(keys[0], values[0]) + emitLog := cache.Load(keys[0], values[0]) + if emitLog { + t.Fatal("expected value in cache") + } + }) + + t.Run("flush only stored", func(t *testing.T) { + _ = cache.Load(keys[1], values[1]) + cache.Store(keys[2], values[2]) + stores := cache.Flush() + expected := []storageCacheStores{ + {Key: keys[0], Value: values[0]}, + {Key: keys[2], Value: values[2]}, + } + sortFunc := func(a, b storageCacheStores) int { + return bytes.Compare(a.Key.Bytes(), b.Key.Bytes()) + } + slices.SortFunc(stores, sortFunc) + slices.SortFunc(expected, sortFunc) + if diff := cmp.Diff(stores, expected); diff != "" { + t.Fatalf("wrong flush: %s", diff) + } + // everything should still be in cache + for i := range keys { + entry, ok := cache.cache[keys[i]] + if !ok { + t.Fatal("entry missing from cache") + } + if entry.dirty() { + t.Fatal("dirty entry after flush") + } + if entry.Value != values[i] { + t.Fatal("wrong value in entry") + } + } + }) + + t.Run("do not flush known values", func(t *testing.T) { + cache.Clear() + _ = cache.Load(keys[0], values[0]) + cache.Store(keys[0], values[0]) + stores := cache.Flush() + if len(stores) != 0 { + t.Fatal("unexpected store") + } + }) +} diff --git a/arbos/util/tracing.go b/arbos/util/tracing.go index a1d9c46828..89652554b8 100644 --- a/arbos/util/tracing.go +++ b/arbos/util/tracing.go @@ -4,12 +4,13 @@ package util import ( - "fmt" + "encoding/binary" "math/big" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/tracers" + "github.com/ethereum/go-ethereum/log" "github.com/holiman/uint256" ) @@ -22,10 +23,11 @@ const ( ) type TracingInfo struct { - Tracer vm.EVMLogger - Scenario TracingScenario - Contract *vm.Contract - Depth int + Tracer vm.EVMLogger + Scenario TracingScenario + Contract *vm.Contract + Depth int + storageCache *storageCache } // holds an address to satisfy core/vm's ContractRef() interface @@ -42,33 +44,14 @@ func NewTracingInfo(evm *vm.EVM, from, to common.Address, scenario TracingScenar return nil } return &TracingInfo{ - Tracer: evm.Config.Tracer, - Scenario: scenario, - Contract: vm.NewContract(addressHolder{to}, addressHolder{from}, uint256.NewInt(0), 0), - Depth: evm.Depth(), + Tracer: evm.Config.Tracer, + Scenario: scenario, + Contract: vm.NewContract(addressHolder{to}, addressHolder{from}, uint256.NewInt(0), 0), + Depth: evm.Depth(), + storageCache: newStorageCache(), } } -func (info *TracingInfo) RecordEmitLog(topics []common.Hash, data []byte) { - size := uint64(len(data)) - var args []uint256.Int - args = append(args, *uint256.NewInt(0)) // offset: byte offset in the memory in bytes - args = append(args, *uint256.NewInt(size)) // size: byte size to copy (length of data) - for _, topic := range topics { - args = append(args, HashToUint256(topic)) // topic: 32-byte value. Max topics count is 4 - } - memory := vm.NewMemory() - memory.Resize(size) - memory.Set(0, size, data) - scope := &vm.ScopeContext{ - Memory: memory, - Stack: TracingStackFromArgs(args...), - Contract: info.Contract, - } - logType := fmt.Sprintf("LOG%d", len(topics)) - info.Tracer.CaptureState(0, vm.StringToOp(logType), 0, 0, scope, []byte{}, info.Depth, nil) -} - func (info *TracingInfo) RecordStorageGet(key common.Hash) { tracer := info.Tracer if info.Scenario == TracingDuringEVM { @@ -146,6 +129,428 @@ func (info *TracingInfo) MockCall(input []byte, gas uint64, from, to common.Addr tracer.CaptureState(0, vm.POP, 0, 0, popScope, []byte{}, depth, nil) } +func (info *TracingInfo) CaptureEVMTraceForHostio(name string, args, outs []byte, startInk, endInk uint64) { + checkArgs := func(want int) bool { + if len(args) < want { + log.Warn("tracing: missing arguments bytes for hostio", "name", name, "want", want, "got", len(args)) + return false + } + return true + } + + checkOuts := func(want int) bool { + if len(outs) < want { + log.Warn("tracing: missing outputs bytes for hostio", "name", name, "want", want, "got", len(args)) + return false + } + return true + } + + firstOpcode := true + capture := func(op vm.OpCode, memory []byte, stackValues ...[]byte) { + const inkToGas = 10000 + gas := endInk / inkToGas + var cost uint64 + if firstOpcode { + cost = (startInk - endInk) / inkToGas + firstOpcode = false + } else { + // When capturing multiple opcodes, usually the first one is the relevant + // action and the following ones just pop the result values from the stack. + cost = 0 + } + info.captureState(op, gas, cost, memory, stackValues...) + } + + switch name { + case "read_args": + destOffset := []byte(nil) + offset := []byte(nil) + size := lenToBytes(outs) + capture(vm.CALLDATACOPY, outs, destOffset, offset, size) + + case "storage_load_bytes32": + if !checkArgs(32) || !checkOuts(32) { + return + } + key := args[:32] + value := outs[:32] + if info.storageCache.Load(common.Hash(key), common.Hash(value)) { + capture(vm.SLOAD, nil, key) + capture(vm.POP, nil, value) + } + + case "storage_cache_bytes32": + if !checkArgs(32 + 32) { + return + } + key := args[:32] + value := args[32:64] + info.storageCache.Store(common.Hash(key), common.Hash(value)) + + case "storage_flush_cache": + if !checkArgs(1) { + return + } + toClear := args[0] != 0 + for _, store := range info.storageCache.Flush() { + capture(vm.SSTORE, nil, store.Key.Bytes(), store.Value.Bytes()) + } + if toClear { + info.storageCache.Clear() + } + + case "transient_load_bytes32": + if !checkArgs(32) || !checkOuts(32) { + return + } + key := args[:32] + value := outs[:32] + capture(vm.TLOAD, nil, key) + capture(vm.POP, nil, value) + + case "transient_store_bytes32": + if !checkArgs(32 + 32) { + return + } + key := args[:32] + value := args[32:64] + capture(vm.TSTORE, nil, key, value) + + case "create1": + if !checkArgs(32) || !checkOuts(20) { + return + } + value := args[:32] + code := args[32:] + offset := []byte(nil) + size := lenToBytes(code) + address := outs[:20] + capture(vm.CREATE, code, value, offset, size) + capture(vm.POP, code, address) + + case "create2": + if !checkArgs(32+32) || !checkOuts(20) { + return + } + value := args[:32] + salt := args[32:64] + code := args[64:] + offset := []byte(nil) + size := lenToBytes(code) + address := outs[:20] + capture(vm.CREATE2, code, value, offset, size, salt) + capture(vm.POP, code, address) + + case "read_return_data": + if !checkArgs(8) { + return + } + destOffset := []byte(nil) + offset := args[:4] + size := args[4:8] + capture(vm.RETURNDATACOPY, outs, destOffset, offset, size) + + case "return_data_size": + if !checkOuts(4) { + return + } + size := outs[:4] + capture(vm.RETURNDATASIZE, nil) + capture(vm.POP, nil, size) + + case "emit_log": + if !checkArgs(4) { + return + } + numTopics := int(binary.BigEndian.Uint32(args[:4])) + dataOffset := 4 + 32*numTopics + if !checkArgs(dataOffset) { + return + } + data := args[dataOffset:] + offset := []byte(nil) + size := lenToBytes(data) + opcode := vm.LOG0 + vm.OpCode(numTopics) + stack := [][]byte{offset, size} + for i := 0; i < numTopics; i++ { + topic := args[4+32*i : 4+32*(i+1)] + stack = append(stack, topic) + } + capture(opcode, data, stack...) + + case "account_balance": + if !checkArgs(20) || !checkOuts(32) { + return + } + address := args[:20] + balance := outs[:32] + capture(vm.BALANCE, nil, address) + capture(vm.POP, nil, balance) + + case "account_code": + if !checkArgs(20 + 4 + 4) { + return + } + address := args[:20] + destOffset := []byte(nil) + offset := args[20:24] + size := args[24:28] + capture(vm.EXTCODECOPY, nil, address, destOffset, offset, size) + + case "account_code_size": + if !checkArgs(20) || !checkOuts(4) { + return + } + address := args[:20] + size := outs[:4] + capture(vm.EXTCODESIZE, nil, address) + capture(vm.POP, nil, size) + + case "account_codehash": + if !checkArgs(20) || !checkOuts(32) { + return + } + address := args[:20] + hash := outs[:32] + capture(vm.EXTCODEHASH, nil, address) + capture(vm.POP, nil, hash) + + case "block_basefee": + if !checkOuts(32) { + return + } + baseFee := outs[:32] + capture(vm.BASEFEE, nil) + capture(vm.POP, nil, baseFee) + + case "block_coinbase": + if !checkOuts(20) { + return + } + address := outs[:20] + capture(vm.COINBASE, nil) + capture(vm.POP, nil, address) + + case "block_gas_limit": + if !checkOuts(8) { + return + } + gasLimit := outs[:8] + capture(vm.GASLIMIT, nil) + capture(vm.POP, nil, gasLimit) + + case "block_number": + if !checkOuts(8) { + return + } + blockNumber := outs[:8] + capture(vm.NUMBER, nil) + capture(vm.POP, nil, blockNumber) + + case "block_timestamp": + if !checkOuts(8) { + return + } + timestamp := outs[:8] + capture(vm.TIMESTAMP, nil) + capture(vm.POP, nil, timestamp) + + case "chainid": + if !checkOuts(8) { + return + } + chainId := outs[:8] + capture(vm.CHAINID, nil) + capture(vm.POP, nil, chainId) + + case "contract_address": + if !checkOuts(20) { + return + } + address := outs[:20] + capture(vm.ADDRESS, nil) + capture(vm.POP, nil, address) + + case "evm_gas_left", "evm_ink_left": + if !checkOuts(8) { + return + } + gas := outs[:8] + capture(vm.GAS, nil) + capture(vm.POP, nil, gas) + + case "math_div": + if !checkArgs(32+32) || !checkOuts(32) { + return + } + a := args[:32] + b := args[32:64] + result := outs[:32] + capture(vm.DIV, nil, a, b) + capture(vm.POP, nil, result) + + case "math_mod": + if !checkArgs(32+32) || !checkOuts(32) { + return + } + a := args[:32] + b := args[32:64] + result := outs[:32] + capture(vm.MOD, nil, a, b) + capture(vm.POP, nil, result) + + case "math_pow": + if !checkArgs(32+32) || !checkOuts(32) { + return + } + a := args[:32] + b := args[32:64] + result := outs[:32] + capture(vm.EXP, nil, a, b) + capture(vm.POP, nil, result) + + case "math_add_mod": + if !checkArgs(32+32+32) || !checkOuts(32) { + return + } + a := args[:32] + b := args[32:64] + c := args[64:96] + result := outs[:32] + capture(vm.ADDMOD, nil, a, b, c) + capture(vm.POP, nil, result) + + case "math_mul_mod": + if !checkArgs(32+32+32) || !checkOuts(32) { + return + } + a := args[:32] + b := args[32:64] + c := args[64:96] + result := outs[:32] + capture(vm.MULMOD, nil, a, b, c) + capture(vm.POP, nil, result) + + case "msg_sender": + if !checkOuts(20) { + return + } + address := outs[:20] + capture(vm.CALLER, nil) + capture(vm.POP, nil, address) + + case "msg_value": + if !checkOuts(32) { + return + } + value := outs[:32] + capture(vm.CALLVALUE, nil) + capture(vm.POP, nil, value) + + case "native_keccak256": + if !checkOuts(32) { + return + } + offset := []byte(nil) + size := lenToBytes(args) + hash := outs[:32] + capture(vm.KECCAK256, args, offset, size) + capture(vm.POP, args, hash) + + case "tx_gas_price": + if !checkOuts(32) { + return + } + price := outs[:32] + capture(vm.GASPRICE, nil) + capture(vm.POP, nil, price) + + case "tx_ink_price": + if !checkOuts(4) { + return + } + price := outs[:4] + capture(vm.GASPRICE, nil) + capture(vm.POP, nil, price) + + case "tx_origin": + if !checkOuts(20) { + return + } + address := outs[:20] + capture(vm.ORIGIN, nil) + capture(vm.POP, nil, address) + + case "call_contract", "delegate_call_contract", "static_call_contract": + // The API receives the CaptureHostIO after the EVM call is done but we want to + // capture the opcde before it. So, we capture the state in CaptureStylusCall. + + case "write_result", "exit_early": + // These calls are handled on CaptureStylusExit to also cover the normal exit case. + + case "user_entrypoint", "user_returned", "msg_reentrant", "pay_for_memory_grow", "console_log_text", "console_log": + // No EVM counterpart + + default: + log.Warn("unhandled hostio trace", "name", name) + } +} + +func (info *TracingInfo) CaptureStylusCall(opCode vm.OpCode, contract common.Address, value *uint256.Int, input []byte, gas, startGas, baseCost uint64) { + var stack [][]byte + stack = append(stack, intToBytes(gas)) // gas + stack = append(stack, contract.Bytes()) // address + if opCode == vm.CALL { + stack = append(stack, value.Bytes()) // call value + } + stack = append(stack, []byte(nil)) // memory offset + stack = append(stack, lenToBytes(input)) // memory length + stack = append(stack, []byte(nil)) // return offset + stack = append(stack, []byte(nil)) // return size + info.captureState(opCode, startGas, baseCost+gas, input, stack...) +} + +func (info *TracingInfo) CaptureStylusExit(status uint8, data []byte, err error, gas uint64) { + var opCode vm.OpCode + if status == 0 { + if len(data) == 0 { + info.captureState(vm.STOP, gas, 0, nil) + return + } + opCode = vm.RETURN + } else { + opCode = vm.REVERT + if data == nil { + data = []byte(err.Error()) + } + } + offset := []byte(nil) + size := lenToBytes(data) + info.captureState(opCode, gas, 0, data, offset, size) +} + +func (info *TracingInfo) captureState(op vm.OpCode, gas uint64, cost uint64, memory []byte, stackValues ...[]byte) { + stack := []uint256.Int{} + for _, value := range stackValues { + stack = append(stack, *uint256.NewInt(0).SetBytes(value)) + } + scope := &vm.ScopeContext{ + Memory: TracingMemoryFromBytes(memory), + Stack: TracingStackFromArgs(stack...), + Contract: info.Contract, + } + info.Tracer.CaptureState(0, op, gas, cost, scope, []byte{}, info.Depth, nil) +} + +func lenToBytes(data []byte) []byte { + return intToBytes(uint64(len(data))) +} + +func intToBytes(v uint64) []byte { + return binary.BigEndian.AppendUint64(nil, v) +} + func HashToUint256(hash common.Hash) uint256.Int { value := uint256.Int{} value.SetBytes(hash.Bytes()) diff --git a/arbstate/daprovider/util.go b/arbstate/daprovider/util.go index 8f880b9228..d5a369bf3c 100644 --- a/arbstate/daprovider/util.go +++ b/arbstate/daprovider/util.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" "io" + "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" @@ -34,6 +35,8 @@ type DASWriter interface { fmt.Stringer } +var DefaultDASRetentionPeriod time.Duration = time.Hour * 24 * 15 + type DASKeysetFetcher interface { GetKeysetByHash(context.Context, common.Hash) ([]byte, error) } @@ -188,7 +191,7 @@ func RecoverPayloadFromDasBatch( keysetPreimage, err := keysetFetcher.GetKeysetByHash(ctx, cert.KeysetHash) if err != nil { - log.Error("Couldn't get keyset", "err", err) + log.Error("Couldn't get keyset", "err", err, "keysetHash", common.Bytes2Hex(cert.KeysetHash[:])) return nil, err } if preimageRecorder != nil { diff --git a/arbutil/wait_for_l1.go b/arbutil/wait_for_l1.go index eaa5d0790d..4b4819156d 100644 --- a/arbutil/wait_for_l1.go +++ b/arbutil/wait_for_l1.go @@ -19,12 +19,12 @@ import ( type L1Interface interface { bind.ContractBackend + bind.BlockHashContractCaller ethereum.ChainReader ethereum.ChainStateReader ethereum.TransactionReader TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) BlockNumber(ctx context.Context) (uint64, error) - CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) ChainID(ctx context.Context) (*big.Int, error) Client() rpc.ClientInterface diff --git a/audits/ConsenSys_Diligence_Arbitrum_Contracts_11_2021.pdf b/audits/ConsenSys_Diligence_Arbitrum_Contracts_11_2021.pdf deleted file mode 100644 index 4e93ced017..0000000000 Binary files a/audits/ConsenSys_Diligence_Arbitrum_Contracts_11_2021.pdf and /dev/null differ diff --git a/audits/ConsenSys_Diligence_Nitro_Contracts_5_2022.pdf b/audits/ConsenSys_Diligence_Nitro_Contracts_5_2022.pdf deleted file mode 100644 index 7fb9bc8f61..0000000000 Binary files a/audits/ConsenSys_Diligence_Nitro_Contracts_5_2022.pdf and /dev/null differ diff --git a/audits/Trail_Of_Bits_Nitro_10_2022.pdf b/audits/Trail_Of_Bits_Nitro_10_2022.pdf deleted file mode 100644 index 06a0516928..0000000000 Binary files a/audits/Trail_Of_Bits_Nitro_10_2022.pdf and /dev/null differ diff --git a/broadcastclient/broadcastclient.go b/broadcastclient/broadcastclient.go index 2225341560..7d27c57fe9 100644 --- a/broadcastclient/broadcastclient.go +++ b/broadcastclient/broadcastclient.go @@ -133,7 +133,7 @@ type BroadcastClient struct { connMutex sync.Mutex conn net.Conn - retryCount int64 + retryCount atomic.Int64 retrying bool shuttingDown bool @@ -435,7 +435,7 @@ func (bc *BroadcastClient) startBackgroundReader(earlyFrameData io.Reader) { } func (bc *BroadcastClient) GetRetryCount() int64 { - return atomic.LoadInt64(&bc.retryCount) + return bc.retryCount.Load() } func (bc *BroadcastClient) isShuttingDown() bool { @@ -458,7 +458,7 @@ func (bc *BroadcastClient) retryConnect(ctx context.Context) io.Reader { case <-timer.C: } - atomic.AddInt64(&bc.retryCount, 1) + bc.retryCount.Add(1) earlyFrameData, err := bc.connect(ctx, bc.nextSeqNum) if err == nil { bc.retrying = false diff --git a/broadcastclients/broadcastclients.go b/broadcastclients/broadcastclients.go index 23c4bd7738..8cd124bfe0 100644 --- a/broadcastclients/broadcastclients.go +++ b/broadcastclients/broadcastclients.go @@ -49,7 +49,7 @@ type BroadcastClients struct { secondaryRouter *Router // Use atomic access - connected int32 + connected atomic.Int32 } func NewBroadcastClients( @@ -113,7 +113,7 @@ func NewBroadcastClients( } func (bcs *BroadcastClients) adjustCount(delta int32) { - connected := atomic.AddInt32(&bcs.connected, delta) + connected := bcs.connected.Add(delta) if connected <= 0 { log.Error("no connected feed") } diff --git a/cmd/chaininfo/arbitrum_chain_info.json b/cmd/chaininfo/arbitrum_chain_info.json index 7d47d13e84..524433a7b5 100644 --- a/cmd/chaininfo/arbitrum_chain_info.json +++ b/cmd/chaininfo/arbitrum_chain_info.json @@ -164,7 +164,7 @@ "EnableArbOS": true, "AllowDebugPrecompiles": true, "DataAvailabilityCommittee": false, - "InitialArbOSVersion": 11, + "InitialArbOSVersion": 31, "InitialChainOwner": "0x0000000000000000000000000000000000000000", "GenesisBlockNum": 0 } @@ -196,7 +196,7 @@ "EnableArbOS": true, "AllowDebugPrecompiles": true, "DataAvailabilityCommittee": true, - "InitialArbOSVersion": 11, + "InitialArbOSVersion": 31, "InitialChainOwner": "0x0000000000000000000000000000000000000000", "GenesisBlockNum": 0 } diff --git a/cmd/conf/database.go b/cmd/conf/database.go index a75cca77d5..af18bacd57 100644 --- a/cmd/conf/database.go +++ b/cmd/conf/database.go @@ -43,7 +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. If set to empty string the database type will be autodetected and if no pre-existing database is found it will default to creating new pebble database ('leveldb', 'pebble' or '' = auto-detect)") - PebbleConfigAddOptions(prefix+".pebble", f) + PebbleConfigAddOptions(prefix+".pebble", f, &PersistentConfigDefault.Pebble) } func (c *PersistentConfig) ResolveDirectoryNames() error { @@ -120,9 +120,9 @@ var PebbleConfigDefault = PebbleConfig{ 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 PebbleConfigAddOptions(prefix string, f *flag.FlagSet, defaultConfig *PebbleConfig) { + f.Int(prefix+".max-concurrent-compactions", defaultConfig.MaxConcurrentCompactions, "maximum number of concurrent compactions") + PebbleExperimentalConfigAddOptions(prefix+".experimental", f, &defaultConfig.Experimental) } func (c *PebbleConfig) Validate() error { @@ -189,29 +189,29 @@ var PebbleExperimentalConfigDefault = PebbleExperimentalConfig{ 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") +func PebbleExperimentalConfigAddOptions(prefix string, f *flag.FlagSet, defaultConfig *PebbleExperimentalConfig) { + f.Int(prefix+".bytes-per-sync", defaultConfig.BytesPerSync, "number of bytes to write to a SSTable before calling Sync on it in the background") + f.Int(prefix+".l0-compaction-file-threshold", defaultConfig.L0CompactionFileThreshold, "count of L0 files necessary to trigger an L0 compaction") + f.Int(prefix+".l0-compaction-threshold", defaultConfig.L0CompactionThreshold, "amount of L0 read-amplification necessary to trigger an L0 compaction") + f.Int(prefix+".l0-stop-writes-threshold", defaultConfig.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", defaultConfig.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", defaultConfig.MemTableStopWritesThreshold, "hard limit on the number of queued of MemTables") + f.Bool(prefix+".disable-automatic-compactions", defaultConfig.DisableAutomaticCompactions, "disables automatic compactions") + f.Int(prefix+".wal-bytes-per-sync", defaultConfig.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", defaultConfig.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", defaultConfig.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", defaultConfig.TargetByteDeletionRate, "rate (in bytes per second) at which sstable file deletions are limited to (under normal circumstances).") + f.Int(prefix+".block-size", defaultConfig.BlockSize, "target uncompressed size in bytes of each table block") + f.Int(prefix+".index-block-size", defaultConfig.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", defaultConfig.TargetFileSize, "target file size for the level 0") + f.Bool(prefix+".target-file-size-equal-levels", defaultConfig.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.") + f.Int(prefix+".l0-compaction-concurrency", defaultConfig.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", defaultConfig.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", defaultConfig.ReadCompactionRate, "controls the frequency of read triggered compactions by adjusting `AllowedSeeks` in manifest.FileMetadata: AllowedSeeks = FileSize / ReadCompactionRate") + f.Int64(prefix+".read-sampling-multiplier", defaultConfig.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", defaultConfig.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", defaultConfig.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 { diff --git a/cmd/conf/init.go b/cmd/conf/init.go index 4bea00f9f2..d88bcdd241 100644 --- a/cmd/conf/init.go +++ b/cmd/conf/init.go @@ -7,7 +7,6 @@ import ( "time" "github.com/ethereum/go-ethereum/log" - "github.com/offchainlabs/nitro/execution/gethexec" "github.com/spf13/pflag" ) @@ -30,9 +29,11 @@ type InitConfig struct { PruneBloomSize uint64 `koanf:"prune-bloom-size"` PruneThreads int `koanf:"prune-threads"` PruneTrieCleanCache int `koanf:"prune-trie-clean-cache"` - ResetToMessage int64 `koanf:"reset-to-message"` RecreateMissingStateFrom uint64 `koanf:"recreate-missing-state-from"` RebuildLocalWasm bool `koanf:"rebuild-local-wasm"` + ReorgToBatch int64 `koanf:"reorg-to-batch"` + ReorgToMessageBatch int64 `koanf:"reorg-to-message-batch"` + ReorgToBlockBatch int64 `koanf:"reorg-to-block-batch"` } var InitConfigDefault = InitConfig{ @@ -53,10 +54,12 @@ var InitConfigDefault = InitConfig{ Prune: "", PruneBloomSize: 2048, PruneThreads: runtime.NumCPU(), - PruneTrieCleanCache: gethexec.DefaultCachingConfig.TrieCleanCache, - ResetToMessage: -1, + PruneTrieCleanCache: 600, RecreateMissingStateFrom: 0, // 0 = disabled RebuildLocalWasm: true, + ReorgToBatch: -1, + ReorgToMessageBatch: -1, + ReorgToBlockBatch: -1, } func InitConfigAddOptions(prefix string, f *pflag.FlagSet) { @@ -78,9 +81,11 @@ func InitConfigAddOptions(prefix string, f *pflag.FlagSet) { f.Uint64(prefix+".prune-bloom-size", InitConfigDefault.PruneBloomSize, "the amount of memory in megabytes to use for the pruning bloom filter (higher values prune better)") f.Int(prefix+".prune-threads", InitConfigDefault.PruneThreads, "the number of threads to use when pruning") f.Int(prefix+".prune-trie-clean-cache", InitConfigDefault.PruneTrieCleanCache, "amount of memory in megabytes to cache unchanged state trie nodes with when traversing state database during pruning") - f.Int64(prefix+".reset-to-message", InitConfigDefault.ResetToMessage, "forces a reset to an old message height. Also set max-reorg-resequence-depth=0 to force re-reading messages") f.Uint64(prefix+".recreate-missing-state-from", InitConfigDefault.RecreateMissingStateFrom, "block number to start recreating missing states from (0 = disabled)") f.Bool(prefix+".rebuild-local-wasm", InitConfigDefault.RebuildLocalWasm, "rebuild local wasm database on boot if needed (otherwise-will be done lazily)") + f.Int64(prefix+".reorg-to-batch", InitConfigDefault.ReorgToBatch, "rolls back the blockchain to a specified batch number") + f.Int64(prefix+".reorg-to-message-batch", InitConfigDefault.ReorgToMessageBatch, "rolls back the blockchain to the first batch at or before a given message index") + f.Int64(prefix+".reorg-to-block-batch", InitConfigDefault.ReorgToBlockBatch, "rolls back the blockchain to the first batch at or before a given block number") } func (c *InitConfig) Validate() error { @@ -96,9 +101,22 @@ func (c *InitConfig) Validate() error { if c.PruneTrieCleanCache < 0 { return fmt.Errorf("invalid trie clean cache size: %d, has to be greater or equal 0", c.PruneTrieCleanCache) } + numReorgOptionsSpecified := 0 + for _, reorgOption := range []int64{c.ReorgToBatch, c.ReorgToMessageBatch, c.ReorgToBlockBatch} { + if reorgOption >= 0 { + numReorgOptionsSpecified++ + if numReorgOptionsSpecified > 1 { + return fmt.Errorf("at most one init reorg option can be specified") + } + } + } return nil } +func (c *InitConfig) IsReorgRequested() bool { + return c.ReorgToBatch >= 0 || c.ReorgToBlockBatch >= 0 || c.ReorgToMessageBatch >= 0 +} + var ( acceptedSnapshotKinds = []string{"archive", "pruned", "genesis"} acceptedSnapshotKindsStr = "(accepted values: \"" + strings.Join(acceptedSnapshotKinds, "\" | \"") + "\")" diff --git a/cmd/dbconv/dbconv/config.go b/cmd/dbconv/dbconv/config.go new file mode 100644 index 0000000000..74623bc264 --- /dev/null +++ b/cmd/dbconv/dbconv/config.go @@ -0,0 +1,95 @@ +package dbconv + +import ( + "errors" + "fmt" + + "github.com/offchainlabs/nitro/cmd/conf" + "github.com/offchainlabs/nitro/cmd/genericconf" + flag "github.com/spf13/pflag" +) + +type DBConfig struct { + Data string `koanf:"data"` + DBEngine string `koanf:"db-engine"` + Handles int `koanf:"handles"` + Cache int `koanf:"cache"` + Namespace string `koanf:"namespace"` + Pebble conf.PebbleConfig `koanf:"pebble"` +} + +var DBConfigDefaultDst = DBConfig{ + DBEngine: "pebble", + Handles: conf.PersistentConfigDefault.Handles, + Cache: 2048, // 2048 MB + Namespace: "dstdb/", + Pebble: conf.PebbleConfigDefault, +} + +var DBConfigDefaultSrc = DBConfig{ + DBEngine: "leveldb", + Handles: conf.PersistentConfigDefault.Handles, + Cache: 2048, // 2048 MB + Namespace: "srcdb/", +} + +func DBConfigAddOptions(prefix string, f *flag.FlagSet, defaultConfig *DBConfig) { + f.String(prefix+".data", defaultConfig.Data, "directory of stored chain state") + f.String(prefix+".db-engine", defaultConfig.DBEngine, "backing database implementation to use ('leveldb' or 'pebble')") + f.Int(prefix+".handles", defaultConfig.Handles, "number of files to be open simultaneously") + f.Int(prefix+".cache", defaultConfig.Cache, "the capacity(in megabytes) of the data caching") + f.String(prefix+".namespace", defaultConfig.Namespace, "metrics namespace") + conf.PebbleConfigAddOptions(prefix+".pebble", f, &defaultConfig.Pebble) +} + +type DBConvConfig struct { + Src DBConfig `koanf:"src"` + Dst DBConfig `koanf:"dst"` + IdealBatchSize int `koanf:"ideal-batch-size"` + Convert bool `koanf:"convert"` + Compact bool `koanf:"compact"` + Verify string `koanf:"verify"` + LogLevel string `koanf:"log-level"` + LogType string `koanf:"log-type"` + Metrics bool `koanf:"metrics"` + MetricsServer genericconf.MetricsServerConfig `koanf:"metrics-server"` +} + +var DefaultDBConvConfig = DBConvConfig{ + Src: DBConfigDefaultSrc, + Dst: DBConfigDefaultDst, + IdealBatchSize: 100 * 1024 * 1024, // 100 MB + Convert: false, + Compact: false, + Verify: "", + LogLevel: "INFO", + LogType: "plaintext", + Metrics: false, + MetricsServer: genericconf.MetricsServerConfigDefault, +} + +func DBConvConfigAddOptions(f *flag.FlagSet) { + DBConfigAddOptions("src", f, &DefaultDBConvConfig.Src) + DBConfigAddOptions("dst", f, &DefaultDBConvConfig.Dst) + f.Int("ideal-batch-size", DefaultDBConvConfig.IdealBatchSize, "ideal write batch size") + f.Bool("convert", DefaultDBConvConfig.Convert, "enables conversion step") + f.Bool("compact", DefaultDBConvConfig.Compact, "enables compaction step") + f.String("verify", DefaultDBConvConfig.Verify, "enables verification step (\"\" = disabled, \"keys\" = only keys, \"full\" = keys and values)") + f.String("log-level", DefaultDBConvConfig.LogLevel, "log level, valid values are CRIT, ERROR, WARN, INFO, DEBUG, TRACE") + f.String("log-type", DefaultDBConvConfig.LogType, "log type (plaintext or json)") + f.Bool("metrics", DefaultDBConvConfig.Metrics, "enable metrics") + genericconf.MetricsServerAddOptions("metrics-server", f) +} + +func (c *DBConvConfig) Validate() error { + if c.Verify != "keys" && c.Verify != "full" && c.Verify != "" { + return fmt.Errorf("Invalid verify mode: %v", c.Verify) + } + if !c.Convert && c.Verify == "" && !c.Compact { + return errors.New("nothing to be done, conversion, verification and compaction disabled") + } + if c.IdealBatchSize <= 0 { + return fmt.Errorf("Invalid ideal batch size: %d, has to be greater then 0", c.IdealBatchSize) + } + return nil +} diff --git a/cmd/dbconv/dbconv/dbconv.go b/cmd/dbconv/dbconv/dbconv.go new file mode 100644 index 0000000000..6a97df31c0 --- /dev/null +++ b/cmd/dbconv/dbconv/dbconv.go @@ -0,0 +1,172 @@ +package dbconv + +import ( + "bytes" + "context" + "errors" + "fmt" + "time" + + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/util/dbutil" +) + +type DBConverter struct { + config *DBConvConfig + stats Stats +} + +func NewDBConverter(config *DBConvConfig) *DBConverter { + return &DBConverter{ + config: config, + } +} + +func openDB(config *DBConfig, name string, readonly bool) (ethdb.Database, error) { + db, err := rawdb.Open(rawdb.OpenOptions{ + Type: config.DBEngine, + Directory: config.Data, + // we don't open freezer, it doesn't need to be converted as it has format independent of db-engine + // note: user needs to handle copying/moving the ancient directory + AncientsDirectory: "", + Namespace: config.Namespace, + Cache: config.Cache, + Handles: config.Handles, + ReadOnly: readonly, + PebbleExtraOptions: config.Pebble.ExtraOptions(name), + }) + if err != nil { + return nil, err + } + if err := dbutil.UnfinishedConversionCheck(db); err != nil { + if closeErr := db.Close(); closeErr != nil { + err = errors.Join(err, closeErr) + } + return nil, err + } + + return db, nil +} + +func (c *DBConverter) Convert(ctx context.Context) error { + var err error + src, err := openDB(&c.config.Src, "src", true) + if err != nil { + return err + } + defer src.Close() + dst, err := openDB(&c.config.Dst, "dst", false) + if err != nil { + return err + } + defer dst.Close() + c.stats.Reset() + log.Info("Converting database", "src", c.config.Src.Data, "dst", c.config.Dst.Data, "db-engine", c.config.Dst.DBEngine) + if err = dbutil.PutUnfinishedConversionCanary(dst); err != nil { + return err + } + it := src.NewIterator(nil, nil) + defer it.Release() + batch := dst.NewBatch() + entriesInBatch := 0 + for it.Next() && ctx.Err() == nil { + if err = batch.Put(it.Key(), it.Value()); err != nil { + return err + } + entriesInBatch++ + if batchSize := batch.ValueSize(); batchSize >= c.config.IdealBatchSize { + if err = batch.Write(); err != nil { + return err + } + c.stats.LogEntries(int64(entriesInBatch)) + c.stats.LogBytes(int64(batchSize)) + batch.Reset() + entriesInBatch = 0 + } + } + if err = ctx.Err(); err == nil { + batchSize := batch.ValueSize() + if err = batch.Write(); err != nil { + return err + } + c.stats.LogEntries(int64(entriesInBatch)) + c.stats.LogBytes(int64(batchSize)) + } + if err == nil { + if err = dbutil.DeleteUnfinishedConversionCanary(dst); err != nil { + return err + } + } + return err +} + +func (c *DBConverter) CompactDestination() error { + dst, err := openDB(&c.config.Dst, "dst", false) + if err != nil { + return err + } + defer dst.Close() + start := time.Now() + log.Info("Compacting destination database", "dst", c.config.Dst.Data) + if err := dst.Compact(nil, nil); err != nil { + return err + } + log.Info("Compaction done", "elapsed", time.Since(start)) + return nil +} + +func (c *DBConverter) Verify(ctx context.Context) error { + if c.config.Verify == "keys" { + log.Info("Starting quick verification - verifying only keys existence") + } else if c.config.Verify == "full" { + log.Info("Starting full verification - verifying keys and values") + } + var err error + src, err := openDB(&c.config.Src, "src", true) + if err != nil { + return err + } + defer src.Close() + + dst, err := openDB(&c.config.Dst, "dst", true) + if err != nil { + return err + } + defer dst.Close() + + c.stats.Reset() + it := src.NewIterator(nil, nil) + defer it.Release() + for it.Next() && ctx.Err() == nil { + switch c.config.Verify { + case "keys": + has, err := dst.Has(it.Key()) + if err != nil { + return fmt.Errorf("Failed to check key existence in destination db, key: %v, err: %w", it.Key(), err) + } + if !has { + return fmt.Errorf("Missing key in destination db, key: %v", it.Key()) + } + c.stats.LogBytes(int64(len(it.Key()))) + case "full": + dstValue, err := dst.Get(it.Key()) + if err != nil { + return err + } + if !bytes.Equal(dstValue, it.Value()) { + return fmt.Errorf("Value mismatch for key: %v, src value: %v, dst value: %s", it.Key(), it.Value(), dstValue) + } + c.stats.LogBytes(int64(len(it.Key()) + len(dstValue))) + default: + return fmt.Errorf("Invalid verify config value: %v", c.config.Verify) + } + c.stats.LogEntries(1) + } + return ctx.Err() +} + +func (c *DBConverter) Stats() *Stats { + return &c.stats +} diff --git a/cmd/dbconv/dbconv/dbconv_test.go b/cmd/dbconv/dbconv/dbconv_test.go new file mode 100644 index 0000000000..f31dd68618 --- /dev/null +++ b/cmd/dbconv/dbconv/dbconv_test.go @@ -0,0 +1,72 @@ +package dbconv + +import ( + "context" + "testing" + + "github.com/ethereum/go-ethereum/log" + "github.com/offchainlabs/nitro/util/testhelpers" +) + +func TestConversion(t *testing.T) { + _ = testhelpers.InitTestLog(t, log.LvlTrace) + oldDBConfig := DBConfigDefaultSrc + oldDBConfig.Data = t.TempDir() + + newDBConfig := DBConfigDefaultDst + newDBConfig.Data = t.TempDir() + + func() { + oldDb, err := openDB(&oldDBConfig, "", false) + defer oldDb.Close() + Require(t, err) + err = oldDb.Put([]byte{}, []byte{0xde, 0xed, 0xbe, 0xef}) + Require(t, err) + for i := 0; i < 20; i++ { + err = oldDb.Put([]byte{byte(i)}, []byte{byte(i + 1)}) + Require(t, err) + } + }() + + config := DefaultDBConvConfig + config.Src = oldDBConfig + config.Dst = newDBConfig + config.IdealBatchSize = 5 + config.Verify = "full" + conv := NewDBConverter(&config) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + err := conv.Convert(ctx) + Require(t, err) + + err = conv.Verify(ctx) + Require(t, err) + + // check if new database doesn't have any extra keys + oldDb, err := openDB(&oldDBConfig, "", true) + Require(t, err) + defer oldDb.Close() + newDb, err := openDB(&newDBConfig, "", true) + Require(t, err) + defer newDb.Close() + it := newDb.NewIterator(nil, nil) + defer it.Release() + for it.Next() { + has, err := oldDb.Has(it.Key()) + Require(t, err) + if !has { + Fail(t, "Unexpected key in the converted db, key:", it.Key()) + } + } +} + +func Require(t *testing.T, err error, printables ...interface{}) { + t.Helper() + testhelpers.RequireImpl(t, err, printables...) +} + +func Fail(t *testing.T, printables ...interface{}) { + t.Helper() + testhelpers.FailImpl(t, printables...) +} diff --git a/cmd/dbconv/dbconv/stats.go b/cmd/dbconv/dbconv/stats.go new file mode 100644 index 0000000000..729a408f38 --- /dev/null +++ b/cmd/dbconv/dbconv/stats.go @@ -0,0 +1,96 @@ +package dbconv + +import ( + "sync/atomic" + "time" +) + +type Stats struct { + entries atomic.Int64 + bytes atomic.Int64 + + startTimestamp int64 + prevEntries int64 + prevBytes int64 + prevEntriesTimestamp int64 + prevBytesTimestamp int64 +} + +func (s *Stats) Reset() { + now := time.Now().UnixNano() + s.entries.Store(0) + s.bytes.Store(0) + s.startTimestamp = now + s.prevEntries = 0 + s.prevBytes = 0 + s.prevEntriesTimestamp = now + s.prevBytesTimestamp = now +} + +func (s *Stats) LogEntries(entries int64) { + s.entries.Add(entries) +} + +func (s *Stats) Entries() int64 { + return s.entries.Load() +} + +func (s *Stats) LogBytes(bytes int64) { + s.bytes.Add(bytes) +} + +func (s *Stats) Bytes() int64 { + return s.bytes.Load() +} + +func (s *Stats) Elapsed() time.Duration { + now := time.Now().UnixNano() + dt := now - s.startTimestamp + return time.Duration(dt) +} + +// not thread safe vs itself +func (s *Stats) EntriesPerSecond() float64 { + now := time.Now().UnixNano() + current := s.Entries() + dt := now - s.prevEntriesTimestamp + if dt == 0 { + dt = 1 + } + de := current - s.prevEntries + s.prevEntries = current + s.prevEntriesTimestamp = now + return float64(de) * 1e9 / float64(dt) +} + +// not thread safe vs itself +func (s *Stats) BytesPerSecond() float64 { + now := time.Now().UnixNano() + current := s.Bytes() + dt := now - s.prevBytesTimestamp + if dt == 0 { + dt = 1 + } + db := current - s.prevBytes + s.prevBytes = current + s.prevBytesTimestamp = now + return float64(db) * 1e9 / float64(dt) +} + +func (s *Stats) AverageEntriesPerSecond() float64 { + now := time.Now().UnixNano() + dt := now - s.startTimestamp + if dt == 0 { + dt = 1 + } + return float64(s.Entries()) * 1e9 / float64(dt) +} + +func (s *Stats) AverageBytesPerSecond() float64 { + now := time.Now().UnixNano() + dt := now - s.startTimestamp + if dt == 0 { + dt = 1 + } + return float64(s.Bytes()) * 1e9 / float64(dt) +} diff --git a/cmd/dbconv/main.go b/cmd/dbconv/main.go new file mode 100644 index 0000000000..c0b5c8f8e4 --- /dev/null +++ b/cmd/dbconv/main.go @@ -0,0 +1,110 @@ +package main + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/metrics" + "github.com/ethereum/go-ethereum/metrics/exp" + "github.com/offchainlabs/nitro/cmd/dbconv/dbconv" + "github.com/offchainlabs/nitro/cmd/genericconf" + "github.com/offchainlabs/nitro/cmd/util/confighelpers" + flag "github.com/spf13/pflag" +) + +func parseDBConv(args []string) (*dbconv.DBConvConfig, error) { + f := flag.NewFlagSet("dbconv", flag.ContinueOnError) + dbconv.DBConvConfigAddOptions(f) + k, err := confighelpers.BeginCommonParse(f, args) + if err != nil { + return nil, err + } + var config dbconv.DBConvConfig + if err := confighelpers.EndCommonParse(k, &config); err != nil { + return nil, err + } + return &config, config.Validate() +} + +func printSampleUsage(name string) { + fmt.Printf("Sample usage: %s --help \n\n", name) +} + +func printProgress(conv *dbconv.DBConverter) { + stats := conv.Stats() + fmt.Printf("Progress:\n") + fmt.Printf("\tprocessed entries: %d\n", stats.Entries()) + fmt.Printf("\tprocessed data (MB): %d\n", stats.Bytes()/1024/1024) + fmt.Printf("\telapsed:\t%v\n", stats.Elapsed()) + fmt.Printf("\tcurrent:\t%.3e entries/s\t%.3f MB/s\n", stats.EntriesPerSecond()/1000, stats.BytesPerSecond()/1024/1024) + fmt.Printf("\taverage:\t%.3e entries/s\t%.3f MB/s\n", stats.AverageEntriesPerSecond()/1000, stats.AverageBytesPerSecond()/1024/1024) +} + +func main() { + args := os.Args[1:] + config, err := parseDBConv(args) + if err != nil { + confighelpers.PrintErrorAndExit(err, printSampleUsage) + } + + err = genericconf.InitLog(config.LogType, config.LogLevel, &genericconf.FileLoggingConfig{Enable: false}, nil) + if err != nil { + fmt.Fprintf(os.Stderr, "Error initializing logging: %v\n", err) + os.Exit(1) + } + + if config.Metrics { + go metrics.CollectProcessMetrics(config.MetricsServer.UpdateInterval) + exp.Setup(fmt.Sprintf("%v:%v", config.MetricsServer.Addr, config.MetricsServer.Port)) + } + + conv := dbconv.NewDBConverter(config) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + ticker := time.NewTicker(10 * time.Second) + go func() { + defer ticker.Stop() + for { + select { + case <-ticker.C: + printProgress(conv) + case <-ctx.Done(): + return + } + } + }() + + if config.Convert { + err = conv.Convert(ctx) + if err != nil { + log.Error("Conversion error", "err", err) + os.Exit(1) + } + stats := conv.Stats() + log.Info("Conversion finished.", "entries", stats.Entries(), "MB", stats.Bytes()/1024/1024, "avg Ke/s", stats.AverageEntriesPerSecond()/1000, "avg MB/s", stats.AverageBytesPerSecond()/1024/1024, "elapsed", stats.Elapsed()) + } + + if config.Compact { + ticker.Stop() + err = conv.CompactDestination() + if err != nil { + log.Error("Compaction error", "err", err) + os.Exit(1) + } + } + + if config.Verify != "" { + ticker.Reset(10 * time.Second) + err = conv.Verify(ctx) + if err != nil { + log.Error("Verification error", "err", err) + os.Exit(1) + } + stats := conv.Stats() + log.Info("Verification completed successfully.", "elapsed", stats.Elapsed()) + } +} diff --git a/cmd/nitro-val/nitro_val.go b/cmd/nitro-val/nitro_val.go index 1a7d2e6283..3ff859c302 100644 --- a/cmd/nitro-val/nitro_val.go +++ b/cmd/nitro-val/nitro_val.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "math" _ "net/http/pprof" // #nosec G108 "os" "os/signal" @@ -66,6 +67,8 @@ func mainImpl() int { } stackConf := DefaultValidationNodeStackConfig stackConf.DataDir = "" // ephemeral + stackConf.HTTPBodyLimit = math.MaxInt + stackConf.WSReadLimit = math.MaxInt64 nodeConfig.HTTP.Apply(&stackConf) nodeConfig.WS.Apply(&stackConf) nodeConfig.Auth.Apply(&stackConf) diff --git a/cmd/nitro/config_test.go b/cmd/nitro/config_test.go index f94f941e0b..9626893083 100644 --- a/cmd/nitro/config_test.go +++ b/cmd/nitro/config_test.go @@ -53,12 +53,31 @@ func TestUnsafeStakerConfig(t *testing.T) { Require(t, err) } +const validatorArgs = "--persistent.chain /tmp/data --init.dev-init --node.parent-chain-reader.enable=false --parent-chain.id 5 --chain.id 421613 --node.staker.parent-chain-wallet.pathname /l1keystore --node.staker.parent-chain-wallet.password passphrase --http.addr 0.0.0.0 --ws.addr 0.0.0.0 --node.staker.enable --node.staker.strategy MakeNodes --node.staker.staker-interval 10s --execution.forwarding-target null" + func TestValidatorConfig(t *testing.T) { args := strings.Split("--persistent.chain /tmp/data --init.dev-init --node.parent-chain-reader.enable=false --parent-chain.id 5 --chain.id 421613 --node.staker.parent-chain-wallet.pathname /l1keystore --node.staker.parent-chain-wallet.password passphrase --http.addr 0.0.0.0 --ws.addr 0.0.0.0 --node.staker.enable --node.staker.strategy MakeNodes --node.staker.staker-interval 10s --execution.forwarding-target null", " ") _, _, err := ParseNode(context.Background(), args) Require(t, err) } +func TestInvalidCachingStateSchemeForValidator(t *testing.T) { + validatorArgsWithPathScheme := fmt.Sprintf("%s --execution.caching.state-scheme path", validatorArgs) + args := strings.Split(validatorArgsWithPathScheme, " ") + _, _, err := ParseNode(context.Background(), args) + if !strings.Contains(err.Error(), "path cannot be used as execution.caching.state-scheme when validator is required") { + Fail(t, "failed to detect invalid state scheme for validator") + } +} + +func TestInvalidArchiveConfig(t *testing.T) { + args := strings.Split("--execution.caching.archive --execution.caching.state-scheme path --persistent.chain /tmp/data --init.dev-init --node.parent-chain-reader.enable=false --parent-chain.id 5 --chain.id 421613 --node.staker.parent-chain-wallet.pathname /l1keystore --node.staker.parent-chain-wallet.password passphrase --http.addr 0.0.0.0 --ws.addr 0.0.0.0 --node.staker.enable --node.staker.strategy MakeNodes --node.staker.staker-interval 10s --execution.forwarding-target null", " ") + _, _, err := ParseNode(context.Background(), args) + if !strings.Contains(err.Error(), "archive cannot be set when using path as the state-scheme") { + Fail(t, "failed to detect invalid state scheme for archive") + } +} + func TestAggregatorConfig(t *testing.T) { args := strings.Split("--persistent.chain /tmp/data --init.dev-init --node.parent-chain-reader.enable=false --parent-chain.id 5 --chain.id 421613 --node.batch-poster.parent-chain-wallet.pathname /l1keystore --node.batch-poster.parent-chain-wallet.password passphrase --http.addr 0.0.0.0 --ws.addr 0.0.0.0 --node.sequencer --execution.sequencer.enable --node.feed.output.enable --node.feed.output.port 9642 --node.data-availability.enable --node.data-availability.rpc-aggregator.backends [{\"url\":\"http://localhost:8547\",\"pubkey\":\"abc==\"}]", " ") _, _, err := ParseNode(context.Background(), args) @@ -76,11 +95,11 @@ func TestReloads(t *testing.T) { hot := node.Type().Field(i).Tag.Get("reload") == "hot" dot := path + "." + node.Type().Field(i).Name if hot && cold { - t.Fatalf(fmt.Sprintf( + t.Fatalf( "Option %v%v%v is reloadable but %v%v%v is not", colors.Red, dot, colors.Clear, colors.Red, path, colors.Clear, - )) + ) } if hot { colors.PrintBlue(dot) diff --git a/cmd/nitro/init.go b/cmd/nitro/init.go index 8f6764ba26..5e74733ece 100644 --- a/cmd/nitro/init.go +++ b/cmd/nitro/init.go @@ -46,6 +46,7 @@ import ( "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/statetransfer" "github.com/offchainlabs/nitro/util/arbmath" + "github.com/offchainlabs/nitro/util/dbutil" ) var notFoundError = errors.New("file not found") @@ -300,6 +301,7 @@ func setLatestSnapshotUrl(ctx context.Context, initConfig *conf.InitConfig, chai return fmt.Errorf("failed to parse latest mirror \"%s\": %w", initConfig.LatestBase, err) } latestFileUrl := baseUrl.JoinPath(chain, "latest-"+initConfig.Latest+".txt").String() + latestFileUrl = strings.ToLower(latestFileUrl) latestFileBytes, err := httpGet(ctx, latestFileUrl) if err != nil { return fmt.Errorf("failed to get latest file at \"%s\": %w", latestFileUrl, err) @@ -311,6 +313,7 @@ func setLatestSnapshotUrl(ctx context.Context, initConfig *conf.InitConfig, chai } else { initConfig.Url = baseUrl.JoinPath(latestFile).String() } + initConfig.Url = strings.ToLower(initConfig.Url) log.Info("Set latest snapshot url", "url", initConfig.Url) return nil } @@ -396,14 +399,80 @@ func checkEmptyDatabaseDir(dir string, force bool) error { return nil } -var pebbleNotExistErrorRegex = regexp.MustCompile("pebble: database .* does not exist") +func databaseIsEmpty(db ethdb.Database) bool { + it := db.NewIterator(nil, nil) + defer it.Release() + return !it.Next() +} + +// removes all entries with keys prefixed with prefixes and of length used in initial version of wasm store schema +func purgeVersion0WasmStoreEntries(db ethdb.Database) error { + prefixes, keyLength := rawdb.DeprecatedPrefixesV0() + batch := db.NewBatch() + notMatchingLengthKeyLogged := false + for _, prefix := range prefixes { + it := db.NewIterator(prefix, nil) + defer it.Release() + for it.Next() { + key := it.Key() + if len(key) != keyLength { + if !notMatchingLengthKeyLogged { + log.Warn("Found key with deprecated prefix but not matching length, skipping removal. (this warning is logged only once)", "key", key) + notMatchingLengthKeyLogged = true + } + continue + } + if err := batch.Delete(key); err != nil { + return fmt.Errorf("Failed to remove key %v : %w", key, err) + } -func isPebbleNotExistError(err error) bool { - return pebbleNotExistErrorRegex.MatchString(err.Error()) + // Recreate the iterator after every batch commit in order + // to allow the underlying compactor to delete the entries. + if batch.ValueSize() >= ethdb.IdealBatchSize { + if err := batch.Write(); err != nil { + return fmt.Errorf("Failed to write batch: %w", err) + } + batch.Reset() + it.Release() + it = db.NewIterator(prefix, key) + } + } + } + if batch.ValueSize() > 0 { + if err := batch.Write(); err != nil { + return fmt.Errorf("Failed to write batch: %w", err) + } + batch.Reset() + } + return nil } -func isLeveldbNotExistError(err error) bool { - return os.IsNotExist(err) +// if db is not empty, validates if wasm database schema version matches current version +// otherwise persists current version +func validateOrUpgradeWasmStoreSchemaVersion(db ethdb.Database) error { + if !databaseIsEmpty(db) { + version, err := rawdb.ReadWasmSchemaVersion(db) + if err != nil { + if dbutil.IsErrNotFound(err) { + version = []byte{0} + } else { + return fmt.Errorf("Failed to retrieve wasm schema version: %w", err) + } + } + if len(version) != 1 || version[0] > rawdb.WasmSchemaVersion { + return fmt.Errorf("Unsupported wasm database schema version, current version: %v, read from wasm database: %v", rawdb.WasmSchemaVersion, version) + } + // special step for upgrading from version 0 - remove all entries added in version 0 + if version[0] == 0 { + log.Warn("Detected wasm store schema version 0 - removing all old wasm store entries") + if err := purgeVersion0WasmStoreEntries(db); err != nil { + return fmt.Errorf("Failed to purge wasm store version 0 entries: %w", err) + } + log.Info("Wasm store schama version 0 entries successfully removed.") + } + } + rawdb.WriteWasmSchemaVersion(db) + return nil } 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) { @@ -418,11 +487,24 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo if err != nil { return nil, nil, err } + if err := dbutil.UnfinishedConversionCheck(chainData); err != nil { + return nil, nil, fmt.Errorf("l2chaindata unfinished database conversion check error: %w", err) + } 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 } + if err := validateOrUpgradeWasmStoreSchemaVersion(wasmDb); err != nil { + return nil, nil, err + } + if err := dbutil.UnfinishedConversionCheck(wasmDb); err != nil { + return nil, nil, fmt.Errorf("wasm unfinished database conversion check error: %w", err) + } chainDb := rawdb.WrapDatabaseWithWasm(chainData, wasmDb, 1) + _, err = rawdb.ParseStateScheme(cacheConfig.StateScheme, chainDb) + if err != nil { + return nil, nil, err + } 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) @@ -468,7 +550,7 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo startBlockHash = latestBlock.Hash() } log.Info("Starting or continuing rebuilding of wasm store", "codeHash", position, "startBlockHash", startBlockHash) - if err := gethexec.RebuildWasmStore(ctx, wasmDb, chainDb, config.Execution.RPC.MaxRecreateStateDepth, l2BlockChain, position, startBlockHash); err != nil { + if err := gethexec.RebuildWasmStore(ctx, wasmDb, chainDb, config.Execution.RPC.MaxRecreateStateDepth, &config.Execution.StylusTarget, l2BlockChain, position, startBlockHash); err != nil { return nil, nil, fmt.Errorf("error rebuilding of wasm store: %w", err) } } @@ -476,8 +558,8 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo return chainDb, l2BlockChain, nil } readOnlyDb.Close() - } else if !isLeveldbNotExistError(err) && !isPebbleNotExistError(err) { - // we only want to continue if the error is pebble or leveldb not exist error + } else if !dbutil.IsNotExistError(err) { + // we only want to continue if the database does not exist return nil, nil, fmt.Errorf("Failed to open database: %w", err) } } @@ -532,7 +614,14 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo if err != nil { return nil, nil, err } + if err := validateOrUpgradeWasmStoreSchemaVersion(wasmDb); err != nil { + return nil, nil, err + } chainDb := rawdb.WrapDatabaseWithWasm(chainData, wasmDb, 1) + _, err = rawdb.ParseStateScheme(cacheConfig.StateScheme, chainDb) + if err != nil { + return nil, nil, err + } // Rebuilding wasm store is not required when just starting out err = gethexec.WriteToKeyValueStore(wasmDb, gethexec.RebuildingPositionKey, gethexec.RebuildingDone) @@ -673,6 +762,10 @@ func openInitializeChainDb(ctx context.Context, stack *node.Node, config *NodeCo log.Warn("Created fake init message as L1Reader is disabled and serialized chain config from init message is not available", "json", string(serializedChainConfig)) } + emptyBlockChain := rawdb.ReadHeadHeader(chainDb) == nil + if !emptyBlockChain && (cacheConfig.StateScheme == rawdb.PathScheme) && config.Init.Force { + return chainDb, nil, errors.New("It is not possible to force init with non-empty blockchain when using path scheme") + } l2BlockChain, err = gethexec.WriteOrTestBlockChain(chainDb, cacheConfig, initDataReader, chainConfig, parsedInitMessage, config.Execution.TxLookupLimit, config.Init.AccountsPerSync, tracer) if err != nil { return chainDb, nil, err diff --git a/cmd/nitro/init_test.go b/cmd/nitro/init_test.go index 6c363972e9..b2773ed861 100644 --- a/cmd/nitro/init_test.go +++ b/cmd/nitro/init_test.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 main @@ -10,6 +10,7 @@ import ( "encoding/hex" "errors" "fmt" + "math/big" "net" "net/http" "os" @@ -19,9 +20,16 @@ import ( "testing" "time" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/node" + "github.com/offchainlabs/nitro/arbnode" + "github.com/offchainlabs/nitro/cmd/chaininfo" "github.com/offchainlabs/nitro/cmd/conf" + "github.com/offchainlabs/nitro/execution/gethexec" "github.com/offchainlabs/nitro/util/testhelpers" + "github.com/offchainlabs/nitro/util/testhelpers/env" ) const ( @@ -201,6 +209,7 @@ func TestSetLatestSnapshotUrl(t *testing.T) { testCases := []struct { name string + chain string latestContents string wantUrl func(string) string }{ @@ -224,6 +233,12 @@ func TestSetLatestSnapshotUrl(t *testing.T) { latestContents: "https://some.domain.com/arb1/2024/21/archive.tar.gz", wantUrl: func(serverAddr string) string { return "https://some.domain.com/arb1/2024/21/archive.tar.gz" }, }, + { + name: "chain and contents with upper case", + chain: "ARB1", + latestContents: "ARB1/2024/21/ARCHIVE.TAR.GZ", + wantUrl: func(serverAddr string) string { return serverAddr + "/arb1/2024/21/archive.tar.gz" }, + }, } for _, testCase := range testCases { @@ -231,6 +246,7 @@ func TestSetLatestSnapshotUrl(t *testing.T) { // Create latest file serverDir := t.TempDir() + err := os.Mkdir(filepath.Join(serverDir, chain), dirPerm) Require(t, err) err = os.WriteFile(filepath.Join(serverDir, chain, latestFile), []byte(testCase.latestContents), filePerm) @@ -245,7 +261,11 @@ func TestSetLatestSnapshotUrl(t *testing.T) { initConfig := conf.InitConfigDefault initConfig.Latest = snapshotKind initConfig.LatestBase = addr - err = setLatestSnapshotUrl(ctx, &initConfig, chain) + configChain := testCase.chain + if configChain == "" { + configChain = chain + } + err = setLatestSnapshotUrl(ctx, &initConfig, configChain) Require(t, err) // Check url @@ -280,38 +300,6 @@ func startFileServer(t *testing.T, ctx context.Context, dir string) string { return addr } -func testIsNotExistError(t *testing.T, dbEngine string, isNotExist func(error) bool) { - stackConf := node.DefaultConfig - stackConf.DataDir = t.TempDir() - stackConf.DBEngine = dbEngine - stack, err := node.New(&stackConf) - if err != nil { - t.Fatalf("Failed to created test stack: %v", err) - } - defer stack.Close() - readonly := true - _, err = stack.OpenDatabaseWithExtraOptions("test", 16, 16, "", readonly, nil) - if err == nil { - t.Fatal("Opening non-existent database did not fail") - } - if !isNotExist(err) { - t.Fatalf("Failed to classify error as not exist error - internal implementation of OpenDatabaseWithExtraOptions might have changed, err: %v", err) - } - err = errors.New("some other error") - if isNotExist(err) { - t.Fatalf("Classified other error as not exist, err: %v", err) - } -} - -func TestIsNotExistError(t *testing.T) { - t.Run("TestIsPebbleNotExistError", func(t *testing.T) { - testIsNotExistError(t, "pebble", isPebbleNotExistError) - }) - t.Run("TestIsLeveldbNotExistError", func(t *testing.T) { - testIsNotExistError(t, "leveldb", isLeveldbNotExistError) - }) -} - func TestEmptyDatabaseDir(t *testing.T) { testCases := []struct { name string @@ -361,3 +349,208 @@ func TestEmptyDatabaseDir(t *testing.T) { }) } } + +func TestOpenInitializeChainDbIncompatibleStateScheme(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + stackConfig := testhelpers.CreateStackConfigForTest(t.TempDir()) + stack, err := node.New(stackConfig) + defer stack.Close() + Require(t, err) + + nodeConfig := NodeConfigDefault + nodeConfig.Execution.Caching.StateScheme = rawdb.PathScheme + nodeConfig.Chain.ID = 42161 + nodeConfig.Node = *arbnode.ConfigDefaultL2Test() + nodeConfig.Init.DevInit = true + nodeConfig.Init.DevInitAddress = "0x3f1Eae7D46d88F08fc2F8ed27FCb2AB183EB2d0E" + + l1Client := ethclient.NewClient(stack.Attach()) + + // opening for the first time doesn't error + chainDb, blockchain, err := openInitializeChainDb( + ctx, + stack, + &nodeConfig, + new(big.Int).SetUint64(nodeConfig.Chain.ID), + gethexec.DefaultCacheConfigFor(stack, &nodeConfig.Execution.Caching), + &nodeConfig.Persistent, + l1Client, + chaininfo.RollupAddresses{}, + ) + Require(t, err) + blockchain.Stop() + err = chainDb.Close() + Require(t, err) + + // opening for the second time doesn't error + chainDb, blockchain, err = openInitializeChainDb( + ctx, + stack, + &nodeConfig, + new(big.Int).SetUint64(nodeConfig.Chain.ID), + gethexec.DefaultCacheConfigFor(stack, &nodeConfig.Execution.Caching), + &nodeConfig.Persistent, + l1Client, + chaininfo.RollupAddresses{}, + ) + Require(t, err) + blockchain.Stop() + err = chainDb.Close() + Require(t, err) + + // opening with a different state scheme errors + nodeConfig.Execution.Caching.StateScheme = rawdb.HashScheme + _, _, err = openInitializeChainDb( + ctx, + stack, + &nodeConfig, + new(big.Int).SetUint64(nodeConfig.Chain.ID), + gethexec.DefaultCacheConfigFor(stack, &nodeConfig.Execution.Caching), + &nodeConfig.Persistent, + l1Client, + chaininfo.RollupAddresses{}, + ) + if !strings.Contains(err.Error(), "incompatible state scheme, stored: path, provided: hash") { + t.Fatalf("Failed to detect incompatible state scheme") + } +} + +func writeKeys(t *testing.T, db ethdb.Database, keys [][]byte) { + t.Helper() + batch := db.NewBatch() + for _, key := range keys { + err := batch.Put(key, []byte("some data")) + if err != nil { + t.Fatal("Internal test error - failed to insert key:", err) + } + } + err := batch.Write() + if err != nil { + t.Fatal("Internal test error - failed to write batch:", err) + } + batch.Reset() +} + +func checkKeys(t *testing.T, db ethdb.Database, keys [][]byte, shouldExist bool) { + t.Helper() + for _, key := range keys { + has, err := db.Has(key) + if err != nil { + t.Fatal("Failed to check key existence, key: ", key) + } + if shouldExist && !has { + t.Fatal("Key not found:", key) + } + if !shouldExist && has { + t.Fatal("Key found:", key, "k3:", string(key[:3]), "len", len(key)) + } + } +} + +func TestPurgeVersion0WasmStoreEntries(t *testing.T) { + stackConf := node.DefaultConfig + stackConf.DataDir = t.TempDir() + stack, err := node.New(&stackConf) + if err != nil { + t.Fatalf("Failed to create test stack: %v", err) + } + defer stack.Close() + db, err := stack.OpenDatabaseWithExtraOptions("wasm", NodeConfigDefault.Execution.Caching.DatabaseCache, NodeConfigDefault.Persistent.Handles, "wasm/", false, nil) + if err != nil { + t.Fatalf("Failed to open test db: %v", err) + } + var version0Keys [][]byte + for i := 0; i < 20; i++ { + version0Keys = append(version0Keys, + append([]byte{0x00, 'w', 'a'}, testhelpers.RandomSlice(32)...)) + version0Keys = append(version0Keys, + append([]byte{0x00, 'w', 'm'}, testhelpers.RandomSlice(32)...)) + } + var collidedKeys [][]byte + for i := 0; i < 5; i++ { + collidedKeys = append(collidedKeys, + append([]byte{0x00, 'w', 'a'}, testhelpers.RandomSlice(31)...)) + collidedKeys = append(collidedKeys, + append([]byte{0x00, 'w', 'm'}, testhelpers.RandomSlice(31)...)) + collidedKeys = append(collidedKeys, + append([]byte{0x00, 'w', 'a'}, testhelpers.RandomSlice(33)...)) + collidedKeys = append(collidedKeys, + append([]byte{0x00, 'w', 'm'}, testhelpers.RandomSlice(33)...)) + } + var otherKeys [][]byte + for i := 0x00; i <= 0xff; i++ { + if byte(i) == 'a' || byte(i) == 'm' { + continue + } + otherKeys = append(otherKeys, + append([]byte{0x00, 'w', byte(i)}, testhelpers.RandomSlice(32)...)) + otherKeys = append(otherKeys, + append([]byte{0x00, 'w', byte(i)}, testhelpers.RandomSlice(32)...)) + } + for i := 0; i < 10; i++ { + var randomSlice []byte + var j int + for j = 0; j < 10; j++ { + randomSlice = testhelpers.RandomSlice(testhelpers.RandomUint64(1, 40)) + if len(randomSlice) >= 3 && !bytes.Equal(randomSlice[:3], []byte{0x00, 'w', 'm'}) && !bytes.Equal(randomSlice[:3], []byte{0x00, 'w', 'm'}) { + break + } + } + if j == 10 { + t.Fatal("Internal test error - failed to generate random key") + } + otherKeys = append(otherKeys, randomSlice) + } + writeKeys(t, db, version0Keys) + writeKeys(t, db, collidedKeys) + writeKeys(t, db, otherKeys) + checkKeys(t, db, version0Keys, true) + checkKeys(t, db, collidedKeys, true) + checkKeys(t, db, otherKeys, true) + err = purgeVersion0WasmStoreEntries(db) + if err != nil { + t.Fatal("Failed to purge version 0 keys, err:", err) + } + checkKeys(t, db, version0Keys, false) + checkKeys(t, db, collidedKeys, true) + checkKeys(t, db, otherKeys, true) +} + +func TestOpenInitializeChainDbEmptyInit(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + stackConfig := testhelpers.CreateStackConfigForTest(t.TempDir()) + stack, err := node.New(stackConfig) + defer stack.Close() + Require(t, err) + + nodeConfig := NodeConfigDefault + nodeConfig.Execution.Caching.StateScheme = env.GetTestStateScheme() + nodeConfig.Chain.ID = 42161 + nodeConfig.Node = *arbnode.ConfigDefaultL2Test() + nodeConfig.Init.Empty = true + + l1Client := ethclient.NewClient(stack.Attach()) + + chainDb, blockchain, err := openInitializeChainDb( + ctx, + stack, + &nodeConfig, + new(big.Int).SetUint64(nodeConfig.Chain.ID), + gethexec.DefaultCacheConfigFor(stack, &nodeConfig.Execution.Caching), + &nodeConfig.Persistent, + l1Client, + chaininfo.RollupAddresses{}, + ) + Require(t, err) + blockchain.Stop() + err = chainDb.Close() + Require(t, err) +} diff --git a/cmd/nitro/nitro.go b/cmd/nitro/nitro.go index 943efc479d..98ee7ec721 100644 --- a/cmd/nitro/nitro.go +++ b/cmd/nitro/nitro.go @@ -31,6 +31,7 @@ import ( "github.com/ethereum/go-ethereum/arbitrum" "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/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/eth/tracers" @@ -42,6 +43,7 @@ import ( "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/metrics/exp" "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/params" "github.com/offchainlabs/nitro/arbnode" "github.com/offchainlabs/nitro/arbnode/resourcemanager" @@ -62,6 +64,7 @@ import ( "github.com/offchainlabs/nitro/staker" "github.com/offchainlabs/nitro/staker/validatorwallet" "github.com/offchainlabs/nitro/util/colors" + "github.com/offchainlabs/nitro/util/dbutil" "github.com/offchainlabs/nitro/util/headerreader" "github.com/offchainlabs/nitro/util/iostat" "github.com/offchainlabs/nitro/util/rpcclient" @@ -237,6 +240,10 @@ func mainImpl() int { if nodeConfig.Execution.Sequencer.Enable != nodeConfig.Node.Sequencer { log.Error("consensus and execution must agree if sequencing is enabled or not", "Execution.Sequencer.Enable", nodeConfig.Execution.Sequencer.Enable, "Node.Sequencer", nodeConfig.Node.Sequencer) } + if nodeConfig.Node.SeqCoordinator.Enable && !nodeConfig.Node.ParentChainReader.Enable { + log.Error("Sequencer coordinator must be enabled with parent chain reader, try starting node with --parent-chain.connection.url") + return 1 + } var dataSigner signature.DataSignerFunc var l1TransactionOptsValidator *bind.TransactOpts @@ -374,11 +381,6 @@ func mainImpl() int { return 0 } - if nodeConfig.Execution.Caching.Archive && nodeConfig.Execution.TxLookupLimit != 0 { - log.Info("retaining ability to lookup full transaction history as archive mode is enabled") - nodeConfig.Execution.TxLookupLimit = 0 - } - if err := resourcemanager.Init(&nodeConfig.Node.ResourceMgmt); err != nil { flag.Usage() log.Crit("Failed to start resource management module", "err", err) @@ -503,6 +505,10 @@ func mainImpl() int { log.Error("database is corrupt; delete it and try again", "database-directory", stack.InstanceDir()) return 1 } + if err := dbutil.UnfinishedConversionCheck(arbDb); err != nil { + log.Error("arbitrumdata unfinished conversion check error", "err", err) + return 1 + } fatalErrChan := make(chan error, 10) @@ -523,7 +529,7 @@ func mainImpl() int { } } - if nodeConfig.Init.ThenQuit && nodeConfig.Init.ResetToMessage < 0 { + if nodeConfig.Init.ThenQuit && !nodeConfig.Init.IsReorgRequested() { return 0 } @@ -680,29 +686,34 @@ func mainImpl() int { sigint := make(chan os.Signal, 1) signal.Notify(sigint, os.Interrupt, syscall.SIGTERM) - exitCode := 0 - - if err == nil && nodeConfig.Init.ResetToMessage > 0 { - err = currentNode.TxStreamer.ReorgTo(arbutil.MessageIndex(nodeConfig.Init.ResetToMessage)) + if err == nil && nodeConfig.Init.IsReorgRequested() { + err = initReorg(nodeConfig.Init, chainInfo.ChainConfig, currentNode.InboxTracker) if err != nil { - fatalErrChan <- fmt.Errorf("error reseting message: %w", err) - exitCode = 1 - } - if nodeConfig.Init.ThenQuit { - return exitCode + fatalErrChan <- fmt.Errorf("error reorging per init config: %w", err) + } else if nodeConfig.Init.ThenQuit { + return 0 } } + err = nil select { - case err := <-fatalErrChan: + case err = <-fatalErrChan: + case <-sigint: + // If there was both a sigint and a fatal error, we want to log the fatal error + select { + case err = <-fatalErrChan: + default: + log.Info("shutting down because of sigint") + } + } + + if err != nil { log.Error("shutting down due to fatal error", "err", err) defer log.Error("shut down due to fatal error", "err", err) - exitCode = 1 - case <-sigint: - log.Info("shutting down because of sigint") + return 1 } - return exitCode + return 0 } type NodeConfig struct { @@ -848,6 +859,9 @@ func (c *NodeConfig) Validate() error { if err := c.BlocksReExecutor.Validate(); err != nil { return err } + if c.Node.ValidatorRequired() && (c.Execution.Caching.StateScheme == rawdb.PathScheme) { + return errors.New("path cannot be used as execution.caching.state-scheme when validator is required") + } return c.Persistent.Validate() } @@ -893,10 +907,12 @@ func ParseNode(ctx context.Context, args []string) (*NodeConfig, *genericconf.Wa // Don't print wallet passwords if nodeConfig.Conf.Dump { err = confighelpers.DumpConfig(k, map[string]interface{}{ - "parent-chain.wallet.password": "", - "parent-chain.wallet.private-key": "", - "chain.dev-wallet.password": "", - "chain.dev-wallet.private-key": "", + "node.batch-poster.parent-chain-wallet.password": "", + "node.batch-poster.parent-chain-wallet.private-key": "", + "node.staker.parent-chain-wallet.password": "", + "node.staker.parent-chain-wallet.private-key": "", + "chain.dev-wallet.password": "", + "chain.dev-wallet.private-key": "", }) if err != nil { return nil, nil, err @@ -919,6 +935,12 @@ func ParseNode(ctx context.Context, args []string) (*NodeConfig, *genericconf.Wa if nodeConfig.Execution.Caching.Archive { nodeConfig.Node.MessagePruner.Enable = false } + + if nodeConfig.Execution.Caching.Archive && nodeConfig.Execution.TxLookupLimit != 0 { + log.Info("retaining ability to lookup full transaction history as archive mode is enabled") + nodeConfig.Execution.TxLookupLimit = 0 + } + err = nodeConfig.Validate() if err != nil { return nil, nil, err @@ -1005,6 +1027,39 @@ func applyChainParameters(ctx context.Context, k *koanf.Koanf, chainId uint64, c return nil } +func initReorg(initConfig conf.InitConfig, chainConfig *params.ChainConfig, inboxTracker *arbnode.InboxTracker) error { + var batchCount uint64 + if initConfig.ReorgToBatch >= 0 { + batchCount = uint64(initConfig.ReorgToBatch) + 1 + } else { + var messageIndex arbutil.MessageIndex + if initConfig.ReorgToMessageBatch >= 0 { + messageIndex = arbutil.MessageIndex(initConfig.ReorgToMessageBatch) + } else if initConfig.ReorgToBlockBatch > 0 { + genesis := chainConfig.ArbitrumChainParams.GenesisBlockNum + blockNum := uint64(initConfig.ReorgToBlockBatch) + if blockNum < genesis { + return fmt.Errorf("ReorgToBlockBatch %d before genesis %d", blockNum, genesis) + } + messageIndex = arbutil.MessageIndex(blockNum - genesis) + } else { + log.Warn("Tried to do init reorg, but no init reorg options specified") + return nil + } + // Reorg out the batch containing the next message + var missing bool + var err error + batchCount, missing, err = inboxTracker.FindInboxBatchContainingMessage(messageIndex + 1) + if err != nil { + return err + } + if missing { + return fmt.Errorf("cannot reorg to unknown message index %v", messageIndex) + } + } + return inboxTracker.ReorgBatchesTo(batchCount) +} + type NodeConfigFetcher struct { *genericconf.LiveConfig[*NodeConfig] } diff --git a/cmd/pruning/pruning.go b/cmd/pruning/pruning.go index ab6ec80942..096bb4b1ae 100644 --- a/cmd/pruning/pruning.go +++ b/cmd/pruning/pruning.go @@ -233,6 +233,10 @@ func findImportantRoots(ctx context.Context, chainDb ethdb.Database, stack *node } 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 cacheConfig.StateScheme == rawdb.PathScheme { + return nil + } + if initConfig.Prune == "" { return pruner.RecoverPruning(stack.InstanceDir(), chainDb, initConfig.PruneThreads) } diff --git a/cmd/replay/main.go b/cmd/replay/main.go index 484c55ab92..3262dd6e24 100644 --- a/cmd/replay/main.go +++ b/cmd/replay/main.go @@ -203,6 +203,13 @@ func main() { panic(fmt.Sprintf("Error opening state db: %v", err.Error())) } + batchFetcher := func(batchNum uint64) ([]byte, error) { + currentBatch := wavmio.GetInboxPosition() + if batchNum > currentBatch { + return nil, fmt.Errorf("invalid batch fetch request %d, max %d", batchNum, currentBatch) + } + return wavmio.ReadInboxMessage(batchNum), nil + } readMessage := func(dasEnabled bool) *arbostypes.MessageWithMetadata { var delayedMessagesRead uint64 if lastBlockHeader != nil { @@ -232,6 +239,10 @@ func main() { panic(fmt.Sprintf("Error reading from inbox multiplexer: %v", err.Error())) } + err = message.Message.FillInBatchGasCost(batchFetcher) + if err != nil { + message.Message = arbostypes.InvalidL1Message + } return message } @@ -280,14 +291,11 @@ func main() { message := readMessage(chainConfig.ArbitrumChainParams.DataAvailabilityCommittee) chainContext := WavmChainContext{} - batchFetcher := func(batchNum uint64) ([]byte, error) { - return wavmio.ReadInboxMessage(batchNum), nil - } - newBlock, _, err = arbos.ProduceBlock(message.Message, message.DelayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, batchFetcher, false, nil) + + newBlock, _, err = arbos.ProduceBlock(message.Message, message.DelayedMessagesRead, lastBlockHeader, statedb, chainContext, chainConfig, false, nil) if err != nil { panic(err) } - } else { // Initialize ArbOS with this init message and create the genesis block. diff --git a/cmd/staterecovery/staterecovery.go b/cmd/staterecovery/staterecovery.go index 58ad06ad14..bb01477414 100644 --- a/cmd/staterecovery/staterecovery.go +++ b/cmd/staterecovery/staterecovery.go @@ -5,12 +5,13 @@ import ( "time" "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/ethdb" "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/trie" - "github.com/ethereum/go-ethereum/trie/triedb/hashdb" + "github.com/ethereum/go-ethereum/triedb" + "github.com/ethereum/go-ethereum/triedb/hashdb" ) func RecreateMissingStates(chainDb ethdb.Database, bc *core.BlockChain, cacheConfig *core.CacheConfig, startBlock uint64) error { @@ -30,12 +31,18 @@ func RecreateMissingStates(chainDb ethdb.Database, bc *core.BlockChain, cacheCon if previousBlock == nil { return fmt.Errorf("start block parent is missing, parent block number: %d", current-1) } + + if cacheConfig.StateScheme == rawdb.PathScheme { + return fmt.Errorf("recreating missing states, which is supposed to only run in archive mode, is not supported with path scheme") + } + hashConfig := *hashdb.Defaults hashConfig.CleanCacheSize = cacheConfig.TrieCleanLimit * 1024 * 1024 - trieConfig := &trie.Config{ + trieConfig := &triedb.Config{ Preimages: false, HashDB: &hashConfig, } + database := state.NewDatabaseWithConfig(chainDb, trieConfig) defer database.TrieDB().Close() previousState, err := state.New(previousBlock.Root(), database, nil) diff --git a/cmd/util/confighelpers/configuration.go b/cmd/util/confighelpers/configuration.go index ff33da6732..19b5b1a24c 100644 --- a/cmd/util/confighelpers/configuration.go +++ b/cmd/util/confighelpers/configuration.go @@ -7,11 +7,12 @@ import ( "errors" "fmt" "os" + "reflect" "strings" + "time" "github.com/knadh/koanf" "github.com/knadh/koanf/parsers/json" - koanfjson "github.com/knadh/koanf/parsers/json" "github.com/knadh/koanf/providers/confmap" "github.com/knadh/koanf/providers/env" "github.com/knadh/koanf/providers/file" @@ -98,19 +99,24 @@ var envvarsToSplitOnComma map[string]any = map[string]any{ "chain.info-files": struct{}{}, "conf.file": struct{}{}, "execution.secondary-forwarding-target": struct{}{}, + "execution.sequencer.sender-whitelist": struct{}{}, "graphql.corsdomain": struct{}{}, "graphql.vhosts": struct{}{}, "http.api": struct{}{}, "http.corsdomain": struct{}{}, "http.vhosts": struct{}{}, - "node.data-availability.rest-aggregator.urls": struct{}{}, - "node.feed.input.secondary-url": struct{}{}, - "node.feed.input.url": struct{}{}, - "node.feed.input.verify.allowed-addresses": struct{}{}, - "node.seq-coordinator.signer.ecdsa.allowed-addresses": struct{}{}, - "p2p.bootnodes": struct{}{}, - "p2p.bootnodes-v5": struct{}{}, - "validation.api-auth": struct{}{}, + "node.batch-poster.data-poster.blob-tx-replacement-times": time.Duration(0), + "node.batch-poster.data-poster.replacement-times": time.Duration(0), + "node.data-availability.rest-aggregator.urls": struct{}{}, + "node.feed.input.secondary-url": struct{}{}, + "node.feed.input.url": struct{}{}, + "node.feed.input.verify.allowed-addresses": struct{}{}, + "node.seq-coordinator.signer.ecdsa.allowed-addresses": struct{}{}, + "node.staker.batch-poster.data-poster.blob-tx-replacement-times": time.Duration(0), + "node.staker.batch-poster.data-poster.replacement-times": time.Duration(0), + "p2p.bootnodes": struct{}{}, + "p2p.bootnodes-v5": struct{}{}, + "validation.api-auth": struct{}{}, "validation.arbitrator.redis-validation-server-config.module-roots": struct{}{}, "validation.wasm.allowed-wasm-module-roots": struct{}{}, "ws.api": struct{}{}, @@ -126,8 +132,22 @@ func loadEnvironmentVariables(k *koanf.Koanf) error { strings.TrimPrefix(key, envPrefix+"_")), "__", "-") key = strings.ReplaceAll(key, "_", ".") - if _, found := envvarsToSplitOnComma[key]; found { + if value, found := envvarsToSplitOnComma[key]; found { // If there are commas in the value, split the value into a slice. + if _, ok := value.(time.Duration); ok { + // Special case for time.Duration + // v[1:len(v)-1] removes the '[' , ']' around the string + durationStrings := strings.Split(v[1:len(v)-1], ",") + var durations []time.Duration + for _, durationString := range durationStrings { + duration, err := time.ParseDuration(durationString) + if err != nil { + return key, nil + } + durations = append(durations, duration) + } + return key, durations + } if strings.Contains(v, ",") { return key, strings.Split(v, ",") @@ -194,15 +214,17 @@ func devFlagArgs() []string { } func BeginCommonParse(f *flag.FlagSet, args []string) (*koanf.Koanf, error) { + var expandedArgs []string for _, arg := range args { if arg == "--version" || arg == "-v" { return nil, ErrVersion } else if arg == "--dev" { - args = devFlagArgs() - break + expandedArgs = append(expandedArgs, devFlagArgs()...) + } else { + expandedArgs = append(expandedArgs, arg) } } - if err := f.Parse(args); err != nil { + if err := f.Parse(expandedArgs); err != nil { return nil, err } @@ -227,6 +249,7 @@ func EndCommonParse(k *koanf.Koanf, config interface{}) error { // Default values DecodeHook: mapstructure.ComposeDecodeHookFunc( + stringToSliceDurationHookFunc(","), mapstructure.StringToTimeDurationHookFunc()), Metadata: nil, Result: config, @@ -240,6 +263,36 @@ func EndCommonParse(k *koanf.Koanf, config interface{}) error { return nil } +func stringToSliceDurationHookFunc(sep string) mapstructure.DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf([]time.Duration{}) { + return data, nil + } + + raw, _ := data.(string) + if raw == "" { + return []time.Duration{}, nil + } + // raw[1:len(raw)-1] removes the '[' , ']' around the string + durationStrings := strings.Split(raw[1:len(raw)-1], sep) + var durations []time.Duration + for _, durationString := range durationStrings { + duration, err := time.ParseDuration(durationString) + if err != nil { + return nil, err + } + durations = append(durations, duration) + } + return durations, nil + } +} + func DumpConfig(k *koanf.Koanf, extraOverrideFields map[string]interface{}) error { overrideFields := map[string]interface{}{"conf.dump": false} @@ -253,7 +306,7 @@ func DumpConfig(k *koanf.Koanf, extraOverrideFields map[string]interface{}) erro return fmt.Errorf("error removing extra parameters before dump: %w", err) } - c, err := k.Marshal(koanfjson.Parser()) + c, err := k.Marshal(json.Parser()) if err != nil { return fmt.Errorf("unable to marshal config file to JSON: %w", err) } diff --git a/contracts b/contracts index 61204dd455..f7894d3a6d 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit 61204dd455966cb678192427a07aa9795ff91c14 +Subproject commit f7894d3a6d4035ba60f51a7f1334f0f2d4f02dce diff --git a/das/aggregator.go b/das/aggregator.go index 9aa558b92c..d944f8d48a 100644 --- a/das/aggregator.go +++ b/das/aggregator.go @@ -193,11 +193,7 @@ func (a *Aggregator) Store(ctx context.Context, message []byte, timeout uint64) cert, err := d.service.Store(storeCtx, message, timeout) if err != nil { incFailureMetric() - if errors.Is(err, context.DeadlineExceeded) { - metrics.GetOrRegisterCounter(metricWithServiceName+"/error/timeout/total", nil).Inc(1) - } else { - metrics.GetOrRegisterCounter(metricWithServiceName+"/error/client/total", nil).Inc(1) - } + log.Warn("DAS Aggregator failed to store batch to backend", "backend", d.metricName, "err", err) responses <- storeResponse{d, nil, err} return } @@ -207,13 +203,13 @@ func (a *Aggregator) Store(ctx context.Context, message []byte, timeout uint64) ) if err != nil { incFailureMetric() - metrics.GetOrRegisterCounter(metricWithServiceName+"/error/bad_response/total", nil).Inc(1) + log.Warn("DAS Aggregator couldn't parse backend's store response signature", "backend", d.metricName, "err", err) responses <- storeResponse{d, nil, err} return } if !verified { incFailureMetric() - metrics.GetOrRegisterCounter(metricWithServiceName+"/error/bad_response/total", nil).Inc(1) + log.Warn("DAS Aggregator failed to verify backend's store response signature", "backend", d.metricName, "err", err) responses <- storeResponse{d, nil, errors.New("signature verification failed")} return } @@ -222,13 +218,13 @@ func (a *Aggregator) Store(ctx context.Context, message []byte, timeout uint64) if cert.DataHash != expectedHash { incFailureMetric() - metrics.GetOrRegisterCounter(metricWithServiceName+"/error/bad_response/total", nil).Inc(1) + log.Warn("DAS Aggregator got a store response with a data hash not matching the expected hash", "backend", d.metricName, "dataHash", cert.DataHash, "expectedHash", expectedHash, "err", err) responses <- storeResponse{d, nil, errors.New("hash verification failed")} return } if cert.Timeout != timeout { incFailureMetric() - metrics.GetOrRegisterCounter(metricWithServiceName+"/error/bad_response/total", nil).Inc(1) + log.Warn("DAS Aggregator got a store response with any expiry time not matching the expected expiry time", "backend", d.metricName, "dataHash", cert.DataHash, "expectedHash", expectedHash, "err", err) responses <- storeResponse{d, nil, fmt.Errorf("timeout was %d, expected %d", cert.Timeout, timeout)} return } diff --git a/das/local_file_storage_service.go b/das/local_file_storage_service.go index 621cf3efdb..65ca6fe15c 100644 --- a/das/local_file_storage_service.go +++ b/das/local_file_storage_service.go @@ -110,17 +110,21 @@ func (s *LocalFileStorageService) Close(ctx context.Context) error { func (s *LocalFileStorageService) GetByHash(ctx context.Context, key common.Hash) ([]byte, error) { log.Trace("das.LocalFileStorageService.GetByHash", "key", pretty.PrettyHash(key), "this", s) - var batchPath string - if s.enableLegacyLayout { - batchPath = s.legacyLayout.batchPath(key) - } else { - batchPath = s.layout.batchPath(key) - } + + legacyBatchPath := s.legacyLayout.batchPath(key) + batchPath := s.layout.batchPath(key) data, err := os.ReadFile(batchPath) if err != nil { if errors.Is(err, os.ErrNotExist) { - return nil, ErrNotFound + data, err = os.ReadFile(legacyBatchPath) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return nil, ErrNotFound + } + return nil, err + } + return data, nil } return nil, err } @@ -224,8 +228,15 @@ func (s *LocalFileStorageService) String() string { } func (s *LocalFileStorageService) HealthCheck(ctx context.Context) error { - testData := []byte("Test-Data") - err := s.Put(ctx, testData, uint64(time.Now().Add(time.Minute).Unix())) + testData := []byte("Test Data") + // Store some data with an expiry time at the start of the epoch. + // If expiry is disabled it will only create an index entry for the + // same timestamp each time the health check happens. + // If expiry is enabled, it will be cleaned up each time the pruning + // runs. There is a slight chance of a race between pruning and the + // Put and Get calls, but systems using the HealthCheck will just retry + // and succeed the next time. + err := s.Put(ctx, testData, 0 /* start of epoch */) if err != nil { return err } @@ -726,6 +737,8 @@ func (l *trieLayout) commitMigration() error { return err } + // in OSX - syscall.Sync() returns an error, but in linux it does not. + // nolint:errcheck syscall.Sync() // Done migrating diff --git a/das/reader_aggregator_strategies.go b/das/reader_aggregator_strategies.go index d20760bd5b..8e10d52c16 100644 --- a/das/reader_aggregator_strategies.go +++ b/das/reader_aggregator_strategies.go @@ -41,7 +41,7 @@ func (s *abstractAggregatorStrategy) update(readers []daprovider.DASReader, stat // Exponentially growing Explore Exploit Strategy type simpleExploreExploitStrategy struct { - iterations uint32 + iterations atomic.Uint32 exploreIterations uint32 exploitIterations uint32 @@ -49,7 +49,7 @@ type simpleExploreExploitStrategy struct { } func (s *simpleExploreExploitStrategy) newInstance() aggregatorStrategyInstance { - iterations := atomic.AddUint32(&s.iterations, 1) + iterations := s.iterations.Add(1) readerSets := make([][]daprovider.DASReader, 0) s.RLock() diff --git a/das/s3_storage_service.go b/das/s3_storage_service.go index a1de200c52..a289b0d46f 100644 --- a/das/s3_storage_service.go +++ b/das/s3_storage_service.go @@ -110,7 +110,7 @@ func (s3s *S3StorageService) Put(ctx context.Context, value []byte, timeout uint Bucket: aws.String(s3s.bucket), Key: aws.String(s3s.objectPrefix + EncodeStorageServiceKey(dastree.Hash(value))), Body: bytes.NewReader(value)} - if !s3s.discardAfterTimeout { + if s3s.discardAfterTimeout { expires := time.Unix(int64(timeout), 0) putObjectInput.Expires = &expires } diff --git a/das/syncing_fallback_storage.go b/das/syncing_fallback_storage.go index 1cf6a832f3..8af46b7fc5 100644 --- a/das/syncing_fallback_storage.go +++ b/das/syncing_fallback_storage.go @@ -68,7 +68,7 @@ type SyncToStorageConfig struct { var DefaultSyncToStorageConfig = SyncToStorageConfig{ Eager: false, EagerLowerBoundBlock: 0, - RetentionPeriod: defaultStorageRetention, + RetentionPeriod: daprovider.DefaultDASRetentionPeriod, DelayOnError: time.Second, IgnoreWriteErrors: true, ParentChainBlocksPerRead: 100, @@ -105,9 +105,9 @@ type l1SyncService struct { lastBatchAcc common.Hash } -// The original syncing process had a bug, so the file was renamed to cause any mirrors -// in the wild to re-sync from their configured starting block number. -const nextBlockNoFilename = "nextBlockNumberV2" +// The filename has been updated when we have discovered bugs that may have impacted +// syncing, to cause mirrors to re-sync. +const nextBlockNoFilename = "nextBlockNumberV3" func readSyncStateOrDefault(syncDir string, dflt uint64) uint64 { if syncDir == "" { diff --git a/execution/gethexec/block_recorder.go b/execution/gethexec/block_recorder.go index e55bd8d9b2..28719aac47 100644 --- a/execution/gethexec/block_recorder.go +++ b/execution/gethexec/block_recorder.go @@ -16,7 +16,6 @@ import ( "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/execution" - "github.com/offchainlabs/nitro/validator" ) // BlockRecorder uses a separate statedatabase from the blockchain. @@ -120,23 +119,7 @@ func (r *BlockRecorder) RecordBlockCreation( } var blockHash common.Hash - var readBatchInfo []validator.BatchInfo if msg != nil { - batchFetcher := func(batchNum uint64) ([]byte, error) { - data, blockHash, err := r.execEngine.consensus.FetchBatch(ctx, batchNum) - if err != nil { - return nil, err - } - readBatchInfo = append(readBatchInfo, validator.BatchInfo{ - Number: batchNum, - BlockHash: blockHash, - Data: data, - }) - return data, nil - } - // Re-fetch the batch instead of using our cached cost, - // as the replay binary won't have the cache populated. - msg.Message.BatchGasCost = nil block, _, err := arbos.ProduceBlock( msg.Message, msg.DelayedMessagesRead, @@ -144,7 +127,6 @@ func (r *BlockRecorder) RecordBlockCreation( recordingdb, chaincontext, chainConfig, - batchFetcher, false, r.execEngine.logger, ) @@ -173,7 +155,6 @@ func (r *BlockRecorder) RecordBlockCreation( Pos: pos, BlockHash: blockHash, Preimages: preimages, - BatchInfo: readBatchInfo, UserWasms: recordingdb.UserWasms(), }, err } diff --git a/execution/gethexec/blockchain.go b/execution/gethexec/blockchain.go index ea45f8689e..2477c072f7 100644 --- a/execution/gethexec/blockchain.go +++ b/execution/gethexec/blockchain.go @@ -38,6 +38,8 @@ type CachingConfig struct { 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"` + StateScheme string `koanf:"state-scheme"` + StateHistory uint64 `koanf:"state-history"` } func CachingConfigAddOptions(prefix string, f *flag.FlagSet) { @@ -53,24 +55,15 @@ func CachingConfigAddOptions(prefix string, f *flag.FlagSet) { 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") + f.String(prefix+".state-scheme", DefaultCachingConfig.StateScheme, "scheme to use for state trie storage (hash, path)") + f.Uint64(prefix+".state-history", DefaultCachingConfig.StateHistory, "number of recent blocks to retain state history for (path state-scheme only)") } -var DefaultCachingConfig = 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: 256, +func getStateHistory(maxBlockSpeed time.Duration) uint64 { + return uint64(24 * time.Hour / maxBlockSpeed) } -var TestCachingConfig = CachingConfig{ +var DefaultCachingConfig = CachingConfig{ Archive: false, BlockCount: 128, BlockAge: 30 * time.Minute, @@ -82,7 +75,9 @@ var TestCachingConfig = CachingConfig{ SnapshotRestoreGasLimit: 300_000_000_000, MaxNumberOfBlocksToSkipStateSaving: 0, MaxAmountOfGasToSkipStateSaving: 0, - StylusLRUCache: 0, + StylusLRUCache: 256, + StateScheme: rawdb.HashScheme, + StateHistory: getStateHistory(DefaultSequencerConfig.MaxBlockSpeed), } // TODO remove stack from parameters as it is no longer needed here @@ -105,10 +100,29 @@ func DefaultCacheConfigFor(stack *node.Node, cachingConfig *CachingConfig) *core SnapshotRestoreMaxGas: cachingConfig.SnapshotRestoreGasLimit, MaxNumberOfBlocksToSkipStateSaving: cachingConfig.MaxNumberOfBlocksToSkipStateSaving, MaxAmountOfGasToSkipStateSaving: cachingConfig.MaxAmountOfGasToSkipStateSaving, + StateScheme: cachingConfig.StateScheme, + StateHistory: cachingConfig.StateHistory, + } +} + +func (c *CachingConfig) validateStateScheme() error { + switch c.StateScheme { + case rawdb.HashScheme: + case rawdb.PathScheme: + if c.Archive { + return errors.New("archive cannot be set when using path as the state-scheme") + } + default: + return errors.New("Invalid StateScheme") } + return nil +} + +func (c *CachingConfig) Validate() error { + return c.validateStateScheme() } -func WriteOrTestGenblock(chainDb ethdb.Database, initData statetransfer.InitDataReader, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage, accountsPerSync uint) error { +func WriteOrTestGenblock(chainDb ethdb.Database, cacheConfig *core.CacheConfig, initData statetransfer.InitDataReader, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage, accountsPerSync uint) error { EmptyHash := common.Hash{} prevHash := EmptyHash prevDifficulty := big.NewInt(0) @@ -129,7 +143,7 @@ func WriteOrTestGenblock(chainDb ethdb.Database, initData statetransfer.InitData } timestamp = prevHeader.Time } - stateRoot, err := arbosState.InitializeArbosInDatabase(chainDb, initData, chainConfig, initMessage, timestamp, accountsPerSync) + stateRoot, err := arbosState.InitializeArbosInDatabase(chainDb, cacheConfig, initData, chainConfig, initMessage, timestamp, accountsPerSync) if err != nil { return err } @@ -199,7 +213,15 @@ func GetBlockChain(chainDb ethdb.Database, cacheConfig *core.CacheConfig, chainC } func WriteOrTestBlockChain(chainDb ethdb.Database, cacheConfig *core.CacheConfig, initData statetransfer.InitDataReader, chainConfig *params.ChainConfig, initMessage *arbostypes.ParsedInitMessage, txLookupLimit uint64, accountsPerSync uint, tracer core.BlockchainLogger) (*core.BlockChain, error) { - err := WriteOrTestGenblock(chainDb, initData, chainConfig, initMessage, accountsPerSync) + emptyBlockChain := rawdb.ReadHeadHeader(chainDb) == nil + if !emptyBlockChain && (cacheConfig.StateScheme == rawdb.PathScheme) { + // When using path scheme, and the stored state trie is not empty, + // WriteOrTestGenBlock is not able to recover EmptyRootHash state trie node. + // In that case Nitro doesn't test genblock, but just returns the BlockChain. + return GetBlockChain(chainDb, cacheConfig, chainConfig, txLookupLimit, tracer) + } + + err := WriteOrTestGenblock(chainDb, cacheConfig, initData, chainConfig, initMessage, accountsPerSync) if err != nil { return nil, err } diff --git a/execution/gethexec/blockchain_test.go b/execution/gethexec/blockchain_test.go new file mode 100644 index 0000000000..375488cedc --- /dev/null +++ b/execution/gethexec/blockchain_test.go @@ -0,0 +1,18 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package gethexec + +import ( + "testing" + "time" +) + +func TestGetStateHistory(t *testing.T) { + maxBlockSpeed := time.Millisecond * 250 + expectedStateHistory := uint64(345600) + actualStateHistory := getStateHistory(maxBlockSpeed) + if actualStateHistory != expectedStateHistory { + t.Errorf("Expected state history to be %d, but got %d", expectedStateHistory, actualStateHistory) + } +} diff --git a/execution/gethexec/executionengine.go b/execution/gethexec/executionengine.go index 7a4ae7864c..a12b0cee12 100644 --- a/execution/gethexec/executionengine.go +++ b/execution/gethexec/executionengine.go @@ -27,6 +27,7 @@ import ( "time" "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/types" "github.com/ethereum/go-ethereum/log" @@ -142,7 +143,7 @@ func (s *ExecutionEngine) MarkFeedStart(to arbutil.MessageIndex) { defer s.cachedL1PriceData.mutex.Unlock() if to < s.cachedL1PriceData.startOfL1PriceDataCache { - log.Info("trying to trim older cache which doesnt exist anymore") + log.Debug("trying to trim older L1 price data cache which doesnt exist anymore") } else if to >= s.cachedL1PriceData.endOfL1PriceDataCache { s.cachedL1PriceData.startOfL1PriceDataCache = 0 s.cachedL1PriceData.endOfL1PriceDataCache = 0 @@ -154,10 +155,32 @@ func (s *ExecutionEngine) MarkFeedStart(to arbutil.MessageIndex) { } } -func (s *ExecutionEngine) Initialize(rustCacheSize uint32) { +func populateStylusTargetCache(targetConfig *StylusTargetConfig) error { + var effectiveStylusTarget string + target := rawdb.LocalTarget() + switch target { + case rawdb.TargetArm64: + effectiveStylusTarget = targetConfig.Arm64 + case rawdb.TargetAmd64: + effectiveStylusTarget = targetConfig.Amd64 + case rawdb.TargetHost: + effectiveStylusTarget = targetConfig.Host + } + err := programs.SetTarget(target, effectiveStylusTarget, true) + if err != nil { + return fmt.Errorf("Failed to set stylus target: %w", err) + } + return nil +} + +func (s *ExecutionEngine) Initialize(rustCacheSize uint32, targetConfig *StylusTargetConfig) error { if rustCacheSize != 0 { programs.ResizeWasmLruCache(rustCacheSize) } + if err := populateStylusTargetCache(targetConfig); err != nil { + return fmt.Errorf("error populating stylus target cache: %w", err) + } + return nil } func (s *ExecutionEngine) SetRecorder(recorder *BlockRecorder) { @@ -360,8 +383,7 @@ func (s *ExecutionEngine) resequenceReorgedMessages(messages []*arbostypes.Messa log.Warn("skipping non-standard sequencer message found from reorg", "header", header) continue } - // We don't need a batch fetcher as this is an L2 message - txes, err := arbos.ParseL2Transactions(msg.Message, s.bc.Config().ChainID, nil) + txes, err := arbos.ParseL2Transactions(msg.Message, s.bc.Config().ChainID) if err != nil { log.Warn("failed to parse sequencer message found from reorg", "err", err) continue @@ -631,11 +653,6 @@ 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, @@ -643,7 +660,6 @@ func (s *ExecutionEngine) createBlockFromNextMessage(msg *arbostypes.MessageWith statedb, s.bc, s.bc.Config(), - batchFetcher, isMsgForPrefetch, logger, ) diff --git a/execution/gethexec/forwarder.go b/execution/gethexec/forwarder.go index 984c7224e8..cdb4f394e5 100644 --- a/execution/gethexec/forwarder.go +++ b/execution/gethexec/forwarder.go @@ -35,15 +35,6 @@ type ForwarderConfig struct { RetryInterval time.Duration `koanf:"retry-interval"` } -var DefaultTestForwarderConfig = ForwarderConfig{ - ConnectionTimeout: 2 * time.Second, - IdleConnectionTimeout: 2 * time.Second, - MaxIdleConnections: 1, - RedisUrl: "", - UpdateInterval: time.Millisecond * 10, - RetryInterval: time.Millisecond * 3, -} - var DefaultNodeForwarderConfig = ForwarderConfig{ ConnectionTimeout: 30 * time.Second, IdleConnectionTimeout: 15 * time.Second, diff --git a/execution/gethexec/node.go b/execution/gethexec/node.go index 9eb864e441..f411a135a6 100644 --- a/execution/gethexec/node.go +++ b/execution/gethexec/node.go @@ -19,23 +19,31 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" "github.com/offchainlabs/nitro/arbos/arbostypes" + "github.com/offchainlabs/nitro/arbos/programs" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/execution" "github.com/offchainlabs/nitro/solgen/go/precompilesgen" + "github.com/offchainlabs/nitro/util/dbutil" "github.com/offchainlabs/nitro/util/headerreader" flag "github.com/spf13/pflag" ) -type DangerousConfig struct { - ReorgToBlock int64 `koanf:"reorg-to-block"` +type StylusTargetConfig struct { + Arm64 string `koanf:"arm64"` + Amd64 string `koanf:"amd64"` + Host string `koanf:"host"` } -var DefaultDangerousConfig = DangerousConfig{ - ReorgToBlock: -1, +var DefaultStylusTargetConfig = StylusTargetConfig{ + Arm64: programs.DefaultTargetDescriptionArm, + Amd64: programs.DefaultTargetDescriptionX86, + Host: "", } -func DangerousConfigAddOptions(prefix string, f *flag.FlagSet) { - f.Int64(prefix+".reorg-to-block", DefaultDangerousConfig.ReorgToBlock, "DANGEROUS! forces a reorg to an old block height. To be used for testing only. -1 to disable") +func StylusTargetConfigAddOptions(prefix string, f *flag.FlagSet) { + f.String(prefix+".arm64", DefaultStylusTargetConfig.Arm64, "stylus programs compilation target for arm64 linux") + f.String(prefix+".amd64", DefaultStylusTargetConfig.Amd64, "stylus programs compilation target for amd64 linux") + f.String(prefix+".host", DefaultStylusTargetConfig.Host, "stylus programs compilation target for system other than 64-bit ARM or 64-bit x86") } type Config struct { @@ -49,14 +57,17 @@ type Config struct { Caching CachingConfig `koanf:"caching"` RPC arbitrum.Config `koanf:"rpc"` TxLookupLimit uint64 `koanf:"tx-lookup-limit"` - Dangerous DangerousConfig `koanf:"dangerous"` EnablePrefetchBlock bool `koanf:"enable-prefetch-block"` SyncMonitor SyncMonitorConfig `koanf:"sync-monitor"` + StylusTarget StylusTargetConfig `koanf:"stylus-target"` forwardingTarget string } func (c *Config) Validate() error { + if err := c.Caching.Validate(); err != nil { + return err + } if err := c.Sequencer.Validate(); err != nil { return err } @@ -86,8 +97,8 @@ func ConfigAddOptions(prefix string, f *flag.FlagSet) { 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") + StylusTargetConfigAddOptions(prefix+".stylus-target", f) } var ConfigDefault = Config{ @@ -100,34 +111,9 @@ var ConfigDefault = Config{ TxPreChecker: DefaultTxPreCheckerConfig, TxLookupLimit: 126_230_400, // 1 year at 4 blocks per second Caching: DefaultCachingConfig, - Dangerous: DefaultDangerousConfig, Forwarder: DefaultNodeForwarderConfig, EnablePrefetchBlock: true, -} - -func ConfigDefaultNonSequencerTest() *Config { - config := ConfigDefault - config.Caching = TestCachingConfig - config.ParentChainReader = headerreader.TestConfig - config.Sequencer.Enable = false - config.Forwarder = DefaultTestForwarderConfig - config.ForwardingTarget = "null" - - _ = config.Validate() - - return &config -} - -func ConfigDefaultTest() *Config { - config := ConfigDefault - config.Caching = TestCachingConfig - config.Sequencer = TestSequencerConfig - config.ParentChainReader = headerreader.TestConfig - config.ForwardingTarget = "null" - - _ = config.Validate() - - return &config + StylusTarget: DefaultStylusTargetConfig, } type ConfigFetcher func() *Config @@ -222,11 +208,16 @@ func CreateExecutionNode( 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 { + classicMsgDb, err := stack.OpenDatabase("classic-msg", 0, 0, "classicmsg/", true) + if dbutil.IsNotExistError(err) { log.Warn("Classic Msg Database not found", "err", err) classicOutbox = nil + } else if err != nil { + return nil, fmt.Errorf("Failed to open classic-msg database: %w", err) } else { + if err := dbutil.UnfinishedConversionCheck(classicMsgDb); err != nil { + return nil, fmt.Errorf("classic-msg unfinished database conversion check error: %w", err) + } classicOutbox = NewClassicOutboxRetriever(classicMsgDb) } } @@ -286,9 +277,13 @@ func (n *ExecutionNode) MarkFeedStart(to arbutil.MessageIndex) { } func (n *ExecutionNode) Initialize(ctx context.Context) error { - n.ExecEngine.Initialize(n.ConfigFetcher().Caching.StylusLRUCache) + config := n.ConfigFetcher() + err := n.ExecEngine.Initialize(config.Caching.StylusLRUCache, &config.StylusTarget) + if err != nil { + return fmt.Errorf("error initializing execution engine: %w", err) + } n.ArbInterface.Initialize(n) - err := n.Backend.Start() + err = n.Backend.Start() if err != nil { return fmt.Errorf("error starting geth backend: %w", err) } diff --git a/execution/gethexec/sequencer.go b/execution/gethexec/sequencer.go index 2bace9b677..90e3082062 100644 --- a/execution/gethexec/sequencer.go +++ b/execution/gethexec/sequencer.go @@ -11,7 +11,6 @@ import ( "math/big" "runtime/debug" "strconv" - "strings" "sync" "sync/atomic" "time" @@ -52,8 +51,8 @@ var ( nonceFailureCacheOverflowCounter = metrics.NewRegisteredGauge("arb/sequencer/noncefailurecache/overflow", nil) blockCreationTimer = metrics.NewRegisteredTimer("arb/sequencer/block/creation", nil) successfulBlocksCounter = metrics.NewRegisteredCounter("arb/sequencer/block/successful", nil) - conditionalTxRejectedBySequencerCounter = metrics.NewRegisteredCounter("arb/sequencer/condtionaltx/rejected", nil) - conditionalTxAcceptedBySequencerCounter = metrics.NewRegisteredCounter("arb/sequencer/condtionaltx/accepted", nil) + conditionalTxRejectedBySequencerCounter = metrics.NewRegisteredCounter("arb/sequencer/conditionaltx/rejected", nil) + conditionalTxAcceptedBySequencerCounter = metrics.NewRegisteredCounter("arb/sequencer/conditionaltx/accepted", nil) l1GasPriceGauge = metrics.NewRegisteredGauge("arb/sequencer/l1gasprice", nil) callDataUnitsBacklogGauge = metrics.NewRegisteredGauge("arb/sequencer/calldataunitsbacklog", nil) unusedL1GasChargeGauge = metrics.NewRegisteredGauge("arb/sequencer/unusedl1gascharge", nil) @@ -66,7 +65,7 @@ type SequencerConfig struct { 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"` + SenderWhitelist []string `koanf:"sender-whitelist"` Forwarder ForwarderConfig `koanf:"forwarder"` QueueSize int `koanf:"queue-size"` QueueTimeout time.Duration `koanf:"queue-timeout" reload:"hot"` @@ -82,8 +81,7 @@ type SequencerConfig struct { } func (c *SequencerConfig) Validate() error { - entries := strings.Split(c.SenderWhitelist, ",") - for _, address := range entries { + for _, address := range c.SenderWhitelist { if len(address) == 0 { continue } @@ -118,6 +116,7 @@ var DefaultSequencerConfig = SequencerConfig{ MaxBlockSpeed: time.Millisecond * 250, MaxRevertGasReject: 0, MaxAcceptableTimestampDelta: time.Hour, + SenderWhitelist: []string{}, Forwarder: DefaultSequencerForwarderConfig, QueueSize: 1024, QueueTimeout: time.Second * 12, @@ -132,30 +131,12 @@ var DefaultSequencerConfig = SequencerConfig{ 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, - ExpectedSurplusSoftThreshold: "default", - ExpectedSurplusHardThreshold: "default", - EnableProfiling: false, -} - func SequencerConfigAddOptions(prefix string, f *flag.FlagSet) { f.Bool(prefix+".enable", DefaultSequencerConfig.Enable, "act and post to l1 as sequencer") f.Duration(prefix+".max-block-speed", DefaultSequencerConfig.MaxBlockSpeed, "minimum delay between blocks (sets a maximum speed of block production)") f.Uint64(prefix+".max-revert-gas-reject", DefaultSequencerConfig.MaxRevertGasReject, "maximum gas executed in a revert for the sequencer to reject the transaction instead of posting it (anti-DOS)") f.Duration(prefix+".max-acceptable-timestamp-delta", DefaultSequencerConfig.MaxAcceptableTimestampDelta, "maximum acceptable time difference between the local time and the latest L1 block's timestamp") - f.String(prefix+".sender-whitelist", DefaultSequencerConfig.SenderWhitelist, "comma separated whitelist of authorized senders (if empty, everyone is allowed)") + f.StringSlice(prefix+".sender-whitelist", DefaultSequencerConfig.SenderWhitelist, "comma separated whitelist of authorized senders (if empty, everyone is allowed)") AddOptionsForSequencerForwarderConfig(prefix+".forwarder", f) f.Int(prefix+".queue-size", DefaultSequencerConfig.QueueSize, "size of the pending tx queue") f.Duration(prefix+".queue-timeout", DefaultSequencerConfig.QueueTimeout, "maximum amount of time transaction can wait in queue") @@ -321,7 +302,7 @@ type Sequencer struct { onForwarderSet chan struct{} L1BlockAndTimeMutex sync.Mutex - l1BlockNumber uint64 + l1BlockNumber atomic.Uint64 l1Timestamp uint64 // activeMutex manages pauseChan (pauses execution) and forwarder @@ -342,8 +323,7 @@ func NewSequencer(execEngine *ExecutionEngine, l1Reader *headerreader.HeaderRead return nil, err } senderWhitelist := make(map[common.Address]struct{}) - entries := strings.Split(config.SenderWhitelist, ",") - for _, address := range entries { + for _, address := range config.SenderWhitelist { if len(address) == 0 { continue } @@ -356,7 +336,6 @@ func NewSequencer(execEngine *ExecutionEngine, l1Reader *headerreader.HeaderRead config: configFetcher, senderWhitelist: senderWhitelist, nonceCache: newNonceCache(config.NonceCacheSize), - l1BlockNumber: 0, l1Timestamp: 0, pauseChan: nil, onForwarderSet: make(chan struct{}, 1), @@ -900,7 +879,7 @@ func (s *Sequencer) createBlock(ctx context.Context) (returnValue bool) { timestamp := time.Now().Unix() s.L1BlockAndTimeMutex.Lock() - l1Block := s.l1BlockNumber + l1Block := s.l1BlockNumber.Load() l1Timestamp := s.l1Timestamp s.L1BlockAndTimeMutex.Unlock() @@ -1014,9 +993,9 @@ func (s *Sequencer) updateLatestParentChainBlock(header *types.Header) { defer s.L1BlockAndTimeMutex.Unlock() l1BlockNumber := arbutil.ParentHeaderToL1BlockNumber(header) - if header.Time > s.l1Timestamp || (header.Time == s.l1Timestamp && l1BlockNumber > s.l1BlockNumber) { + if header.Time > s.l1Timestamp || (header.Time == s.l1Timestamp && l1BlockNumber > s.l1BlockNumber.Load()) { s.l1Timestamp = header.Time - s.l1BlockNumber = l1BlockNumber + s.l1BlockNumber.Store(l1BlockNumber) } } @@ -1082,7 +1061,7 @@ func (s *Sequencer) Start(ctxIn context.Context) error { } if s.l1Reader != nil { - initialBlockNr := atomic.LoadUint64(&s.l1BlockNumber) + initialBlockNr := s.l1BlockNumber.Load() if initialBlockNr == 0 { return errors.New("sequencer not initialized") } diff --git a/execution/gethexec/tx_pre_checker.go b/execution/gethexec/tx_pre_checker.go index 1a48d75fda..191331b48a 100644 --- a/execution/gethexec/tx_pre_checker.go +++ b/execution/gethexec/tx_pre_checker.go @@ -23,10 +23,10 @@ import ( ) var ( - conditionalTxRejectedByTxPreCheckerCurrentStateCounter = metrics.NewRegisteredCounter("arb/txprechecker/condtionaltx/currentstate/rejected", nil) - conditionalTxAcceptedByTxPreCheckerCurrentStateCounter = metrics.NewRegisteredCounter("arb/txprechecker/condtionaltx/currentstate/accepted", nil) - conditionalTxRejectedByTxPreCheckerOldStateCounter = metrics.NewRegisteredCounter("arb/txprechecker/condtionaltx/oldstate/rejected", nil) - conditionalTxAcceptedByTxPreCheckerOldStateCounter = metrics.NewRegisteredCounter("arb/txprechecker/condtionaltx/oldstate/accepted", nil) + conditionalTxRejectedByTxPreCheckerCurrentStateCounter = metrics.NewRegisteredCounter("arb/txprechecker/conditionaltx/currentstate/rejected", nil) + conditionalTxAcceptedByTxPreCheckerCurrentStateCounter = metrics.NewRegisteredCounter("arb/txprechecker/conditionaltx/currentstate/accepted", nil) + conditionalTxRejectedByTxPreCheckerOldStateCounter = metrics.NewRegisteredCounter("arb/txprechecker/conditionaltx/oldstate/rejected", nil) + conditionalTxAcceptedByTxPreCheckerOldStateCounter = metrics.NewRegisteredCounter("arb/txprechecker/conditionaltx/oldstate/accepted", nil) ) const TxPreCheckerStrictnessNone uint = 0 @@ -43,7 +43,7 @@ type TxPreCheckerConfig struct { type TxPreCheckerConfigFetcher func() *TxPreCheckerConfig var DefaultTxPreCheckerConfig = TxPreCheckerConfig{ - Strictness: TxPreCheckerStrictnessNone, + Strictness: TxPreCheckerStrictnessLikelyCompatible, RequiredStateAge: 2, RequiredStateMaxBlocks: 4, } diff --git a/execution/gethexec/wasmstorerebuilder.go b/execution/gethexec/wasmstorerebuilder.go index dcbee45a3f..698ba3ec8a 100644 --- a/execution/gethexec/wasmstorerebuilder.go +++ b/execution/gethexec/wasmstorerebuilder.go @@ -59,9 +59,14 @@ func WriteToKeyValueStore[T any](store ethdb.KeyValueStore, key []byte, val T) e // It also stores a special value that is only set once when rebuilding commenced in RebuildingStartBlockHashKey as the block // time of the latest block when rebuilding was first called, this is used to avoid recomputing of assembly and module of // contracts that were created after rebuilding commenced since they would anyway already be added during sync. -func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, chainDb ethdb.Database, maxRecreateStateDepth int64, l2Blockchain *core.BlockChain, position, rebuildingStartBlockHash common.Hash) error { +func RebuildWasmStore(ctx context.Context, wasmStore ethdb.KeyValueStore, chainDb ethdb.Database, maxRecreateStateDepth int64, targetConfig *StylusTargetConfig, l2Blockchain *core.BlockChain, position, rebuildingStartBlockHash common.Hash) error { var err error var stateDb *state.StateDB + + if err := populateStylusTargetCache(targetConfig); err != nil { + return fmt.Errorf("error populating stylus target cache: %w", err) + } + latestHeader := l2Blockchain.CurrentBlock() // Attempt to get state at the start block when rebuilding commenced, if not available (in case of non-archival nodes) use latest state rebuildingStartHeader := l2Blockchain.GetHeaderByHash(rebuildingStartBlockHash) diff --git a/execution/interface.go b/execution/interface.go index ddf30b4b2a..2a3d79c697 100644 --- a/execution/interface.go +++ b/execution/interface.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/core/state" "github.com/offchainlabs/nitro/arbos/arbostypes" "github.com/offchainlabs/nitro/arbutil" - "github.com/offchainlabs/nitro/validator" ) type MessageResult struct { @@ -21,7 +20,6 @@ type RecordResult struct { Pos arbutil.MessageIndex BlockHash common.Hash Preimages map[common.Hash][]byte - BatchInfo []validator.BatchInfo UserWasms state.UserWasms } @@ -77,7 +75,6 @@ type FullExecutionClient interface { // not implemented in execution, used as input // BatchFetcher is required for any execution node type BatchFetcher interface { - FetchBatch(ctx context.Context, batchNum uint64) ([]byte, common.Hash, error) FindInboxBatchContainingMessage(message arbutil.MessageIndex) (uint64, bool, error) GetBatchParentChainBlock(seqNum uint64) (uint64, error) } diff --git a/gethhook/geth_test.go b/gethhook/geth_test.go index 99bfa4ae1c..57ce2ddec0 100644 --- a/gethhook/geth_test.go +++ b/gethhook/geth_test.go @@ -123,7 +123,7 @@ func RunMessagesThroughAPI(t *testing.T, msgs [][]byte, statedb *state.StateDB) if err != nil { t.Error(err) } - txes, err := arbos.ParseL2Transactions(msg, chainId, nil) + txes, err := arbos.ParseL2Transactions(msg, chainId) if err != nil { t.Error(err) } diff --git a/go-ethereum b/go-ethereum index 8e848f4ae8..3f67e360f8 160000 --- a/go-ethereum +++ b/go-ethereum @@ -1 +1 @@ -Subproject commit 8e848f4ae8110aa14d08d863cd925280b9a61894 +Subproject commit 3f67e360f8927818b30979ebf6e49d783007a8cd diff --git a/go.mod b/go.mod index 0b58aadffb..8b0df08293 100644 --- a/go.mod +++ b/go.mod @@ -28,6 +28,7 @@ require ( 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/btree v1.1.2 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.3.1 github.com/hashicorp/golang-lru/v2 v2.0.7 @@ -119,7 +120,7 @@ require ( github.com/graph-gophers/graphql-go v1.3.0 // indirect github.com/h2non/filetype v1.0.6 // indirect github.com/hashicorp/go-bexpr v0.1.10 // indirect - github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 // indirect + github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect github.com/huin/goupnp v1.3.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect @@ -162,7 +163,8 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.26.0 // indirect golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.22.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/oauth2 v0.22.0 golang.org/x/sync v0.5.0 golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect diff --git a/go.sum b/go.sum index c6e227d20b..c415ad3f2c 100644 --- a/go.sum +++ b/go.sum @@ -281,6 +281,10 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW 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/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= 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= @@ -347,8 +351,8 @@ github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoI 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= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= -github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= +github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/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.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= @@ -702,6 +706,17 @@ 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/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.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-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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= +golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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= @@ -835,6 +850,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 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= diff --git a/nitro-testnode b/nitro-testnode index 9dc0588c50..f328006579 160000 --- a/nitro-testnode +++ b/nitro-testnode @@ -1 +1 @@ -Subproject commit 9dc0588c5066e2efd25c09adf12df7b28ef18cb6 +Subproject commit f328006579cbefe22c6c57de3d6b86397fde4438 diff --git a/pubsub/common.go b/pubsub/common.go index 9f05304e46..d7f041af15 100644 --- a/pubsub/common.go +++ b/pubsub/common.go @@ -2,6 +2,7 @@ package pubsub import ( "context" + "strings" "github.com/ethereum/go-ethereum/log" "github.com/go-redis/redis/v8" @@ -22,7 +23,9 @@ func CreateStream(ctx context.Context, streamName string, client redis.Universal 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) + if !strings.Contains(err.Error(), "no such key") { + log.Error("redis error", "err", err, "searching stream", streamName) + } return false } return got != nil diff --git a/pubsub/producer.go b/pubsub/producer.go index 074670ca0f..2b1cdb5e3f 100644 --- a/pubsub/producer.go +++ b/pubsub/producer.go @@ -13,6 +13,9 @@ import ( "encoding/json" "errors" "fmt" + "math" + "strconv" + "strings" "sync" "time" @@ -59,6 +62,7 @@ type ProducerConfig struct { KeepAliveTimeout time.Duration `koanf:"keepalive-timeout"` // Interval duration for checking the result set by consumers. CheckResultInterval time.Duration `koanf:"check-result-interval"` + CheckPendingItems int64 `koanf:"check-pending-items"` } var DefaultProducerConfig = ProducerConfig{ @@ -66,13 +70,15 @@ var DefaultProducerConfig = ProducerConfig{ CheckPendingInterval: time.Second, KeepAliveTimeout: 5 * time.Minute, CheckResultInterval: 5 * time.Second, + CheckPendingItems: 256, } var TestProducerConfig = ProducerConfig{ - EnableReproduce: true, + EnableReproduce: false, CheckPendingInterval: 10 * time.Millisecond, KeepAliveTimeout: 100 * time.Millisecond, CheckResultInterval: 5 * time.Millisecond, + CheckPendingItems: 256, } func ProducerAddConfigAddOptions(prefix string, f *pflag.FlagSet) { @@ -80,6 +86,7 @@ func ProducerAddConfigAddOptions(prefix string, f *pflag.FlagSet) { 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") + f.Int64(prefix+".check-pending-items", DefaultProducerConfig.CheckPendingItems, "items to screen during check-pending") } func NewProducer[Request any, Response any](client redis.UniversalClient, streamName string, cfg *ProducerConfig) (*Producer[Request, Response], error) { @@ -99,13 +106,13 @@ func NewProducer[Request any, Response any](client redis.UniversalClient, stream }, nil } -func (p *Producer[Request, Response]) errorPromisesFor(msgs []*Message[Request]) { +func (p *Producer[Request, Response]) errorPromisesFor(msgIds []string) { p.promisesLock.Lock() defer p.promisesLock.Unlock() - for _, msg := range msgs { - if promise, found := p.promises[msg.ID]; found { + for _, msg := range msgIds { + if promise, found := p.promises[msg]; found { promise.ProduceError(fmt.Errorf("internal error, consumer died while serving the request")) - delete(p.promises, msg.ID) + delete(p.promises, msg) } } } @@ -113,56 +120,132 @@ func (p *Producer[Request, Response]) errorPromisesFor(msgs []*Message[Request]) // 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) + staleIds, err := p.checkPending(ctx) if err != nil { log.Error("Checking pending messages", "error", err) return p.cfg.CheckPendingInterval } - if len(msgs) == 0 { + if len(staleIds) == 0 { return p.cfg.CheckPendingInterval } - if !p.cfg.EnableReproduce { - p.errorPromisesFor(msgs) - return p.cfg.CheckPendingInterval + if p.cfg.EnableReproduce { + err = p.reproduceIds(ctx, staleIds) + if err != nil { + log.Warn("filed reproducing messages", "err", err) + } + } else { + p.errorPromisesFor(staleIds) } - acked := make(map[string]Request) - for _, msg := range msgs { + return p.cfg.CheckPendingInterval +} + +func (p *Producer[Request, Response]) reproduceIds(ctx context.Context, staleIds []string) error { + log.Info("Attempting to claim", "messages", staleIds) + claimedMsgs, err := p.client.XClaim(ctx, &redis.XClaimArgs{ + Stream: p.redisStream, + Group: p.redisGroup, + Consumer: p.id, + MinIdle: p.cfg.KeepAliveTimeout, + Messages: staleIds, + }).Result() + if err != nil { + return fmt.Errorf("claiming ownership on messages: %v, error: %w", staleIds, err) + } + for _, msg := range claimedMsgs { + data, ok := (msg.Values[messageKey]).(string) + if !ok { + log.Error("redis producer reproduce: message not string", "id", msg.ID, "value", msg.Values[messageKey]) + continue + } + var req Request + if err := json.Unmarshal([]byte(data), &req); err != nil { + log.Error("redis producer reproduce: message not a request", "id", msg.ID, "err", err, "value", msg.Values[messageKey]) + continue + } if _, err := p.client.XAck(ctx, p.redisStream, p.redisGroup, msg.ID).Result(); err != nil { - log.Error("ACKing message", "error", err) + log.Error("redis producer reproduce: could not ACK", "id", msg.ID, "err", 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) + if _, err := p.reproduce(ctx, req, msg.ID); err != nil { + log.Error("redis producer reproduce: error", "err", err) } } - return p.cfg.CheckPendingInterval + return nil +} + +func setMinIdInt(min *[2]uint64, id string) error { + idParts := strings.Split(id, "-") + if len(idParts) != 2 { + return fmt.Errorf("invalid i.d: %v", id) + } + idTimeStamp, err := strconv.ParseUint(idParts[0], 10, 64) + if err != nil { + return fmt.Errorf("invalid i.d: %v err: %w", id, err) + } + if idTimeStamp > min[0] { + return nil + } + idSerial, err := strconv.ParseUint(idParts[1], 10, 64) + if err != nil { + return fmt.Errorf("invalid i.d serial: %v err: %w", id, err) + } + if idTimeStamp < min[0] { + min[0] = idTimeStamp + min[1] = idSerial + return nil + } + // idTimeStamp == min[0] + if idSerial < min[1] { + min[1] = idSerial + } + return nil } // checkResponses checks iteratively whether response for the promise is ready. func (p *Producer[Request, Response]) checkResponses(ctx context.Context) time.Duration { + minIdInt := [2]uint64{math.MaxUint64, math.MaxUint64} p.promisesLock.Lock() defer p.promisesLock.Unlock() + responded := 0 + errored := 0 for id, promise := range p.promises { + if ctx.Err() != nil { + return 0 + } res, err := p.client.Get(ctx, id).Result() if err != nil { - if errors.Is(err, redis.Nil) { - continue + errSetId := setMinIdInt(&minIdInt, id) + if errSetId != nil { + log.Error("error setting minId", "err", err) + return p.cfg.CheckResultInterval + } + if !errors.Is(err, redis.Nil) { + log.Error("Error reading value in redis", "key", id, "error", err) } - log.Error("Error reading value in redis", "key", id, "error", err) + continue } var resp Response if err := json.Unmarshal([]byte(res), &resp); err != nil { + promise.ProduceError(fmt.Errorf("error unmarshalling: %w", err)) log.Error("Error unmarshaling", "value", res, "error", err) - continue + errored++ + } else { + promise.Produce(resp) + responded++ } - promise.Produce(resp) delete(p.promises, id) } + var trimmed int64 + var trimErr error + minId := "+" + if minIdInt[0] < math.MaxUint64 { + minId = fmt.Sprintf("%d-%d", minIdInt[0], minIdInt[1]) + trimmed, trimErr = p.client.XTrimMinID(ctx, p.redisStream, minId).Result() + } else { + trimmed, trimErr = p.client.XTrimMaxLen(ctx, p.redisStream, 0).Result() + } + log.Trace("trimming", "id", minId, "trimmed", trimmed, "responded", responded, "errored", errored, "trim-err", trimErr) return p.cfg.CheckResultInterval } @@ -184,6 +267,10 @@ func (p *Producer[Request, Response]) reproduce(ctx context.Context, value Reque if err != nil { return nil, fmt.Errorf("marshaling value: %w", err) } + // catching the promiseLock before we sendXadd makes sure promise ids will + // be always ascending + p.promisesLock.Lock() + defer p.promisesLock.Unlock() id, err := p.client.XAdd(ctx, &redis.XAddArgs{ Stream: p.redisStream, Values: map[string]any{messageKey: val}, @@ -191,13 +278,12 @@ func (p *Producer[Request, Response]) reproduce(ctx context.Context, value Reque 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") + // don't error + log.Warn("tried reproducing a message but it wasn't found - probably got response", "oldKey", oldKey) } if oldKey == "" || promise == nil { pr := containers.NewPromise[Response](nil) @@ -232,13 +318,14 @@ func (p *Producer[Request, Response]) havePromiseFor(messageID string) bool { return found } -func (p *Producer[Request, Response]) checkPending(ctx context.Context) ([]*Message[Request], error) { +// returns ids of pending messages that's worker doesn't appear alive +func (p *Producer[Request, Response]) checkPending(ctx context.Context) ([]string, error) { pendingMessages, err := p.client.XPendingExt(ctx, &redis.XPendingExtArgs{ Stream: p.redisStream, Group: p.redisGroup, Start: "-", End: "+", - Count: 100, + Count: p.cfg.CheckPendingItems, }).Result() if err != nil && !errors.Is(err, redis.Nil) { @@ -247,6 +334,9 @@ func (p *Producer[Request, Response]) checkPending(ctx context.Context) ([]*Mess if len(pendingMessages) == 0 { return nil, nil } + if len(pendingMessages) >= int(p.cfg.CheckPendingItems) { + log.Warn("redis producer: many pending items found", "stream", p.redisStream, "check-pending-items", p.cfg.CheckPendingItems) + } // IDs of the pending messages with inactive consumers. var ids []string active := make(map[string]bool) @@ -265,35 +355,5 @@ func (p *Producer[Request, Response]) checkPending(ctx context.Context) ([]*Mess } 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 + return ids, nil } diff --git a/pubsub/pubsub_test.go b/pubsub/pubsub_test.go index 72504602e3..9f774b6372 100644 --- a/pubsub/pubsub_test.go +++ b/pubsub/pubsub_test.go @@ -49,10 +49,12 @@ type configOpt interface { apply(consCfg *ConsumerConfig, prodCfg *ProducerConfig) } -type disableReproduce struct{} +type withReproduce struct { + reproduce bool +} -func (e *disableReproduce) apply(_ *ConsumerConfig, prodCfg *ProducerConfig) { - prodCfg.EnableReproduce = false +func (e *withReproduce) apply(_ *ConsumerConfig, prodCfg *ProducerConfig) { + prodCfg.EnableReproduce = e.reproduce } func producerCfg() *ProducerConfig { @@ -61,6 +63,7 @@ func producerCfg() *ProducerConfig { CheckPendingInterval: TestProducerConfig.CheckPendingInterval, KeepAliveTimeout: TestProducerConfig.KeepAliveTimeout, CheckResultInterval: TestProducerConfig.CheckResultInterval, + CheckPendingItems: TestProducerConfig.CheckPendingItems, } } @@ -71,7 +74,7 @@ func consumerCfg() *ConsumerConfig { } } -func newProducerConsumers(ctx context.Context, t *testing.T, opts ...configOpt) (*Producer[testRequest, testResponse], []*Consumer[testRequest, testResponse]) { +func newProducerConsumers(ctx context.Context, t *testing.T, opts ...configOpt) (redis.UniversalClient, string, *Producer[testRequest, testResponse], []*Consumer[testRequest, testResponse]) { t.Helper() redisClient, err := redisutil.RedisClientFromURL(redisutil.CreateTestRedis(ctx, t)) if err != nil { @@ -107,7 +110,7 @@ func newProducerConsumers(ctx context.Context, t *testing.T, opts ...configOpt) log.Debug("Error deleting heartbeat keys", "error", err) } }) - return producer, consumers + return redisClient, streamName, producer, consumers } func messagesMaps(n int) []map[string]string { @@ -118,10 +121,14 @@ func messagesMaps(n int) []map[string]string { return ret } +func msgForIndex(idx int) string { + return fmt.Sprintf("msg: %d", idx) +} + func wantMessages(n int) []string { var ret []string for i := 0; i < n; i++ { - ret = append(ret, fmt.Sprintf("msg: %d", i)) + ret = append(ret, msgForIndex(i)) } sort.Strings(ret) return ret @@ -148,26 +155,25 @@ func produceMessages(ctx context.Context, msgs []string, producer *Producer[test return promises, nil } -func awaitResponses(ctx context.Context, promises []*containers.Promise[testResponse]) ([]string, error) { +func awaitResponses(ctx context.Context, promises []*containers.Promise[testResponse]) ([]string, []int) { var ( responses []string - errs []error + errs []int ) - for _, p := range promises { + for idx, p := range promises { res, err := p.Await(ctx) if err != nil { - errs = append(errs, err) + errs = append(errs, idx) continue } responses = append(responses, res.Response) } - return responses, errors.Join(errs...) + return responses, 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) { +func consume(ctx context.Context, t *testing.T, consumers []*Consumer[testRequest, testResponse], gotMessages []map[string]string) [][]string { t.Helper() - gotMessages := messagesMaps(consumersCount) wantResponses := make([][]string, consumersCount) for idx := 0; idx < consumersCount; idx++ { if consumers[idx].Stopped() { @@ -199,7 +205,7 @@ func consume(ctx context.Context, t *testing.T, consumers []*Consumer[testReques } }) } - return gotMessages, wantResponses + return wantResponses } func TestRedisProduce(t *testing.T) { @@ -208,43 +214,56 @@ func TestRedisProduce(t *testing.T) { for _, tc := range []struct { name string killConsumers bool + autoRecover bool }{ { name: "all consumers are active", killConsumers: false, + autoRecover: false, }, { name: "some consumers killed, others should take over their work", killConsumers: true, + autoRecover: true, + }, + { + name: "some consumers killed, should return failure", + killConsumers: true, + autoRecover: false, }, } { t.Run(tc.name, func(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() - producer, consumers := newProducerConsumers(ctx, t) + redisClient, streamName, producer, consumers := newProducerConsumers(ctx, t, &withReproduce{tc.autoRecover}) producer.Start(ctx) wantMsgs := wantMessages(messagesCount) promises, err := produceMessages(ctx, wantMsgs, producer) if err != nil { t.Fatalf("Error producing messages: %v", err) } + gotMessages := messagesMaps(len(consumers)) 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 { + req, err := consumers[i].Consume(ctx) + if err != nil { t.Errorf("Error consuming message: %v", err) } + if !tc.autoRecover { + gotMessages[i][req.ID] = req.Value.Request + } 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) + wantResponses := consume(ctx, t, consumers, gotMessages) + gotResponses, errIndexes := awaitResponses(ctx, promises) + if len(errIndexes) != 0 && tc.autoRecover { + t.Fatalf("Error awaiting responses: %v", errIndexes) } producer.StopAndWait() for _, c := range consumers { @@ -254,7 +273,6 @@ func TestRedisProduce(t *testing.T) { 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) } @@ -266,57 +284,19 @@ func TestRedisProduce(t *testing.T) { if cnt := producer.promisesLen(); cnt != 0 { t.Errorf("Producer still has %d unfullfilled promises", cnt) } + // Trigger a trim + producer.checkResponses(ctx) + msgs, err := redisClient.XRange(ctx, streamName, "-", "+").Result() + if err != nil { + t.Errorf("XRange failed: %v", err) + } + if len(msgs) != 0 { + t.Errorf("redis still has %v messages", len(msgs)) + } }) } } -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) { diff --git a/safe-smart-account b/safe-smart-account new file mode 160000 index 0000000000..192c7dc672 --- /dev/null +++ b/safe-smart-account @@ -0,0 +1 @@ +Subproject commit 192c7dc67290940fcbc75165522bb86a37187069 diff --git a/scripts/convert-databases.bash b/scripts/convert-databases.bash new file mode 100755 index 0000000000..bd898c2c98 --- /dev/null +++ b/scripts/convert-databases.bash @@ -0,0 +1,278 @@ +#!/usr/bin/env bash + +DEFAULT_DBCONV=/usr/local/bin/dbconv +DEFAULT_SRC=/home/user/.arbitrum/arb1/nitro + +dbconv=$DEFAULT_DBCONV +src=$DEFAULT_SRC +dst= +force=false +skip_existing=false +clean="failed" + +l2chaindata_status="not started" +l2chaindata_ancient_status="not started" +arbitrumdata_status="not started" +wasm_status="not started" +classicmsg_status="not started" + +checkMissingValue () { + if [[ $1 -eq 0 || $2 == -* ]]; then + echo "missing $3 argument value" + exit 1 + fi +} + +printStatus() { + echo "== Conversion status:" + echo " l2chaindata database: $l2chaindata_status" + echo " l2chaindata database freezer (ancient): $l2chaindata_ancient_status" + echo " arbitrumdata database: $arbitrumdata_status" + echo " wasm database: $wasm_status" + echo " classic-msg database: $classicmsg_status" +} + +printUsage() { +echo Usage: $0 \[OPTIONS..\] + echo + echo OPTIONS: + echo "--dbconv dbconv binary path (default: \"$DEFAULT_DBCONV\")" + echo "--src directory containing source databases (default: \"$DEFAULT_SRC\")" + echo "--dst destination directory" + echo "--force remove destination directory if it exists" + echo "--skip-existing skip convertion of databases which directories already exist in the destination directory" + echo "--clean sets what should be removed in case of error, possible values:" + echo " \"failed\" - remove database which conversion failed (default)" + echo " \"none\" - remove nothing, leave unfinished and potentially corrupted databases" + echo " \"all\" - remove whole destination directory" +} + +removeDir() { + cmd="rm -r \"$1\"" + echo $cmd + eval $cmd + return $? +} + +cleanup() { + case $clean in + all) + echo "== Removing destination directory" + removeDir "$dst" + ;; + failed) + echo "== Note: removing only failed destination directory" + dstdir=$(echo $dst/$1 | tr -s /) + removeDir "$dstdir" + ;; + none) + echo "== Warning: not removing destination directories, the destination databases might be incomplete and/or corrupted!" + ;; + *) + # shouldn't happen + echo "Script error, invalid --clean flag value: $clean" + exit 1 + ;; + + esac +} + +while [[ $# -gt 0 ]]; do + case $1 in + --dbconv) + shift + checkMissingValue $# "$1" "--dbconv" + dbconv=$1 + shift + ;; + --src) + shift + checkMissingValue $# "$1" "--src" + src=$1 + shift + ;; + --dst) + shift + checkMissingValue $# "$1" "--dst" + dst=$1 + shift + ;; + --force) + force=true + shift + ;; + --skip-existing) + skip_existing=true + shift + ;; + --clean) + shift + checkMissingValue $# "$1" "--clean" + clean=$1 + shift + ;; + --help) + printUsage + exit 0 + ;; + *) + printUsage + exit 0 + esac +done + +if $force && $skip_existing; then + echo Error: Cannot use both --force and --skipexisting + printUsage + exit 1 +fi + +if [ $clean != "all" ] && [ $clean != "failed" ] && [ $clean != "none" ] ; then + echo Error: Invalid --clean value: $clean + printUsage + exit 1 +fi + +if ! [ -e "$dbconv" ]; then + echo Error: Invalid dbconv binary path: "$dbconv" does not exist + exit 1 +fi + +if ! [ -n "$dst" ]; then + echo Error: Missing destination directory \(\-\-dst\) + printUsage + exit 1 +fi + +if ! [ -d "$src" ]; then + echo Error: Invalid source directory: \""$src"\" is missing + exit 1 +fi + +src=$(realpath "$src") + +if ! [ -d "$src"/l2chaindata ]; then + echo Error: Invalid source directory: \""$src"/l2chaindata\" is missing + exit 1 +fi + +if ! [ -d "$src"/l2chaindata/ancient ]; then + echo Error: Invalid source directory: \""$src"/l2chaindata/ancient\" is missing + exit 1 +fi + +if ! [ -d "$src"/arbitrumdata ]; then + echo Error: Invalid source directory: missing "$src/arbitrumdata" directory + exit 1 +fi + +if [ -e "$dst" ] && ! $skip_existing; then + if $force; then + echo == Warning! Destination already exists, --force is set, removing all files under path: "$dst" + removeDir "$dst" + if [ $? -ne 0 ]; then + echo Error: failed to remove "$dst" + exit 1 + fi + else + echo Error: invalid destination path: "$dst" already exists + exit 1 + fi +fi + +convert_result= +convert () { + srcdir=$(echo $src/$1 | tr -s /) + dstdir=$(echo $dst/$1 | tr -s /) + if ! [ -e $dstdir ]; then + echo "== Converting $1 db" + cmd="$dbconv --src.db-engine=leveldb --src.data \"$srcdir\" --dst.db-engine=pebble --dst.data \"$dstdir\" --convert --compact" + echo $cmd + eval $cmd + if [ $? -ne 0 ]; then + cleanup $1 + convert_result="FAILED" + return 1 + fi + convert_result="converted" + return 0 + else + if $skip_existing; then + echo "== Note: $dstdir directory already exists, skipping conversion (--skip-existing flag is set)" + convert_result="skipped" + return 0 + else + convert_result="FAILED ($dstdir already exists)" + return 1 + fi + fi +} + +convert "l2chaindata" +res=$? +l2chaindata_status=$convert_result +if [ $res -ne 0 ]; then + printStatus + exit 1 +fi + +if ! [ -e "$dst"/l2chaindata/ancient ]; then + ancient_src=$(echo "$src"/l2chaindata/ancient | tr -s /) + ancient_dst=$(echo "$dst"/l2chaindata/ | tr -s /) + echo "== Copying l2chaindata ancients" + cmd="cp -r \"$ancient_src\" \"$ancient_dst\"" + echo $cmd + eval $cmd + if [ $? -ne 0 ]; then + l2chaindata_ancient_status="FAILED (failed to copy)" + cleanup "l2chaindata" + printStatus + exit 1 + fi + l2chaindata_ancient_status="copied" +else + if $skip_existing; then + echo "== Note: l2chaindata/ancient directory already exists, skipping copy (--skip-existing flag is set)" + l2chaindata_ancient_status="skipped" + else + # unreachable, we already had to remove root directory + echo script error, reached unreachable + exit 1 + fi +fi + +convert "arbitrumdata" +res=$? +arbitrumdata_status=$convert_result +if [ $res -ne 0 ]; then + printStatus + exit 1 +fi + +if [ -e $src/wasm ]; then + convert "wasm" + res=$? + wasm_status=$convert_result + if [ $res -ne 0 ]; then + printStatus + exit 1 + fi +else + echo "== Note: Source directory does not contain wasm database." + wasm_status="not found in source directory" +fi + +if [ -e $src/classic-msg ]; then + convert "classic-msg" + res=$? + classicmsg_status=$convert_result + if [ $res -ne 0 ]; then + printStatus + exit 1 + fi +else + echo "== Note: Source directory does not contain classic-msg database." + classicmsg_status="not found in source directory" +fi + +printStatus diff --git a/scripts/split-val-entry.sh b/scripts/split-val-entry.sh index 8e1be0f6cc..42e0c5fe08 100755 --- a/scripts/split-val-entry.sh +++ b/scripts/split-val-entry.sh @@ -2,13 +2,36 @@ xxd -l 32 -ps -c 40 /dev/urandom > /tmp/nitro-val.jwt +legacyvalopts=() +latestvalopts=() +while [[ $1 == "--val-options"* ]]; do + setlegacy=true + setlatest=true + if [[ $1 == "--val-options-legacy" ]]; then + setlatest=false + fi + if [[ $1 == "--val-options-latest" ]]; then + setlegacy=false + fi + shift + while [[ "$1" != "--" ]] && [[ $# -gt 0 ]]; do + if $setlegacy; then + legacyvalopts=( "${legacyvalopts[@]}" "$1" ) + fi + if $setlatest; then + latestvalopts=( "${latestvalopts[@]}" "$1" ) + fi + shift + done + shift +done 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 & +/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 "${latestvalopts[@]}" & +/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 "${legacyvalopts[@]}" & for port in 52000 52001; do while ! nc -w1 -z 127.0.0.10 $port; do echo waiting for validation port $port @@ -16,4 +39,4 @@ for port in 52000 52001; do 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"}]' "$@" +/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":"ws://127.0.0.10:52000"}, {"jwtsecret":"/tmp/nitro-val.jwt","url":"ws://127.0.0.10:52001"}]' "$@" diff --git a/scripts/validate-wasm-module-root.sh b/scripts/validate-wasm-module-root.sh new file mode 100755 index 0000000000..f62a46a8d7 --- /dev/null +++ b/scripts/validate-wasm-module-root.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -e + +MACHINES_DIR=$1 +PROVER=$2 + +for machine in "$MACHINES_DIR"/*/ ; do + if [ -d "$machine" ]; then + expectedWasmModuleRoot=$(cat "$machine/module-root.txt") + actualWasmModuleRoot=$(cd "$machine" && "$PROVER" machine.wavm.br --print-wasmmoduleroot) + if [ "$expectedWasmModuleRoot" != "$actualWasmModuleRoot" ]; then + echo "Error: Expected module root $expectedWasmModuleRoot but found $actualWasmModuleRoot in $machine" + exit 1 + fi + fi +done diff --git a/solgen/gen.go b/solgen/gen.go index 92511595d7..2ad71b0c79 100644 --- a/solgen/gen.go +++ b/solgen/gen.go @@ -73,6 +73,18 @@ func main() { log.Fatal(err) } + filePathsSafeSmartAccount, err := filepath.Glob(filepath.Join(parent, "safe-smart-account", "build", "artifacts", "contracts", "*", "*.sol", "*.json")) + if err != nil { + log.Fatal(err) + } + filePathsSafeSmartAccountOuter, err := filepath.Glob(filepath.Join(parent, "safe-smart-account", "build", "artifacts", "contracts", "*.sol", "*.json")) + if err != nil { + log.Fatal(err) + } + + filePaths = append(filePaths, filePathsSafeSmartAccount...) + filePaths = append(filePaths, filePathsSafeSmartAccountOuter...) + modules := make(map[string]*moduleInfo) for _, path := range filePaths { diff --git a/staker/block_validator.go b/staker/block_validator.go index 94ee907da5..8f5724beac 100644 --- a/staker/block_validator.go +++ b/staker/block_validator.go @@ -8,6 +8,7 @@ import ( "encoding/json" "errors" "fmt" + "net/url" "regexp" "runtime" "sync" @@ -16,6 +17,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" @@ -34,7 +36,14 @@ var ( validatorPendingValidationsGauge = metrics.NewRegisteredGauge("arb/validator/validations/pending", nil) validatorValidValidationsCounter = metrics.NewRegisteredCounter("arb/validator/validations/valid", nil) validatorFailedValidationsCounter = metrics.NewRegisteredCounter("arb/validator/validations/failed", nil) + validatorProfileWaitToRecordHist = metrics.NewRegisteredHistogram("arb/validator/profile/wait_to_record", nil, metrics.NewBoundedHistogramSample()) + validatorProfileRecordingHist = metrics.NewRegisteredHistogram("arb/validator/profile/recording", nil, metrics.NewBoundedHistogramSample()) + validatorProfileWaitToLaunchHist = metrics.NewRegisteredHistogram("arb/validator/profile/wait_to_launch", nil, metrics.NewBoundedHistogramSample()) + validatorProfileLaunchingHist = metrics.NewRegisteredHistogram("arb/validator/profile/launching", nil, metrics.NewBoundedHistogramSample()) + validatorProfileRunningHist = metrics.NewRegisteredHistogram("arb/validator/profile/running", nil, metrics.NewBoundedHistogramSample()) validatorMsgCountCurrentBatch = metrics.NewRegisteredGauge("arb/validator/msg_count_current_batch", nil) + validatorMsgCountCreatedGauge = metrics.NewRegisteredGauge("arb/validator/msg_count_created", nil) + validatorMsgCountRecordSentGauge = metrics.NewRegisteredGauge("arb/validator/msg_count_record_sent", nil) validatorMsgCountValidatedGauge = metrics.NewRegisteredGauge("arb/validator/msg_count_validated", nil) ) @@ -64,9 +73,9 @@ type BlockValidator struct { // can be read (atomic.Load) by anyone holding reorg-read // written (atomic.Set) by appropriate thread or (any way) holding reorg-write - createdA uint64 - recordSentA uint64 - validatedA uint64 + createdA atomic.Uint64 + recordSentA atomic.Uint64 + validatedA atomic.Uint64 validations containers.SyncMap[arbutil.MessageIndex, *validationStatus] config BlockValidatorConfigFetcher @@ -118,6 +127,9 @@ func (c *BlockValidatorConfig) Validate() error { } c.memoryFreeLimit = limit } + if err := c.RedisValidationClientConfig.Validate(); err != nil { + return fmt.Errorf("failed to validate redis validation client config: %w", err) + } streamsEnabled := c.RedisValidationClientConfig.Enabled() if len(c.ValidationServerConfigs) == 0 { c.ValidationServerConfigs = []rpcclient.ClientConfig{c.ValidationServer} @@ -133,6 +145,16 @@ func (c *BlockValidatorConfig) Validate() error { 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) } + serverUrl := c.ValidationServerConfigs[i].URL + if len(serverUrl) > 0 && serverUrl != "self" && serverUrl != "self-auth" { + u, err := url.Parse(serverUrl) + if err != nil { + return fmt.Errorf("failed parsing validation server's url:%s err: %w", serverUrl, err) + } + if u.Scheme != "ws" && u.Scheme != "wss" { + return fmt.Errorf("validation server's url scheme is unsupported, it should either be ws or wss, url:%s", serverUrl) + } + } } return nil } @@ -208,19 +230,27 @@ const ( ) type validationStatus struct { - Status uint32 // atomic: value is one of validationStatus* - Cancel func() // non-atomic: only read/written to with reorg mutex - Entry *validationEntry // non-atomic: only read if Status >= validationStatusPrepared - Runs []validator.ValidationRun // if status >= ValidationSent + Status atomic.Uint32 // atomic: value is one of validationStatus* + Cancel func() // non-atomic: only read/written to with reorg mutex + Entry *validationEntry // non-atomic: only read if Status >= validationStatusPrepared + Runs []validator.ValidationRun // if status >= ValidationSent + profileTS int64 // time-stamp for profiling } func (s *validationStatus) getStatus() valStatusField { - uintStat := atomic.LoadUint32(&s.Status) + uintStat := s.Status.Load() return valStatusField(uintStat) } func (s *validationStatus) replaceStatus(old, new valStatusField) bool { - return atomic.CompareAndSwapUint32(&s.Status, uint32(old), uint32(new)) + return s.Status.CompareAndSwap(uint32(old), uint32(new)) +} + +// gets how many miliseconds last step took, and starts measuring a new step +func (s *validationStatus) profileStep() int64 { + start := s.profileTS + s.profileTS = time.Now().UnixMilli() + return s.profileTS - start } func NewBlockValidator( @@ -283,12 +313,13 @@ func NewBlockValidator( return ret, nil } -func atomicStorePos(addr *uint64, val arbutil.MessageIndex) { - atomic.StoreUint64(addr, uint64(val)) +func atomicStorePos(addr *atomic.Uint64, val arbutil.MessageIndex, metr metrics.Gauge) { + addr.Store(uint64(val)) + metr.Update(int64(val)) } -func atomicLoadPos(addr *uint64) arbutil.MessageIndex { - return arbutil.MessageIndex(atomic.LoadUint64(addr)) +func atomicLoadPos(addr *atomic.Uint64) arbutil.MessageIndex { + return arbutil.MessageIndex(addr.Load()) } func (v *BlockValidator) created() arbutil.MessageIndex { @@ -444,6 +475,8 @@ func (v *BlockValidator) sendRecord(s *validationStatus) error { if !s.replaceStatus(Created, RecordSent) { return fmt.Errorf("failed status check for send record. Status: %v", s.getStatus()) } + + validatorProfileWaitToRecordHist.Update(s.profileStep()) v.LaunchThread(func(ctx context.Context) { err := v.ValidationEntryRecord(ctx, s.Entry) if ctx.Err() != nil { @@ -454,6 +487,7 @@ func (v *BlockValidator) sendRecord(s *validationStatus) error { log.Error("Error while recording", "err", err, "status", s.getStatus()) return } + validatorProfileRecordingHist.Update(s.profileStep()) if !s.replaceStatus(RecordSent, Prepared) { log.Error("Fault trying to update validation with recording", "entry", s.Entry, "status", s.getStatus()) return @@ -465,7 +499,7 @@ func (v *BlockValidator) sendRecord(s *validationStatus) error { //nolint:gosec func (v *BlockValidator) writeToFile(validationEntry *validationEntry, moduleRoot common.Hash) error { - input, err := validationEntry.ToInput() + input, err := validationEntry.ToInput([]rawdb.Target{rawdb.TargetWavm}) if err != nil { return err } @@ -506,25 +540,6 @@ func (v *BlockValidator) SetCurrentWasmModuleRoot(hash common.Hash) error { ) } -func (v *BlockValidator) readBatch(ctx context.Context, batchNum uint64) (bool, []byte, common.Hash, arbutil.MessageIndex, error) { - batchCount, err := v.inboxTracker.GetBatchCount() - if err != nil { - return false, nil, common.Hash{}, 0, err - } - if batchCount <= batchNum { - return false, nil, common.Hash{}, 0, nil - } - batchMsgCount, err := v.inboxTracker.GetBatchMessageCount(batchNum) - if err != nil { - return false, nil, common.Hash{}, 0, err - } - batch, batchBlockHash, err := v.inboxReader.GetSequencerMessageBytes(ctx, batchNum) - if err != nil { - return false, nil, common.Hash{}, 0, err - } - return true, batch, batchBlockHash, batchMsgCount, nil -} - func (v *BlockValidator) createNextValidationEntry(ctx context.Context) (bool, error) { v.reorgMutex.RLock() defer v.reorgMutex.RUnlock() @@ -582,13 +597,14 @@ func (v *BlockValidator) createNextValidationEntry(ctx context.Context) (bool, e return false, err } status := &validationStatus{ - Status: uint32(Created), - Entry: entry, + Entry: entry, + profileTS: time.Now().UnixMilli(), } + status.Status.Store(uint32(Created)) v.validations.Store(pos, status) v.nextCreateStartGS = endGS v.nextCreatePrevDelayed = msg.DelayedMessagesRead - atomicStorePos(&v.createdA, pos+1) + atomicStorePos(&v.createdA, pos+1, validatorMsgCountCreatedGauge) log.Trace("create validation entry: created", "pos", pos) return true, nil } @@ -667,7 +683,7 @@ func (v *BlockValidator) sendNextRecordRequests(ctx context.Context) (bool, erro return false, err } pos += 1 - atomicStorePos(&v.recordSentA, pos) + atomicStorePos(&v.recordSentA, pos, validatorMsgCountRecordSentGauge) log.Trace("next record request: sent", "pos", pos) } @@ -778,11 +794,10 @@ validationsLoop: log.Error("failed writing new validated to database", "pos", pos, "err", err) } go v.recorder.MarkValid(pos, v.lastValidGS.BlockHash) - atomicStorePos(&v.validatedA, pos+1) + atomicStorePos(&v.validatedA, pos+1, validatorMsgCountValidatedGauge) v.validations.Delete(pos) nonBlockingTrigger(v.createNodesChan) nonBlockingTrigger(v.sendRecordChan) - validatorMsgCountValidatedGauge.Update(int64(pos + 1)) if v.testingProgressMadeChan != nil { nonBlockingTrigger(v.testingProgressMadeChan) } @@ -790,12 +805,13 @@ validationsLoop: continue } for _, moduleRoot := range wasmRoots { - if v.chosenValidator[moduleRoot] == nil { + spawner := v.chosenValidator[moduleRoot] + if spawner == nil { notFoundErr := fmt.Errorf("did not find spawner for moduleRoot :%v", moduleRoot) v.possiblyFatal(notFoundErr) return nil, notFoundErr } - if v.chosenValidator[moduleRoot].Room() == 0 { + if spawner.Room() == 0 { log.Trace("advanceValidations: no more room", "moduleRoot", moduleRoot) return nil, nil } @@ -805,28 +821,35 @@ validationsLoop: return nil, nil } if currentStatus == Prepared { - input, err := validationStatus.Entry.ToInput() - if err != nil && ctx.Err() == nil { - v.possiblyFatal(fmt.Errorf("%w: error preparing validation", err)) - continue - } replaced := validationStatus.replaceStatus(Prepared, SendingValidation) if !replaced { v.possiblyFatal(errors.New("failed to set SendingValidation status")) } + validatorProfileWaitToLaunchHist.Update(validationStatus.profileStep()) validatorPendingValidationsGauge.Inc(1) var runs []validator.ValidationRun for _, moduleRoot := range wasmRoots { - run := v.chosenValidator[moduleRoot].Launch(input, moduleRoot) + spawner := v.chosenValidator[moduleRoot] + input, err := validationStatus.Entry.ToInput(spawner.StylusArchs()) + if err != nil && ctx.Err() == nil { + v.possiblyFatal(fmt.Errorf("%w: error preparing validation", err)) + continue + } + if ctx.Err() != nil { + return nil, ctx.Err() + } + run := spawner.Launch(input, moduleRoot) log.Trace("advanceValidations: launched", "pos", validationStatus.Entry.Pos, "moduleRoot", moduleRoot) runs = append(runs, run) } + validatorProfileLaunchingHist.Update(validationStatus.profileStep()) validationCtx, cancel := context.WithCancel(ctx) validationStatus.Runs = runs validationStatus.Cancel = cancel v.LaunchUntrackedThread(func() { defer validatorPendingValidationsGauge.Dec(1) defer cancel() + startTsMilli := validationStatus.profileTS replaced = validationStatus.replaceStatus(SendingValidation, ValidationSent) if !replaced { v.possiblyFatal(errors.New("failed to set status to ValidationSent")) @@ -840,6 +863,7 @@ validationsLoop: return } } + validatorProfileRunningHist.Update(time.Now().UnixMilli() - startTsMilli) nonBlockingTrigger(v.progressValidationsChan) }) } @@ -962,13 +986,13 @@ func (v *BlockValidator) UpdateLatestStaked(count arbutil.MessageIndex, globalSt v.nextCreateStartGS = globalState v.nextCreatePrevDelayed = msg.DelayedMessagesRead v.nextCreateBatchReread = true - v.createdA = countUint64 + v.createdA.Store(countUint64) } // under the reorg mutex we don't need atomic access - if v.recordSentA < countUint64 { - v.recordSentA = countUint64 + if v.recordSentA.Load() < countUint64 { + v.recordSentA.Store(countUint64) } - v.validatedA = countUint64 + v.validatedA.Store(countUint64) v.valLoopPos = count validatorMsgCountValidatedGauge.Update(int64(countUint64)) err = v.writeLastValidated(globalState, nil) // we don't know which wasm roots were validated @@ -1027,13 +1051,13 @@ func (v *BlockValidator) Reorg(ctx context.Context, count arbutil.MessageIndex) v.nextCreatePrevDelayed = msg.DelayedMessagesRead v.nextCreateBatchReread = true countUint64 := uint64(count) - v.createdA = countUint64 + v.createdA.Store(countUint64) // under the reorg mutex we don't need atomic access - if v.recordSentA > countUint64 { - v.recordSentA = countUint64 + if v.recordSentA.Load() > countUint64 { + v.recordSentA.Store(countUint64) } - if v.validatedA > countUint64 { - v.validatedA = countUint64 + if v.validatedA.Load() > countUint64 { + v.validatedA.Store(countUint64) validatorMsgCountValidatedGauge.Update(int64(countUint64)) err := v.writeLastValidated(v.nextCreateStartGS, nil) // we don't know which wasm roots were validated if err != nil { @@ -1222,9 +1246,9 @@ func (v *BlockValidator) checkValidatedGSCaughtUp() (bool, error) { v.nextCreateBatchReread = true v.nextCreateStartGS = v.lastValidGS v.nextCreatePrevDelayed = msg.DelayedMessagesRead - atomicStorePos(&v.createdA, count) - atomicStorePos(&v.recordSentA, count) - atomicStorePos(&v.validatedA, count) + atomicStorePos(&v.createdA, count, validatorMsgCountCreatedGauge) + atomicStorePos(&v.recordSentA, count, validatorMsgCountRecordSentGauge) + atomicStorePos(&v.validatedA, count, validatorMsgCountValidatedGauge) validatorMsgCountValidatedGauge.Update(int64(count)) v.chainCaughtUp = true return true, nil diff --git a/staker/challenge-cache/cache.go b/staker/challenge-cache/cache.go index 8cca4bb835..ed4fad6450 100644 --- a/staker/challenge-cache/cache.go +++ b/staker/challenge-cache/cache.go @@ -10,7 +10,7 @@ store them in a filesystem cache to avoid recomputing them and for hierarchical Each file contains a list of 32 byte hashes, concatenated together as bytes. Using this structure, we can namespace hashes by message number and by challenge level. -Once a validator receives a full list of computed machine hashes for the first time from a validatio node, +Once a validator receives a full list of computed machine hashes for the first time from a validation node, it will write the hashes to this filesystem hierarchy for fast access next time these hashes are needed. Example uses: @@ -18,7 +18,7 @@ Example uses: - Obtain all the hashes from step 100 to 101 at subchallenge level 1 for the execution of message num 70. wavm-module-root-0xab/ - rollup-block-hash-0x12...-message-num-70/ + message-num-70-rollup-block-hash-0x12.../ hashes.bin subchallenge-level-1-big-step-100/ hashes.bin @@ -31,18 +31,21 @@ package challengecache import ( "bufio" + "context" "errors" "fmt" "io" "os" "path/filepath" + "regexp" + "strconv" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/log" ) var ( - ErrNotFoundInCache = errors.New("no found in challenge cache") + ErrNotFoundInCache = errors.New("not found in challenge cache") ErrFileAlreadyExists = errors.New("file already exists") ErrNoHashes = errors.New("no hashes being written") hashesFileName = "hashes.bin" @@ -59,30 +62,46 @@ type HistoryCommitmentCacher interface { Put(lookup *Key, hashes []common.Hash) error } +// Key for cache lookups includes the wavm module root of a challenge, as well +// as the heights for messages and big steps as needed. +type Key struct { + RollupBlockHash common.Hash + WavmModuleRoot common.Hash + MessageHeight uint64 + StepHeights []uint64 +} + // Cache for history commitments on disk. type Cache struct { - baseDir string + baseDir string + tempWritesDir string } // New cache from a base directory path. func New(baseDir string) (*Cache, error) { - if _, err := os.Stat(baseDir); err != nil { - if err := os.MkdirAll(baseDir, os.ModePerm); err != nil { - return nil, fmt.Errorf("could not make base cache directory %s: %w", baseDir, err) - } - } return &Cache{ - baseDir: baseDir, + baseDir: baseDir, + tempWritesDir: "", }, nil } -// Key for cache lookups includes the wavm module root of a challenge, as well -// as the heights for messages and big steps as needed. -type Key struct { - RollupBlockHash common.Hash - WavmModuleRoot common.Hash - MessageHeight uint64 - StepHeights []uint64 +// Init a cache by verifying its base directory exists. +func (c *Cache) Init(_ context.Context) error { + if _, err := os.Stat(c.baseDir); err != nil { + if err := os.MkdirAll(c.baseDir, os.ModePerm); err != nil { + return fmt.Errorf("could not make initialize challenge cache directory %s: %w", c.baseDir, err) + } + } + // We create a temp directory to write our hashes to first when putting to the cache. + // Once writing succeeds, we rename in an atomic operation to the correct file name + // in the cache directory hierarchy in the `Put` function. All of these temporary writes + // will occur in a subdir of the base directory called temp. + tempWritesDir, err := os.MkdirTemp(c.baseDir, "temp") + if err != nil { + return err + } + c.tempWritesDir = tempWritesDir + return nil } // Get a list of hashes from the cache from index 0 up to a certain index. Hashes are saved as files in the directory @@ -122,24 +141,14 @@ func (c *Cache) Put(lookup *Key, hashes []common.Hash) error { if len(hashes) == 0 { return ErrNoHashes } - fName, err := determineFilePath(c.baseDir, lookup) - if err != nil { - return err + if c.tempWritesDir == "" { + return fmt.Errorf("cache not initialized by calling .Init(ctx)") } - // We create a tmp file to write our hashes to first. If writing fails, - // we don't want to leave a half-written file in our cache directory. - // Once writing succeeds, we rename in an atomic operation to the correct file name - // in the cache directory hierarchy. - tmp, err := os.MkdirTemp(c.baseDir, "tmpdir") + fName, err := determineFilePath(c.baseDir, lookup) if err != nil { return err } - tmpFName := filepath.Join(tmp, fName) - dir := filepath.Dir(tmpFName) - if err := os.MkdirAll(dir, os.ModePerm); err != nil { - return fmt.Errorf("could not make tmp directory %s: %w", dir, err) - } - f, err := os.Create(tmpFName) + f, err := os.CreateTemp(c.tempWritesDir, fmt.Sprintf("%s-*", hashesFileName)) if err != nil { return err } @@ -154,11 +163,57 @@ func (c *Cache) Put(lookup *Key, hashes []common.Hash) error { if err := os.MkdirAll(filepath.Dir(fName), os.ModePerm); err != nil { return fmt.Errorf("could not make file directory %s: %w", fName, err) } - // If the file writing was successful, we rename the file from the tmp directory + // If the file writing was successful, we rename the file from the temp directory // into our cache directory. This is an atomic operation. // For more information on this atomic write pattern, see: // https://stackoverflow.com/questions/2333872/how-to-make-file-creation-an-atomic-operation - return os.Rename(tmpFName /*old */, fName /* new */) + return os.Rename(f.Name() /*old */, fName /* new */) +} + +// Prune all entries in the cache with a message number <= a specified value. +func (c *Cache) Prune(ctx context.Context, messageNumber uint64) error { + // Define a regex pattern to extract the message number + numPruned := 0 + messageNumPattern := fmt.Sprintf(`%s-(\d+)-`, messageNumberPrefix) + pattern := regexp.MustCompile(messageNumPattern) + pathsToDelete := make([]string, 0) + if err := filepath.WalkDir(c.baseDir, func(path string, info os.DirEntry, err error) error { + if ctx.Err() != nil { + return ctx.Err() + } + if err != nil { + return err + } + if info.IsDir() { + matches := pattern.FindStringSubmatch(info.Name()) + if len(matches) > 1 { + dirNameMessageNum, err := strconv.Atoi(matches[1]) + if err != nil { + return err + } + // Collect the directory path if the message number is <= the specified value. + if dirNameMessageNum <= int(messageNumber) { + pathsToDelete = append(pathsToDelete, path) + } + } + } + return nil + }); err != nil { + return err + } + // We delete separately from collecting the paths, as deleting while walking + // a dir can cause issues with the filepath.Walk function. + for _, path := range pathsToDelete { + if ctx.Err() != nil { + return ctx.Err() + } + if err := os.RemoveAll(path); err != nil { + return fmt.Errorf("could not prune directory with path %s: %w", path, err) + } + numPruned += 1 + } + log.Info("Pruned challenge cache", "numDirsPruned", numPruned, "messageNumber", messageNumPattern) + return nil } // Reads 32 bytes at a time from a reader up to a specified height. If none, then read all. @@ -217,7 +272,7 @@ for the data requested within the cache directory hierarchy. The folder structur for a given filesystem challenge cache will look as follows: wavm-module-root-0xab/ - rollup-block-hash-0x12...-message-num-70/ + message-num-70-rollup-block-hash-0x12.../ hashes.bin subchallenge-level-1-big-step-100/ hashes.bin @@ -225,7 +280,7 @@ for a given filesystem challenge cache will look as follows: func determineFilePath(baseDir string, lookup *Key) (string, error) { key := make([]string, 0) key = append(key, fmt.Sprintf("%s-%s", wavmModuleRootPrefix, lookup.WavmModuleRoot.Hex())) - key = append(key, fmt.Sprintf("%s-%s-%s-%d", rollupBlockHashPrefix, lookup.RollupBlockHash.Hex(), messageNumberPrefix, lookup.MessageHeight)) + key = append(key, fmt.Sprintf("%s-%d-%s-%s", messageNumberPrefix, lookup.MessageHeight, rollupBlockHashPrefix, lookup.RollupBlockHash.Hex())) for challengeLevel, height := range lookup.StepHeights { key = append(key, fmt.Sprintf( "%s-%d-%s-%d", diff --git a/staker/challenge-cache/cache_test.go b/staker/challenge-cache/cache_test.go index 6b15d62af7..af0a058f78 100644 --- a/staker/challenge-cache/cache_test.go +++ b/staker/challenge-cache/cache_test.go @@ -4,6 +4,7 @@ package challengecache import ( "bytes" + "context" "errors" "fmt" "io" @@ -17,19 +18,19 @@ import ( var _ HistoryCommitmentCacher = (*Cache)(nil) func TestCache(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() basePath := t.TempDir() if err := os.MkdirAll(basePath, os.ModePerm); err != nil { t.Fatal(err) } - t.Cleanup(func() { - if err := os.RemoveAll(basePath); err != nil { - t.Fatal(err) - } - }) cache, err := New(basePath) if err != nil { t.Fatal(err) } + if err = cache.Init(ctx); err != nil { + t.Fatal(err) + } key := &Key{ WavmModuleRoot: common.BytesToHash([]byte("foo")), MessageHeight: 0, @@ -69,6 +70,144 @@ func TestCache(t *testing.T) { } } +func TestPrune(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + basePath := t.TempDir() + if err := os.MkdirAll(basePath, os.ModePerm); err != nil { + t.Fatal(err) + } + cache, err := New(basePath) + if err != nil { + t.Fatal(err) + } + if err = cache.Init(ctx); err != nil { + t.Fatal(err) + } + key := &Key{ + WavmModuleRoot: common.BytesToHash([]byte("foo")), + MessageHeight: 20, + StepHeights: []uint64{0}, + } + if _, err = cache.Get(key, 0); !errors.Is(err, ErrNotFoundInCache) { + t.Fatal(err) + } + t.Run("pruning non-existent dirs does nothing", func(t *testing.T) { + if err = cache.Prune(ctx, key.MessageHeight); err != nil { + t.Error(err) + } + }) + t.Run("pruning single item", func(t *testing.T) { + want := []common.Hash{ + common.BytesToHash([]byte("foo")), + common.BytesToHash([]byte("bar")), + common.BytesToHash([]byte("baz")), + } + err = cache.Put(key, want) + if err != nil { + t.Fatal(err) + } + items, err := cache.Get(key, 3) + if err != nil { + t.Fatal(err) + } + if len(items) != len(want) { + t.Fatalf("Wrong number of hashes. Expected %d, got %d", len(want), len(items)) + } + if err = cache.Prune(ctx, key.MessageHeight); err != nil { + t.Error(err) + } + if _, err = cache.Get(key, 3); !errors.Is(err, ErrNotFoundInCache) { + t.Error(err) + } + }) + t.Run("does not prune items with message number > N", func(t *testing.T) { + want := []common.Hash{ + common.BytesToHash([]byte("foo")), + common.BytesToHash([]byte("bar")), + common.BytesToHash([]byte("baz")), + } + key.MessageHeight = 30 + err = cache.Put(key, want) + if err != nil { + t.Fatal(err) + } + items, err := cache.Get(key, 3) + if err != nil { + t.Fatal(err) + } + if len(items) != len(want) { + t.Fatalf("Wrong number of hashes. Expected %d, got %d", len(want), len(items)) + } + if err = cache.Prune(ctx, 20); err != nil { + t.Error(err) + } + items, err = cache.Get(key, 3) + if err != nil { + t.Fatal(err) + } + if len(items) != len(want) { + t.Fatalf("Wrong number of hashes. Expected %d, got %d", len(want), len(items)) + } + }) + t.Run("prunes many items with message number <= N", func(t *testing.T) { + moduleRoots := []common.Hash{ + common.BytesToHash([]byte("foo")), + common.BytesToHash([]byte("bar")), + common.BytesToHash([]byte("baz")), + } + totalMessages := 10 + for _, root := range moduleRoots { + for i := 0; i < totalMessages; i++ { + hashes := []common.Hash{ + common.BytesToHash([]byte("a")), + common.BytesToHash([]byte("b")), + common.BytesToHash([]byte("c")), + } + key = &Key{ + WavmModuleRoot: root, + MessageHeight: uint64(i), + StepHeights: []uint64{0}, + } + if err = cache.Put(key, hashes); err != nil { + t.Fatal(err) + } + } + } + if err = cache.Prune(ctx, 5); err != nil { + t.Error(err) + } + for _, root := range moduleRoots { + // Expect that we deleted all entries with message number <= 5 + for i := 0; i <= 5; i++ { + key = &Key{ + WavmModuleRoot: root, + MessageHeight: uint64(i), + StepHeights: []uint64{0}, + } + if _, err = cache.Get(key, 3); !errors.Is(err, ErrNotFoundInCache) { + t.Error(err) + } + } + // But also expect that we kept all entries with message number > 5 + for i := 6; i < totalMessages; i++ { + key = &Key{ + WavmModuleRoot: root, + MessageHeight: uint64(i), + StepHeights: []uint64{0}, + } + items, err := cache.Get(key, 3) + if err != nil { + t.Error(err) + } + if len(items) != 3 { + t.Fatalf("Wrong number of hashes. Expected %d, got %d", 3, len(items)) + } + } + } + }) +} + func TestReadWriteStatehashes(t *testing.T) { t.Run("read up to, but had empty reader", func(t *testing.T) { b := bytes.NewBuffer([]byte{}) @@ -255,7 +394,7 @@ func Test_determineFilePath(t *testing.T) { StepHeights: []uint64{50}, }, }, - want: "wavm-module-root-0x0000000000000000000000000000000000000000000000000000000000000000/rollup-block-hash-0x0000000000000000000000000000000000000000000000000000000000000000-message-num-100/subchallenge-level-1-big-step-50/hashes.bin", + want: "wavm-module-root-0x0000000000000000000000000000000000000000000000000000000000000000/message-num-100-rollup-block-hash-0x0000000000000000000000000000000000000000000000000000000000000000/subchallenge-level-1-big-step-50/hashes.bin", wantErr: false, }, } @@ -282,20 +421,20 @@ func Test_determineFilePath(t *testing.T) { } func BenchmarkCache_Read_32Mb(b *testing.B) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() b.StopTimer() basePath := os.TempDir() if err := os.MkdirAll(basePath, os.ModePerm); err != nil { b.Fatal(err) } - b.Cleanup(func() { - if err := os.RemoveAll(basePath); err != nil { - b.Fatal(err) - } - }) cache, err := New(basePath) if err != nil { b.Fatal(err) } + if err = cache.Init(ctx); err != nil { + b.Fatal(err) + } key := &Key{ WavmModuleRoot: common.BytesToHash([]byte("foo")), MessageHeight: 0, diff --git a/staker/challenge_manager.go b/staker/challenge_manager.go index 22897e3c1d..b1421d7e41 100644 --- a/staker/challenge_manager.go +++ b/staker/challenge_manager.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/rpc" @@ -467,7 +468,7 @@ func (m *ChallengeManager) createExecutionBackend(ctx context.Context, step uint if err != nil { return fmt.Errorf("error creating validation entry for challenge %v msg %v for execution challenge: %w", m.challengeIndex, initialCount, err) } - input, err := entry.ToInput() + input, err := entry.ToInput([]rawdb.Target{rawdb.TargetWavm}) if err != nil { return fmt.Errorf("error getting validation entry input of challenge %v msg %v: %w", m.challengeIndex, initialCount, err) } diff --git a/staker/fast_confirm.go b/staker/fast_confirm.go new file mode 100644 index 0000000000..88f457f528 --- /dev/null +++ b/staker/fast_confirm.go @@ -0,0 +1,253 @@ +// Copyright 2023-2024, Offchain Labs, Inc. +// For license information, see https://github.com/OffchainLabs/nitro/blob/master/LICENSE + +package staker + +import ( + "context" + "errors" + "fmt" + "math/big" + "sort" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/log" + + "github.com/offchainlabs/nitro/solgen/go/contractsgen" + "github.com/offchainlabs/nitro/solgen/go/rollupgen" + "github.com/offchainlabs/nitro/staker/txbuilder" + "github.com/offchainlabs/nitro/util/headerreader" +) + +type FastConfirmSafe struct { + safe *contractsgen.Safe + owners []common.Address + threshold uint64 + fastConfirmNextNodeMethod abi.Method + builder *txbuilder.Builder + wallet ValidatorWalletInterface + gasRefunder common.Address + l1Reader *headerreader.HeaderReader +} + +func NewFastConfirmSafe( + callOpts *bind.CallOpts, + fastConfirmSafeAddress common.Address, + builder *txbuilder.Builder, + wallet ValidatorWalletInterface, + gasRefunder common.Address, + l1Reader *headerreader.HeaderReader, +) (*FastConfirmSafe, error) { + fastConfirmSafe := &FastConfirmSafe{ + builder: builder, + wallet: wallet, + gasRefunder: gasRefunder, + l1Reader: l1Reader, + } + safe, err := contractsgen.NewSafe(fastConfirmSafeAddress, builder) + if err != nil { + return nil, err + } + fastConfirmSafe.safe = safe + owners, err := safe.GetOwners(callOpts) + if err != nil { + return nil, fmt.Errorf("calling getOwners: %w", err) + } + + // This is needed because safe contract needs owners to be sorted. + sort.Slice(owners, func(i, j int) bool { + return owners[i].Cmp(owners[j]) < 0 + }) + fastConfirmSafe.owners = owners + threshold, err := safe.GetThreshold(callOpts) + if err != nil { + return nil, fmt.Errorf("calling getThreshold: %w", err) + } + fastConfirmSafe.threshold = threshold.Uint64() + rollupUserLogicAbi, err := rollupgen.RollupUserLogicMetaData.GetAbi() + if err != nil { + return nil, err + } + fastConfirmNextNodeMethod, ok := rollupUserLogicAbi.Methods["fastConfirmNextNode"] + if !ok { + return nil, errors.New("RollupUserLogic ABI missing fastConfirmNextNode method") + } + fastConfirmSafe.fastConfirmNextNodeMethod = fastConfirmNextNodeMethod + return fastConfirmSafe, nil +} + +func (f *FastConfirmSafe) tryFastConfirmation(ctx context.Context, blockHash common.Hash, sendRoot common.Hash, nodeHash common.Hash) error { + if f.wallet.Address() == nil { + return errors.New("fast confirmation requires a wallet which is not setup") + } + fastConfirmCallData, err := f.createFastConfirmCalldata(blockHash, sendRoot, nodeHash) + if err != nil { + return err + } + callOpts := &bind.CallOpts{Context: ctx} + // Current nonce of the safe. + nonce, err := f.safe.Nonce(callOpts) + if err != nil { + return err + } + // Hash of the safe transaction. + safeTxHash, err := f.safe.GetTransactionHash( + callOpts, + f.wallet.RollupAddress(), + big.NewInt(0), + fastConfirmCallData, + 0, + big.NewInt(0), + big.NewInt(0), + big.NewInt(0), + common.Address{}, + common.Address{}, + nonce, + ) + if err != nil { + return err + } + if !f.wallet.CanBatchTxs() { + err = f.flushTransactions(ctx) + if err != nil { + return err + } + } + + alreadyApproved, err := f.safe.ApprovedHashes(&bind.CallOpts{Context: ctx}, *f.wallet.Address(), safeTxHash) + if err != nil { + return err + } + if alreadyApproved.Cmp(common.Big1) == 0 { + _, err = f.checkApprovedHashAndExecTransaction(ctx, fastConfirmCallData, safeTxHash) + return err + } + + auth, err := f.builder.Auth(ctx) + if err != nil { + return err + } + _, err = f.safe.ApproveHash(auth, safeTxHash) + if err != nil { + return err + } + if !f.wallet.CanBatchTxs() { + err = f.flushTransactions(ctx) + if err != nil { + return err + } + } + executedTx, err := f.checkApprovedHashAndExecTransaction(ctx, fastConfirmCallData, safeTxHash) + if err != nil { + return err + } + if executedTx { + return nil + } + // If the transaction was not executed, we need to flush the transactions (for approve hash) and try again. + // This is because the hash might have been approved by another wallet in the same block, + // which might have led to a race condition. + err = f.flushTransactions(ctx) + if err != nil { + return err + } + _, err = f.checkApprovedHashAndExecTransaction(ctx, fastConfirmCallData, safeTxHash) + return err +} + +func (f *FastConfirmSafe) flushTransactions(ctx context.Context) error { + arbTx, err := f.wallet.ExecuteTransactions(ctx, f.builder, f.gasRefunder) + if err != nil { + return err + } + if arbTx != nil { + _, err = f.l1Reader.WaitForTxApproval(ctx, arbTx) + if err == nil { + log.Info("successfully executed staker transaction", "hash", arbTx.Hash()) + } else { + return fmt.Errorf("error waiting for tx receipt: %w", err) + } + } + f.builder.ClearTransactions() + return nil +} + +func (f *FastConfirmSafe) createFastConfirmCalldata( + blockHash common.Hash, sendRoot common.Hash, nodeHash common.Hash, +) ([]byte, error) { + calldata, err := f.fastConfirmNextNodeMethod.Inputs.Pack( + blockHash, + sendRoot, + nodeHash, + ) + if err != nil { + return nil, err + } + fullCalldata := append([]byte{}, f.fastConfirmNextNodeMethod.ID...) + fullCalldata = append(fullCalldata, calldata...) + return fullCalldata, nil +} + +func (f *FastConfirmSafe) checkApprovedHashAndExecTransaction(ctx context.Context, fastConfirmCallData []byte, safeTxHash [32]byte) (bool, error) { + if f.wallet.Address() == nil { + return false, errors.New("wallet address is nil") + } + var signatures []byte + approvedHashCount := uint64(0) + for _, owner := range f.owners { + var approved *big.Int + // No need check if wallet has approved the hash, + // since checkApprovedHashAndExecTransaction is called only after wallet has approved the hash. + if *f.wallet.Address() == owner { + approved = common.Big1 + } else { + var err error + approved, err = f.safe.ApprovedHashes(&bind.CallOpts{Context: ctx}, owner, safeTxHash) + if err != nil { + return false, err + } + } + + // If the owner has approved the hash, we add the signature to the transaction. + // We add the signature in the format r, s, v. + // We set v to 1, as it is the only possible value for a approved hash. + // We set r to the owner's address. + // We set s to the empty hash. + // Refer to the Safe contract for more information. + if approved.Cmp(common.Big1) == 0 { + approvedHashCount++ + v := uint8(1) + r := common.BytesToHash(owner.Bytes()) + s := common.Hash{} + signatures = append(signatures, r.Bytes()...) + signatures = append(signatures, s.Bytes()...) + signatures = append(signatures, v) + } + } + if approvedHashCount >= f.threshold { + auth, err := f.builder.Auth(ctx) + if err != nil { + return false, err + } + _, err = f.safe.ExecTransaction( + auth, + f.wallet.RollupAddress(), + big.NewInt(0), + fastConfirmCallData, + 0, + big.NewInt(0), + big.NewInt(0), + big.NewInt(0), + common.Address{}, + common.Address{}, + signatures, + ) + if err != nil { + return false, err + } + return true, nil + } + return false, nil +} diff --git a/staker/staker.go b/staker/staker.go index 24f5dc61e3..c54e74be37 100644 --- a/staker/staker.go +++ b/staker/staker.go @@ -18,11 +18,13 @@ import ( "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/ethereum/go-ethereum/rpc" + "github.com/google/btree" flag "github.com/spf13/pflag" "github.com/offchainlabs/nitro/arbnode/dataposter" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/cmd/genericconf" + "github.com/offchainlabs/nitro/solgen/go/rollupgen" "github.com/offchainlabs/nitro/staker/txbuilder" "github.com/offchainlabs/nitro/util" "github.com/offchainlabs/nitro/util/arbmath" @@ -91,6 +93,7 @@ type L1ValidatorConfig struct { Dangerous DangerousConfig `koanf:"dangerous"` ParentChainWallet genericconf.WalletConfig `koanf:"parent-chain-wallet"` LogQueryBatchSize uint64 `koanf:"log-query-batch-size" reload:"hot"` + EnableFastConfirmation bool `koanf:"enable-fast-confirmation"` strategy StakerStrategy gasRefunder common.Address @@ -120,7 +123,7 @@ func (c *L1ValidatorConfig) ValidatorRequired() bool { if c.Dangerous.WithoutBlockValidator { return false } - if c.strategy == WatchtowerStrategy { + if c.strategy == WatchtowerStrategy && !c.EnableFastConfirmation { return false } return true @@ -139,6 +142,8 @@ func (c *L1ValidatorConfig) Validate() error { return nil } +type L1ValidatorConfigFetcher func() *L1ValidatorConfig + var DefaultL1ValidatorConfig = L1ValidatorConfig{ Enable: true, Strategy: "Watchtower", @@ -158,6 +163,7 @@ var DefaultL1ValidatorConfig = L1ValidatorConfig{ Dangerous: DefaultDangerousConfig, ParentChainWallet: DefaultValidatorL1WalletConfig, LogQueryBatchSize: 0, + EnableFastConfirmation: false, } var TestL1ValidatorConfig = L1ValidatorConfig{ @@ -179,6 +185,7 @@ var TestL1ValidatorConfig = L1ValidatorConfig{ Dangerous: DefaultDangerousConfig, ParentChainWallet: DefaultValidatorL1WalletConfig, LogQueryBatchSize: 0, + EnableFastConfirmation: false, } var DefaultValidatorL1WalletConfig = genericconf.WalletConfig{ @@ -208,6 +215,7 @@ func L1ValidatorConfigAddOptions(prefix string, f *flag.FlagSet) { dataposter.DataPosterConfigAddOptions(prefix+".data-poster", f, dataposter.DefaultDataPosterConfigForValidator) DangerousConfigAddOptions(prefix+".dangerous", f) genericconf.WalletConfigAddOptions(prefix+".parent-chain-wallet", f, DefaultL1ValidatorConfig.ParentChainWallet.Pathname) + f.Bool(prefix+".enable-fast-confirmation", DefaultL1ValidatorConfig.EnableFastConfirmation, "enable fast confirmation") } type DangerousConfig struct { @@ -238,6 +246,11 @@ type LatestConfirmedNotifier interface { UpdateLatestConfirmed(count arbutil.MessageIndex, globalState validator.GoGlobalState) } +type validatedNode struct { + number uint64 + hash common.Hash +} + type Staker struct { *L1Validator stopwaiter.StopWaiter @@ -246,14 +259,17 @@ type Staker struct { confirmedNotifiers []LatestConfirmedNotifier activeChallenge *ChallengeManager baseCallOpts bind.CallOpts - config L1ValidatorConfig + config L1ValidatorConfigFetcher highGasBlocksBuffer *big.Int lastActCalledBlock *big.Int inactiveLastCheckedNode *nodeAndHash + inactiveValidatedNodes *btree.BTreeG[validatedNode] bringActiveUntilNode uint64 inboxReader InboxReaderInterface statelessBlockValidator *StatelessBlockValidator fatalErr chan<- error + enableFastConfirmation bool + fastConfirmSafe *FastConfirmSafe } type ValidatorWalletInterface interface { @@ -281,7 +297,7 @@ func NewStaker( l1Reader *headerreader.HeaderReader, wallet ValidatorWalletInterface, callOpts bind.CallOpts, - config L1ValidatorConfig, + config L1ValidatorConfigFetcher, blockValidator *BlockValidator, statelessBlockValidator *StatelessBlockValidator, stakedNotifiers []LatestStakedNotifier, @@ -290,7 +306,7 @@ func NewStaker( fatalErr chan<- error, ) (*Staker, error) { - if err := config.Validate(); err != nil { + if err := config().Validate(); err != nil { return nil, err } client := l1Reader.Client() @@ -300,9 +316,12 @@ func NewStaker( return nil, err } stakerLastSuccessfulActionGauge.Update(time.Now().Unix()) - if config.StartValidationFromStaked && blockValidator != nil { + if config().StartValidationFromStaked && blockValidator != nil { stakedNotifiers = append(stakedNotifiers, blockValidator) } + inactiveValidatedNodes := btree.NewG(2, func(a, b validatedNode) bool { + return a.number < b.number || (a.number == b.number && a.hash.Cmp(b.hash) < 0) + }) return &Staker{ L1Validator: val, l1Reader: l1Reader, @@ -310,11 +329,12 @@ func NewStaker( confirmedNotifiers: confirmedNotifiers, baseCallOpts: callOpts, config: config, - highGasBlocksBuffer: big.NewInt(config.PostingStrategy.HighGasDelayBlocks), + highGasBlocksBuffer: big.NewInt(config().PostingStrategy.HighGasDelayBlocks), lastActCalledBlock: nil, inboxReader: statelessBlockValidator.inboxReader, statelessBlockValidator: statelessBlockValidator, fatalErr: fatalErr, + inactiveValidatedNodes: inactiveValidatedNodes, }, nil } @@ -327,7 +347,7 @@ func (s *Staker) Initialize(ctx context.Context) error { if walletAddressOrZero != (common.Address{}) { s.updateStakerBalanceMetric(ctx) } - if s.blockValidator != nil && s.config.StartValidationFromStaked { + if s.blockValidator != nil && s.config().StartValidationFromStaked { latestStaked, _, err := s.validatorUtils.LatestStaked(&s.baseCallOpts, s.rollupAddress, walletAddressOrZero) if err != nil { return err @@ -344,9 +364,91 @@ func (s *Staker) Initialize(ctx context.Context) error { return s.blockValidator.InitAssumeValid(stakedInfo.AfterState().GlobalState) } + return s.setupFastConfirmation(ctx) +} + +// setupFastConfirmation sets the enableFastConfirmation and fastConfirmSafe variables of staker +// based on the config, the wallet address, and the on-chain rollup designated fast confirmer. +// Before this function, both variables should be their default (i.e. fast confirmation is disabled). +func (s *Staker) setupFastConfirmation(ctx context.Context) error { + cfg := s.config() + if !cfg.EnableFastConfirmation { + return nil + } + if s.wallet.Address() == nil { + return errors.New("fast confirmation requires wallet setup") + } + walletAddress := *s.wallet.Address() + client := s.l1Reader.Client() + rollup, err := rollupgen.NewRollupUserLogic(s.rollupAddress, client) + if err != nil { + return err + } + callOpts := s.getCallOpts(ctx) + fastConfirmer, err := rollup.AnyTrustFastConfirmer(callOpts) + if err != nil { + return fmt.Errorf("getting rollup fast confirmer address: %w", err) + } + if fastConfirmer == walletAddress { + // We can directly fast confirm nodes + s.enableFastConfirmation = true + return nil + } else if fastConfirmer == (common.Address{}) { + // No fast confirmer enabled + return errors.New("fast confirmation enabled in config, but no fast confirmer set in rollup contract") + } + // The fast confirmer address is a contract address, not sure if it's a safe contract yet. + fastConfirmSafe, err := NewFastConfirmSafe( + callOpts, + fastConfirmer, + s.builder, + s.wallet, + cfg.gasRefunder, + s.l1Reader, + ) + if err != nil { + // Unknown while loading the safe contract. + return fmt.Errorf("loading fast confirm safe: %w", err) + } + // Fast confirmer address implements getOwners() and is probably a safe. + isOwner, err := fastConfirmSafe.safe.IsOwner(callOpts, walletAddress) + if err != nil { + return fmt.Errorf("checking if wallet is owner of safe: %w", err) + } + if !isOwner { + return fmt.Errorf("staker wallet address %v is not an owner of the fast confirm safe %v", walletAddress, fastConfirmer) + } + s.enableFastConfirmation = true + s.fastConfirmSafe = fastConfirmSafe return nil } +func (s *Staker) tryFastConfirmationNodeNumber(ctx context.Context, number uint64, hash common.Hash) error { + if !s.enableFastConfirmation { + return nil + } + nodeInfo, err := s.rollup.LookupNode(ctx, number) + if err != nil { + return err + } + return s.tryFastConfirmation(ctx, nodeInfo.AfterState().GlobalState.BlockHash, nodeInfo.AfterState().GlobalState.SendRoot, hash) +} + +func (s *Staker) tryFastConfirmation(ctx context.Context, blockHash common.Hash, sendRoot common.Hash, nodeHash common.Hash) error { + if !s.enableFastConfirmation { + return nil + } + if s.fastConfirmSafe != nil { + return s.fastConfirmSafe.tryFastConfirmation(ctx, blockHash, sendRoot, nodeHash) + } + auth, err := s.builder.Auth(ctx) + if err != nil { + return err + } + _, err = s.rollup.FastConfirmNextNode(auth, blockHash, sendRoot, nodeHash) + return err +} + func (s *Staker) getLatestStakedState(ctx context.Context, staker common.Address) (uint64, arbutil.MessageIndex, *validator.GoGlobalState, error) { callOpts := s.getCallOpts(ctx) if s.l1Reader.UseFinalityData() { @@ -417,8 +519,9 @@ func (s *Staker) Start(ctxIn context.Context) { } }() var err error - if common.HexToAddress(s.config.GasRefunderAddress) != (common.Address{}) { - gasRefunderBalance, err := s.client.BalanceAt(ctx, common.HexToAddress(s.config.GasRefunderAddress), nil) + cfg := s.config() + if common.HexToAddress(cfg.GasRefunderAddress) != (common.Address{}) { + gasRefunderBalance, err := s.client.BalanceAt(ctx, common.HexToAddress(cfg.GasRefunderAddress), nil) if err != nil { log.Warn("error fetching validator gas refunder balance", "err", err) } else { @@ -447,7 +550,7 @@ func (s *Staker) Start(ctxIn context.Context) { // Try to create another tx return 0 } - return s.config.StakerInterval + return cfg.StakerInterval } stakerActionFailureCounter.Inc(1) backoff *= 2 @@ -488,7 +591,7 @@ func (s *Staker) Start(ctxIn context.Context) { notifier.UpdateLatestConfirmed(confirmedMsgCount, *confirmedGlobalState) } } - return s.config.StakerInterval + return s.config().StakerInterval }) } @@ -509,6 +612,7 @@ func (s *Staker) IsWhitelisted(ctx context.Context) (bool, error) { } func (s *Staker) shouldAct(ctx context.Context) bool { + cfg := s.config() var gasPriceHigh = false var gasPriceFloat float64 gasPrice, err := s.client.SuggestGasPrice(ctx) @@ -516,7 +620,7 @@ func (s *Staker) shouldAct(ctx context.Context) bool { log.Warn("error getting gas price", "err", err) } else { gasPriceFloat = float64(gasPrice.Int64()) / 1e9 - if gasPriceFloat >= s.config.PostingStrategy.HighGasThreshold { + if gasPriceFloat >= cfg.PostingStrategy.HighGasThreshold { gasPriceHigh = true } } @@ -541,14 +645,14 @@ func (s *Staker) shouldAct(ctx context.Context) bool { // Clamp `s.highGasBlocksBuffer` to between 0 and HighGasDelayBlocks if s.highGasBlocksBuffer.Sign() < 0 { s.highGasBlocksBuffer.SetInt64(0) - } else if s.highGasBlocksBuffer.Cmp(big.NewInt(s.config.PostingStrategy.HighGasDelayBlocks)) > 0 { - s.highGasBlocksBuffer.SetInt64(s.config.PostingStrategy.HighGasDelayBlocks) + } else if s.highGasBlocksBuffer.Cmp(big.NewInt(cfg.PostingStrategy.HighGasDelayBlocks)) > 0 { + s.highGasBlocksBuffer.SetInt64(cfg.PostingStrategy.HighGasDelayBlocks) } if gasPriceHigh && s.highGasBlocksBuffer.Sign() > 0 { log.Warn( "not acting yet as gas price is high", "gasPrice", gasPriceFloat, - "highGasPriceConfig", s.config.PostingStrategy.HighGasThreshold, + "highGasPriceConfig", cfg.PostingStrategy.HighGasThreshold, "highGasBuffer", s.highGasBlocksBuffer, ) return false @@ -579,7 +683,8 @@ func (s *Staker) confirmDataPosterIsReady(ctx context.Context) error { } func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { - if s.config.strategy != WatchtowerStrategy { + cfg := s.config() + if cfg.strategy != WatchtowerStrategy { err := s.confirmDataPosterIsReady(ctx) if err != nil { return nil, err @@ -633,7 +738,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { StakeExists: rawInfo != nil, } - effectiveStrategy := s.config.strategy + effectiveStrategy := cfg.strategy nodesLinear, err := s.validatorUtils.AreUnresolvedNodesLinear(callOpts, s.rollupAddress) if err != nil { return nil, fmt.Errorf("error checking for rollup assertion fork: %w", err) @@ -661,11 +766,68 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { info.LatestStakedNodeHash = s.inactiveLastCheckedNode.hash } + if cfg.EnableFastConfirmation { + firstUnresolvedNode, err := s.rollup.FirstUnresolvedNode(callOpts) + if err != nil { + return nil, err + } + if info.LatestStakedNode >= firstUnresolvedNode { + lastHeader, err := s.l1Reader.LastHeader(ctx) + if err != nil { + return nil, err + } + // To check if a node is correct, we simply check if we're staked on it. + // Since we're staked on it or a later node, this will tell us if it's correct. + // To keep this call consistent with the GetNode call, we pin a specific parent chain block hash. + checkNodeCorrectCallOpts := s.getCallOpts(ctx) + checkNodeCorrectCallOpts.BlockHash = lastHeader.ParentHash + nodeInfo, err := s.rollup.GetNode(checkNodeCorrectCallOpts, firstUnresolvedNode) + if err != nil { + return nil, err + } + validatedNode, haveValidated := s.inactiveValidatedNodes.Get(validatedNode{ + number: firstUnresolvedNode, + hash: nodeInfo.NodeHash, + }) + confirmedCorrect := haveValidated && validatedNode.hash == nodeInfo.NodeHash + if !confirmedCorrect { + stakedOnNode, err := s.rollup.NodeHasStaker(checkNodeCorrectCallOpts, firstUnresolvedNode, walletAddressOrZero) + if err != nil { + return nil, err + } + confirmedCorrect = stakedOnNode + } + if confirmedCorrect { + err = s.tryFastConfirmationNodeNumber(ctx, firstUnresolvedNode, nodeInfo.NodeHash) + if err != nil { + return nil, err + } + if s.builder.BuildingTransactionCount() > 0 { + // Try to fast confirm previous nodes before working on new ones + log.Info("fast confirming previous node", "node", firstUnresolvedNode) + return s.wallet.ExecuteTransactions(ctx, s.builder, cfg.gasRefunder) + } + } + } + } + latestConfirmedNode, err := s.rollup.LatestConfirmed(callOpts) if err != nil { return nil, fmt.Errorf("error getting latest confirmed node: %w", err) } + // Clear s.inactiveValidatedNodes of any entries before or equal to latestConfirmedNode + for { + validatedNode, ok := s.inactiveValidatedNodes.Min() + if !ok { + break + } + if validatedNode.number > latestConfirmedNode { + break + } + s.inactiveValidatedNodes.DeleteMin() + } + requiredStakeElevated, err := s.isRequiredStakeElevated(ctx) if err != nil { return nil, fmt.Errorf("error checking if required stake is elevated: %w", err) @@ -730,7 +892,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { return nil, fmt.Errorf("error withdrawing staker funds from our staker %v: %w", walletAddressOrZero, err) } log.Info("removing old stake and withdrawing funds") - return s.wallet.ExecuteTransactions(ctx, s.builder, s.config.gasRefunder) + return s.wallet.ExecuteTransactions(ctx, s.builder, cfg.gasRefunder) } } @@ -784,7 +946,7 @@ func (s *Staker) Act(ctx context.Context) (*types.Transaction, error) { if info.StakerInfo == nil && info.StakeExists { log.Info("staking to execute transactions") } - return s.wallet.ExecuteTransactions(ctx, s.builder, s.config.gasRefunder) + return s.wallet.ExecuteTransactions(ctx, s.builder, cfg.gasRefunder) } func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { @@ -810,7 +972,7 @@ func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { *info.CurrentChallenge, s.statelessBlockValidator, latestConfirmedCreated, - s.config.ConfirmationBlocks, + s.config().ConfirmationBlocks, ) if err != nil { return fmt.Errorf("error creating challenge manager: %w", err) @@ -824,8 +986,9 @@ func (s *Staker) handleConflict(ctx context.Context, info *StakerInfo) error { } func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiveStrategy StakerStrategy) error { + cfg := s.config() active := effectiveStrategy >= StakeLatestStrategy - action, wrongNodesExist, err := s.generateNodeAction(ctx, info, effectiveStrategy, &s.config) + action, wrongNodesExist, err := s.generateNodeAction(ctx, info, effectiveStrategy, cfg) if err != nil { return fmt.Errorf("error generating node action: %w", err) } @@ -839,7 +1002,7 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv switch action := action.(type) { case createNodeAction: - if wrongNodesExist && s.config.DisableChallenge { + if wrongNodesExist && cfg.DisableChallenge { log.Error("refusing to challenge assertion as config disables challenges") info.CanProgress = false return nil @@ -850,6 +1013,7 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv s.bringActiveUntilNode = info.LatestStakedNode + 1 } info.CanProgress = false + // We can't fast confirm a node that doesn't exist return nil } @@ -868,7 +1032,7 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv if err != nil { return fmt.Errorf("error staking on new node: %w", err) } - return nil + return s.tryFastConfirmation(ctx, action.assertion.AfterState.GlobalState.BlockHash, action.assertion.AfterState.GlobalState.SendRoot, action.hash) } // If we have no stake yet, we'll put one down @@ -890,7 +1054,7 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv return fmt.Errorf("error placing new stake on new node: %w", err) } info.StakeExists = true - return nil + return s.tryFastConfirmation(ctx, action.assertion.AfterState.GlobalState.BlockHash, action.assertion.AfterState.GlobalState.SendRoot, action.hash) case existingNodeAction: info.LatestStakedNode = action.number info.LatestStakedNodeHash = action.hash @@ -904,8 +1068,12 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv id: action.number, hash: action.hash, } + s.inactiveValidatedNodes.ReplaceOrInsert(validatedNode{ + number: action.number, + hash: action.hash, + }) } - return nil + return s.tryFastConfirmationNodeNumber(ctx, action.number, action.hash) } log.Info("staking on existing node", "node", action.number) // We'll return early if we already havea stake @@ -918,7 +1086,7 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv if err != nil { return fmt.Errorf("error staking on existing node: %w", err) } - return nil + return s.tryFastConfirmationNodeNumber(ctx, action.number, action.hash) } // If we have no stake yet, we'll put one down @@ -939,7 +1107,7 @@ func (s *Staker) advanceStake(ctx context.Context, info *OurStakerInfo, effectiv return fmt.Errorf("error placing new stake on existing node: %w", err) } info.StakeExists = true - return nil + return s.tryFastConfirmationNodeNumber(ctx, action.number, action.hash) default: panic("invalid action type") } @@ -1031,7 +1199,7 @@ func (s *Staker) createConflict(ctx context.Context, info *StakerInfo) error { } func (s *Staker) Strategy() StakerStrategy { - return s.config.strategy + return s.config().strategy } func (s *Staker) Rollup() *RollupWatcher { diff --git a/staker/stateless_block_validator.go b/staker/stateless_block_validator.go index 1cf3d7a4c3..d5eeb8eb69 100644 --- a/staker/stateless_block_validator.go +++ b/staker/stateless_block_validator.go @@ -12,6 +12,7 @@ import ( "github.com/offchainlabs/nitro/arbstate/daprovider" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" @@ -48,7 +49,7 @@ type BlockValidatorRegistrer interface { type InboxTrackerInterface interface { BlockValidatorRegistrer - GetDelayedMessageBytes(uint64) ([]byte, error) + GetDelayedMessageBytes(context.Context, uint64) ([]byte, error) GetBatchMessageCount(seqNum uint64) (arbutil.MessageIndex, error) GetBatchAcc(seqNum uint64) (common.Hash, error) GetBatchCount() (uint64, error) @@ -133,21 +134,37 @@ type validationEntry struct { DelayedMsg []byte } -func (e *validationEntry) ToInput() (*validator.ValidationInput, error) { +func (e *validationEntry) ToInput(stylusArchs []rawdb.Target) (*validator.ValidationInput, error) { if e.Stage != Ready { return nil, errors.New("cannot create input from non-ready entry") } - return &validator.ValidationInput{ + res := validator.ValidationInput{ Id: uint64(e.Pos), HasDelayedMsg: e.HasDelayedMsg, DelayedMsgNr: e.DelayedMsgNr, Preimages: e.Preimages, - UserWasms: e.UserWasms, + UserWasms: make(map[rawdb.Target]map[common.Hash][]byte, len(e.UserWasms)), BatchInfo: e.BatchInfo, DelayedMsg: e.DelayedMsg, StartState: e.Start, DebugChain: e.ChainConfig.DebugMode(), - }, nil + } + if len(stylusArchs) == 0 && len(e.UserWasms) > 0 { + return nil, fmt.Errorf("stylus support is required") + } + for _, stylusArch := range stylusArchs { + res.UserWasms[stylusArch] = make(map[common.Hash][]byte) + } + for hash, asmMap := range e.UserWasms { + for _, stylusArch := range stylusArchs { + if asm, exists := asmMap[stylusArch]; exists { + res.UserWasms[stylusArch][hash] = asm + } else { + return nil, fmt.Errorf("stylusArch not supported by block validator: %v", stylusArch) + } + } + } + return &res, nil } func newValidationEntry( @@ -230,6 +247,25 @@ func NewStatelessBlockValidator( }, nil } +func (v *StatelessBlockValidator) readBatch(ctx context.Context, batchNum uint64) (bool, []byte, common.Hash, arbutil.MessageIndex, error) { + batchCount, err := v.inboxTracker.GetBatchCount() + if err != nil { + return false, nil, common.Hash{}, 0, err + } + if batchCount <= batchNum { + return false, nil, common.Hash{}, 0, nil + } + batchMsgCount, err := v.inboxTracker.GetBatchMessageCount(batchNum) + if err != nil { + return false, nil, common.Hash{}, 0, err + } + batch, batchBlockHash, err := v.inboxReader.GetSequencerMessageBytes(ctx, batchNum) + if err != nil { + return false, nil, common.Hash{}, 0, err + } + return true, batch, batchBlockHash, batchMsgCount, nil +} + func (v *StatelessBlockValidator) ValidationEntryRecord(ctx context.Context, e *validationEntry) error { if e.Stage != ReadyForRecord { return fmt.Errorf("validation entry should be ReadyForRecord, is: %v", e.Stage) @@ -243,7 +279,27 @@ func (v *StatelessBlockValidator) ValidationEntryRecord(ctx context.Context, e * if recording.BlockHash != e.End.BlockHash { return fmt.Errorf("recording failed: pos %d, hash expected %v, got %v", e.Pos, e.End.BlockHash, recording.BlockHash) } - e.BatchInfo = append(e.BatchInfo, recording.BatchInfo...) + // record any additional batch fetching + batchFetcher := func(batchNum uint64) ([]byte, error) { + found, data, hash, _, err := v.readBatch(ctx, batchNum) + if err != nil { + return nil, err + } + if !found { + return nil, errors.New("batch not found") + } + e.BatchInfo = append(e.BatchInfo, validator.BatchInfo{ + Number: batchNum, + BlockHash: hash, + Data: data, + }) + return data, nil + } + e.msg.Message.BatchGasCost = nil + err = e.msg.Message.FillInBatchGasCost(batchFetcher) + if err != nil { + return err + } if recording.Preimages != nil { e.Preimages[arbutil.Keccak256PreimageType] = recording.Preimages @@ -251,7 +307,7 @@ func (v *StatelessBlockValidator) ValidationEntryRecord(ctx context.Context, e * e.UserWasms = recording.UserWasms } if e.HasDelayedMsg { - delayedMsg, err := v.inboxTracker.GetDelayedMessageBytes(e.DelayedMsgNr) + delayedMsg, err := v.inboxTracker.GetDelayedMessageBytes(ctx, e.DelayedMsgNr) if err != nil { log.Error( "error while trying to read delayed msg for proving", @@ -372,14 +428,14 @@ func (v *StatelessBlockValidator) ValidateResult( if err != nil { return false, nil, err } - input, err := entry.ToInput() - if err != nil { - return false, nil, err - } var run validator.ValidationRun if !useExec { if v.redisValidator != nil { if validator.SpawnerSupportsModule(v.redisValidator, moduleRoot) { + input, err := entry.ToInput(v.redisValidator.StylusArchs()) + if err != nil { + return false, nil, err + } run = v.redisValidator.Launch(input, moduleRoot) } } @@ -387,6 +443,10 @@ func (v *StatelessBlockValidator) ValidateResult( if run == nil { for _, spawner := range v.execSpawners { if validator.SpawnerSupportsModule(spawner, moduleRoot) { + input, err := entry.ToInput(spawner.StylusArchs()) + if err != nil { + return false, nil, err + } run = spawner.Launch(input, moduleRoot) break } diff --git a/util/arbmath/math.go b/util/arbmath/math.go index 7413955409..62af1e26e0 100644 --- a/util/arbmath/math.go +++ b/util/arbmath/math.go @@ -387,7 +387,7 @@ func DivCeil[T Unsigned](value, divisor T) T { // ApproxExpBasisPoints return the Maclaurin series approximation of e^x, where x is denominated in basis points. // The quartic polynomial will underestimate e^x by about 5% as x approaches 20000 bips. -func ApproxExpBasisPoints(value Bips, degree uint64) Bips { +func ApproxExpBasisPoints(value Bips, accuracy uint64) Bips { input := value negative := value < 0 if negative { @@ -396,9 +396,9 @@ func ApproxExpBasisPoints(value Bips, degree uint64) Bips { x := uint64(input) bips := uint64(OneInBips) - res := bips + x/degree - for i := uint64(1); i < degree; i++ { - res = bips + SaturatingUMul(res, x)/((degree-i)*bips) + res := bips + x/accuracy + for i := uint64(1); i < accuracy; i++ { + res = bips + SaturatingUMul(res, x)/((accuracy-i)*bips) } if negative { diff --git a/util/containers/promise.go b/util/containers/promise.go index a180c094eb..54f0df195d 100644 --- a/util/containers/promise.go +++ b/util/containers/promise.go @@ -20,7 +20,7 @@ type Promise[R any] struct { chanReady chan struct{} result R err error - produced uint32 + produced atomic.Bool cancel func() } @@ -67,7 +67,7 @@ func (p *Promise[R]) Cancel() { } func (p *Promise[R]) ProduceErrorSafe(err error) error { - if !atomic.CompareAndSwapUint32(&p.produced, 0, 1) { + if !p.produced.CompareAndSwap(false, true) { return errors.New("cannot produce two values") } p.err = err @@ -83,7 +83,7 @@ func (p *Promise[R]) ProduceError(err error) { } func (p *Promise[R]) ProduceSafe(value R) error { - if !atomic.CompareAndSwapUint32(&p.produced, 0, 1) { + if !p.produced.CompareAndSwap(false, true) { return errors.New("cannot produce two values") } p.result = value diff --git a/util/containers/promise_test.go b/util/containers/promise_test.go index 61ee4f2c3a..3e41a6465d 100644 --- a/util/containers/promise_test.go +++ b/util/containers/promise_test.go @@ -25,8 +25,8 @@ func TestPromise(t *testing.T) { t.Fatal("unexpected Promise.Current when ready") } - cancelCalled := int64(0) - cancelFunc := func() { atomic.AddInt64(&cancelCalled, 1) } + var cancelCalled atomic.Int64 + cancelFunc := func() { cancelCalled.Add(1) } tempPromise = NewPromise[int](cancelFunc) res, err = tempPromise.Current() @@ -68,12 +68,12 @@ func TestPromise(t *testing.T) { t.Fatal("unexpected Promise.Current 2nd time") } - if atomic.LoadInt64(&cancelCalled) != 0 { + if cancelCalled.Load() != 0 { t.Fatal("cancel called by await/current when it shouldn't be") } tempPromise.Cancel() - if atomic.LoadInt64(&cancelCalled) != 0 { + if cancelCalled.Load() != 0 { t.Fatal("cancel called after error produced") } @@ -84,11 +84,11 @@ func TestPromise(t *testing.T) { if res != 0 || !errors.Is(err, context.DeadlineExceeded) { t.Fatal("unexpected Promise.Await with timeout") } - if atomic.LoadInt64(&cancelCalled) != 1 { + if cancelCalled.Load() != 1 { t.Fatal("cancel not called by await on timeout") } tempPromise.Cancel() - if atomic.LoadInt64(&cancelCalled) != 2 { + if cancelCalled.Load() != 2 { t.Fatal("cancel not called by promise.Cancel") } } diff --git a/util/dbutil/dbutil.go b/util/dbutil/dbutil.go new file mode 100644 index 0000000000..6573c5742c --- /dev/null +++ b/util/dbutil/dbutil.go @@ -0,0 +1,57 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package dbutil + +import ( + "errors" + "fmt" + "io/fs" + "regexp" + + "github.com/cockroachdb/pebble" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/ethdb/memorydb" + "github.com/syndtr/goleveldb/leveldb" +) + +func IsErrNotFound(err error) bool { + return errors.Is(err, leveldb.ErrNotFound) || errors.Is(err, pebble.ErrNotFound) || errors.Is(err, memorydb.ErrMemorydbNotFound) +} + +var pebbleNotExistErrorRegex = regexp.MustCompile("pebble: database .* does not exist") + +func isPebbleNotExistError(err error) bool { + return err != nil && pebbleNotExistErrorRegex.MatchString(err.Error()) +} + +func isLeveldbNotExistError(err error) bool { + return errors.Is(err, fs.ErrNotExist) +} + +// IsNotExistError returns true if the error is a "database not found" error. +// It must return false if err is nil. +func IsNotExistError(err error) bool { + return isLeveldbNotExistError(err) || isPebbleNotExistError(err) +} + +var unfinishedConversionCanaryKey = []byte("unfinished-conversion-canary-key") + +func PutUnfinishedConversionCanary(db ethdb.KeyValueStore) error { + return db.Put(unfinishedConversionCanaryKey, []byte{1}) +} + +func DeleteUnfinishedConversionCanary(db ethdb.KeyValueStore) error { + return db.Delete(unfinishedConversionCanaryKey) +} + +func UnfinishedConversionCheck(db ethdb.KeyValueStore) error { + unfinished, err := db.Has(unfinishedConversionCanaryKey) + if err != nil { + return fmt.Errorf("Failed to check UnfinishedConversionCanaryKey existence: %w", err) + } + if unfinished { + return errors.New("Unfinished conversion canary key detected") + } + return nil +} diff --git a/util/dbutil/dbutil_test.go b/util/dbutil/dbutil_test.go new file mode 100644 index 0000000000..b28f8a2c23 --- /dev/null +++ b/util/dbutil/dbutil_test.go @@ -0,0 +1,46 @@ +package dbutil + +import ( + "errors" + "testing" + + "github.com/ethereum/go-ethereum/node" +) + +func testIsNotExistError(t *testing.T, dbEngine string, isNotExist func(error) bool) { + stackConf := node.DefaultConfig + stackConf.DataDir = t.TempDir() + stackConf.DBEngine = dbEngine + stack, err := node.New(&stackConf) + if err != nil { + t.Fatalf("Failed to created test stack: %v", err) + } + defer stack.Close() + readonly := true + _, err = stack.OpenDatabaseWithExtraOptions("test", 16, 16, "", readonly, nil) + if err == nil { + t.Fatal("Opening non-existent database did not fail") + } + if !isNotExist(err) { + t.Fatalf("Failed to classify error as not exist error - internal implementation of OpenDatabaseWithExtraOptions might have changed, err: %v", err) + } + err = errors.New("some other error") + if isNotExist(err) { + t.Fatalf("Classified other error as not exist, err: %v", err) + } +} + +func TestIsNotExistError(t *testing.T) { + t.Run("TestIsPebbleNotExistError", func(t *testing.T) { + testIsNotExistError(t, "pebble", isPebbleNotExistError) + }) + t.Run("TestIsLeveldbNotExistError", func(t *testing.T) { + testIsNotExistError(t, "leveldb", isLeveldbNotExistError) + }) + t.Run("TestIsNotExistErrorWithPebble", func(t *testing.T) { + testIsNotExistError(t, "pebble", IsNotExistError) + }) + t.Run("TestIsNotExistErrorWithLeveldb", func(t *testing.T) { + testIsNotExistError(t, "leveldb", IsNotExistError) + }) +} diff --git a/util/headerreader/header_reader.go b/util/headerreader/header_reader.go index 06dfcfbfa8..074d24338e 100644 --- a/util/headerreader/header_reader.go +++ b/util/headerreader/header_reader.go @@ -63,6 +63,7 @@ type Config struct { Enable bool `koanf:"enable"` PollOnly bool `koanf:"poll-only" reload:"hot"` PollInterval time.Duration `koanf:"poll-interval" reload:"hot"` + PollTimeout time.Duration `koanf:"poll-timeout" reload:"hot"` SubscribeErrInterval time.Duration `koanf:"subscribe-err-interval" reload:"hot"` TxTimeout time.Duration `koanf:"tx-timeout" reload:"hot"` OldHeaderTimeout time.Duration `koanf:"old-header-timeout" reload:"hot"` @@ -80,6 +81,7 @@ var DefaultConfig = Config{ Enable: true, PollOnly: false, PollInterval: 15 * time.Second, + PollTimeout: 5 * time.Second, SubscribeErrInterval: 5 * time.Minute, TxTimeout: 5 * time.Minute, OldHeaderTimeout: 5 * time.Minute, @@ -94,6 +96,7 @@ func AddOptions(prefix string, f *flag.FlagSet) { f.Bool(prefix+".poll-only", DefaultConfig.PollOnly, "do not attempt to subscribe to header events") f.Bool(prefix+".use-finality-data", DefaultConfig.UseFinalityData, "use l1 data about finalized/safe blocks") f.Duration(prefix+".poll-interval", DefaultConfig.PollInterval, "interval when polling endpoint") + f.Duration(prefix+".poll-timeout", DefaultConfig.PollTimeout, "timeout when polling endpoint") f.Duration(prefix+".subscribe-err-interval", DefaultConfig.SubscribeErrInterval, "interval for subscribe error") f.Duration(prefix+".tx-timeout", DefaultConfig.TxTimeout, "timeout when waiting for a transaction") f.Duration(prefix+".old-header-timeout", DefaultConfig.OldHeaderTimeout, "warns if the latest l1 block is at least this old") @@ -108,6 +111,7 @@ var TestConfig = Config{ Enable: true, PollOnly: false, PollInterval: time.Millisecond * 10, + PollTimeout: time.Second * 5, TxTimeout: time.Second * 5, OldHeaderTimeout: 5 * time.Minute, UseFinalityData: false, @@ -287,7 +291,9 @@ func (s *HeaderReader) broadcastLoop(ctx context.Context) { s.possiblyBroadcast(h) timer.Stop() case <-timer.C: - h, err := s.client.HeaderByNumber(ctx, nil) + timedCtx, cancelFunc := context.WithTimeout(ctx, s.config().PollTimeout) + h, err := s.client.HeaderByNumber(timedCtx, nil) + cancelFunc() if err != nil { s.setError(fmt.Errorf("failed reading HeaderByNumber: %w", err)) if !errors.Is(err, context.Canceled) { diff --git a/util/iostat/iostat.go b/util/iostat/iostat.go index 9bc5ff800c..342a44100e 100644 --- a/util/iostat/iostat.go +++ b/util/iostat/iostat.go @@ -30,7 +30,9 @@ func RegisterAndPopulateMetrics(ctx context.Context, spawnInterval, maxDeviceCou if _, ok := deviceMetrics[stat.DeviceName]; !ok { // Register metrics for a maximum of maxDeviceCount (fail safe incase iostat command returns incorrect names indefinitely) if len(deviceMetrics) < maxDeviceCount { - baseMetricName := fmt.Sprintf("isotat/%s/", stat.DeviceName) + // Replace hyphens with underscores to avoid metric name issues + sanitizedDeviceName := strings.ReplaceAll(stat.DeviceName, "-", "_") + baseMetricName := fmt.Sprintf("iostat/%s/", sanitizedDeviceName) deviceMetrics[stat.DeviceName] = make(map[string]metrics.GaugeFloat64) deviceMetrics[stat.DeviceName]["readspersecond"] = metrics.NewRegisteredGaugeFloat64(baseMetricName+"readspersecond", nil) deviceMetrics[stat.DeviceName]["writespersecond"] = metrics.NewRegisteredGaugeFloat64(baseMetricName+"writespersecond", nil) diff --git a/util/redisutil/redis_coordinator.go b/util/redisutil/redis_coordinator.go index 59e3b0e0f9..2c12ffec50 100644 --- a/util/redisutil/redis_coordinator.go +++ b/util/redisutil/redis_coordinator.go @@ -13,12 +13,13 @@ import ( "github.com/offchainlabs/nitro/arbutil" ) -const CHOSENSEQ_KEY string = "coordinator.chosen" // Never overwritten. Expires or released only -const MSG_COUNT_KEY string = "coordinator.msgCount" // Only written by sequencer holding CHOSEN key -const PRIORITIES_KEY string = "coordinator.priorities" // Read only -const WANTS_LOCKOUT_KEY_PREFIX string = "coordinator.liveliness." // Per server. Only written by self -const MESSAGE_KEY_PREFIX string = "coordinator.msg." // Per Message. Only written by sequencer holding CHOSEN -const SIGNATURE_KEY_PREFIX string = "coordinator.msg.sig." // Per Message. Only written by sequencer holding CHOSEN +const CHOSENSEQ_KEY string = "coordinator.chosen" // Never overwritten. Expires or released only +const MSG_COUNT_KEY string = "coordinator.msgCount" // Only written by sequencer holding CHOSEN key +const FINALIZED_MSG_COUNT_KEY string = "coordinator.finalizedMsgCount" // Only written by sequencer holding CHOSEN key +const PRIORITIES_KEY string = "coordinator.priorities" // Read only +const WANTS_LOCKOUT_KEY_PREFIX string = "coordinator.liveliness." // Per server. Only written by self +const MESSAGE_KEY_PREFIX string = "coordinator.msg." // Per Message. Only written by sequencer holding CHOSEN +const SIGNATURE_KEY_PREFIX string = "coordinator.msg.sig." // Per Message. Only written by sequencer holding CHOSEN const WANTS_LOCKOUT_VAL string = "OK" const INVALID_VAL string = "INVALID" const INVALID_URL string = "" diff --git a/util/rpcclient/rpcclient.go b/util/rpcclient/rpcclient.go index 56aebef396..be5825a28d 100644 --- a/util/rpcclient/rpcclient.go +++ b/util/rpcclient/rpcclient.go @@ -44,6 +44,13 @@ func (c *ClientConfig) Validate() error { return err } +func (c *ClientConfig) UnmarshalJSON(data []byte) error { + // Use DefaultClientConfig for default values when unmarshalling JSON + *c = DefaultClientConfig + type clientConfigWithoutCustomUnmarshal ClientConfig + return json.Unmarshal(data, (*clientConfigWithoutCustomUnmarshal)(c)) +} + type ClientConfigFetcher func() *ClientConfig var TestClientConfig = ClientConfig{ @@ -77,7 +84,7 @@ type RpcClient struct { config ClientConfigFetcher client *rpc.Client autoStack *node.Node - logId uint64 + logId atomic.Uint64 } func NewRpcClient(config ClientConfigFetcher, stack *node.Node) *RpcClient { @@ -154,7 +161,7 @@ func (c *RpcClient) CallContext(ctx_in context.Context, result interface{}, meth if c.client == nil { return errors.New("not connected") } - logId := atomic.AddUint64(&c.logId, 1) + logId := c.logId.Add(1) log.Trace("sending RPC request", "method", method, "logId", logId, "args", limitedArgumentsMarshal{int(c.config().ArgLogLimit), args}) var err error for i := 0; i < int(c.config().Retries)+1; i++ { diff --git a/util/rpcclient/rpcclient_test.go b/util/rpcclient/rpcclient_test.go index 8613671d37..1a7da54774 100644 --- a/util/rpcclient/rpcclient_test.go +++ b/util/rpcclient/rpcclient_test.go @@ -2,13 +2,17 @@ package rpcclient import ( "context" + "encoding/json" "errors" + "regexp" "sync/atomic" "testing" "time" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/rpc" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/offchainlabs/nitro/util/testhelpers" ) @@ -46,10 +50,13 @@ func createTestNode(t *testing.T, ctx context.Context, stuckOrFailed int64) *nod stack, err := node.New(&stackConf) Require(t, err) + service := &testAPI{} + service.stuckCalls.Store(stuckOrFailed) + service.failedCalls.Store(stuckOrFailed) testAPIs := []rpc.API{{ Namespace: "test", Version: "1.0", - Service: &testAPI{stuckOrFailed, stuckOrFailed}, + Service: service, Public: true, Authenticated: false, }} @@ -67,12 +74,12 @@ func createTestNode(t *testing.T, ctx context.Context, stuckOrFailed int64) *nod } type testAPI struct { - stuckCalls int64 - failedCalls int64 + stuckCalls atomic.Int64 + failedCalls atomic.Int64 } func (t *testAPI) StuckAtFirst(ctx context.Context) error { - stuckRemaining := atomic.AddInt64(&t.stuckCalls, -1) + 1 + stuckRemaining := t.stuckCalls.Add(-1) + 1 if stuckRemaining <= 0 { return nil } @@ -81,7 +88,7 @@ func (t *testAPI) StuckAtFirst(ctx context.Context) error { } func (t *testAPI) FailAtFirst(ctx context.Context) error { - failedRemaining := atomic.AddInt64(&t.failedCalls, -1) + 1 + failedRemaining := t.failedCalls.Add(-1) + 1 if failedRemaining <= 0 { return nil } @@ -201,6 +208,21 @@ func TestIsAlreadyKnownError(t *testing.T) { } } +func TestUnmarshalClientConfig(t *testing.T) { + exampleJson := `[{"jwtsecret":"/tmp/nitro-val.jwt","url":"http://127.0.0.10:52000"}, {"jwtsecret":"/tmp/nitro-val.jwt","url":"http://127.0.0.10:52001"}]` + var clientConfigs []ClientConfig + Require(t, json.Unmarshal([]byte(exampleJson), &clientConfigs)) + expectedClientConfigs := []ClientConfig{DefaultClientConfig, DefaultClientConfig} + expectedClientConfigs[0].JWTSecret = "/tmp/nitro-val.jwt" + expectedClientConfigs[0].URL = "http://127.0.0.10:52000" + expectedClientConfigs[1].JWTSecret = "/tmp/nitro-val.jwt" + expectedClientConfigs[1].URL = "http://127.0.0.10:52001" + // Ensure the configs are equivalent to the expected configs, ignoring the retryErrors regexp as cmp can't compare it + if diff := cmp.Diff(expectedClientConfigs, clientConfigs, cmpopts.IgnoreTypes(®exp.Regexp{})); diff != "" { + t.Errorf("unmarshalling example JSON unexpected diff:\n%s", diff) + } +} + func Require(t *testing.T, err error, printables ...interface{}) { t.Helper() testhelpers.RequireImpl(t, err, printables...) diff --git a/util/testhelpers/env/env.go b/util/testhelpers/env/env.go new file mode 100644 index 0000000000..27d74465de --- /dev/null +++ b/util/testhelpers/env/env.go @@ -0,0 +1,23 @@ +// Copyright 2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package env + +import ( + "os" + + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/log" +) + +// There are two CI steps, one to run tests using the path state scheme, and one to run tests using the hash state scheme. +// An environment variable controls that behavior. +func GetTestStateScheme() string { + envTestStateScheme := os.Getenv("TEST_STATE_SCHEME") + stateScheme := rawdb.PathScheme + if envTestStateScheme == rawdb.PathScheme || envTestStateScheme == rawdb.HashScheme { + stateScheme = envTestStateScheme + } + log.Debug("test state scheme", "testStateScheme", stateScheme) + return stateScheme +} diff --git a/util/testhelpers/github/releases.go b/util/testhelpers/github/releases.go index 59f591d92c..5555c90aa1 100644 --- a/util/testhelpers/github/releases.go +++ b/util/testhelpers/github/releases.go @@ -4,10 +4,12 @@ import ( "context" "fmt" "net/url" + "os" "regexp" "strings" "github.com/google/go-github/v62/github" + "golang.org/x/oauth2" ) var wasmRootExp = regexp.MustCompile(`\*\*WAVM Module Root\*\*: (0x[a-f0-9]{64})`) @@ -18,9 +20,18 @@ type ConsensusRelease struct { ReplayWasmURL url.URL } +func getAuthGitClient(ctx context.Context) *github.Client { + token := os.Getenv("GITHUB_TOKEN") + if token == "" { + return github.NewClient(nil) + } + tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token}) + return github.NewClient(oauth2.NewClient(ctx, tokenSource)) +} + // NitroReleases returns the most recent 50 releases of the Nitro repository. func NitroReleases(ctx context.Context) ([]*github.RepositoryRelease, error) { - client := github.NewClient(nil) + client := getAuthGitClient(ctx) opts := &github.ListOptions{ PerPage: 50, } @@ -36,7 +47,7 @@ func LatestConsensusRelease(ctx context.Context) (*ConsensusRelease, error) { } var found *ConsensusRelease for _, release := range releases { - if strings.HasPrefix(release.GetTagName(), "consensus") { + if strings.HasPrefix(release.GetTagName(), "consensus") && !release.GetPrerelease() { if found, err = fromRelease(release); err != nil { return nil, err } diff --git a/util/testhelpers/stackconfig.go b/util/testhelpers/stackconfig.go new file mode 100644 index 0000000000..45ab653a1c --- /dev/null +++ b/util/testhelpers/stackconfig.go @@ -0,0 +1,23 @@ +// Copyright 2021-2024, Offchain Labs, Inc. +// For license information, see https://github.com/nitro/blob/master/LICENSE + +package testhelpers + +import "github.com/ethereum/go-ethereum/node" + +func CreateStackConfigForTest(dataDir string) *node.Config { + stackConf := node.DefaultConfig + stackConf.DataDir = dataDir + stackConf.UseLightweightKDF = true + stackConf.WSPort = 0 + stackConf.WSModules = append(stackConf.WSModules, "eth", "debug") + stackConf.HTTPPort = 0 + stackConf.HTTPHost = "" + stackConf.HTTPModules = append(stackConf.HTTPModules, "eth", "debug") + stackConf.P2P.NoDiscovery = true + stackConf.P2P.NoDial = true + stackConf.P2P.ListenAddr = "" + stackConf.P2P.NAT = nil + stackConf.DBEngine = "leveldb" + return &stackConf +} diff --git a/util/testhelpers/testhelpers.go b/util/testhelpers/testhelpers.go index 071429879e..b1b08708e7 100644 --- a/util/testhelpers/testhelpers.go +++ b/util/testhelpers/testhelpers.go @@ -11,6 +11,7 @@ import ( "math/rand" "os" "regexp" + "runtime/debug" "sync" "testing" @@ -24,6 +25,7 @@ import ( func RequireImpl(t *testing.T, err error, printables ...interface{}) { t.Helper() if err != nil { + t.Log(string(debug.Stack())) t.Fatal(colors.Red, printables, err, colors.Clear) } } diff --git a/validator/client/redis/producer.go b/validator/client/redis/producer.go index 4aa4031350..f98c246d0e 100644 --- a/validator/client/redis/producer.go +++ b/validator/client/redis/producer.go @@ -6,6 +6,7 @@ import ( "sync/atomic" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/log" "github.com/go-redis/redis/v8" "github.com/offchainlabs/nitro/pubsub" @@ -20,8 +21,10 @@ import ( type ValidationClientConfig struct { Name string `koanf:"name"` + StreamPrefix string `koanf:"stream-prefix"` Room int32 `koanf:"room"` RedisURL string `koanf:"redis-url"` + StylusArchs []string `koanf:"stylus-archs"` ProducerConfig pubsub.ProducerConfig `koanf:"producer-config"` CreateStreams bool `koanf:"create-streams"` } @@ -30,10 +33,20 @@ func (c ValidationClientConfig) Enabled() bool { return c.RedisURL != "" } +func (c ValidationClientConfig) Validate() error { + for _, arch := range c.StylusArchs { + if !rawdb.Target(arch).IsValid() { + return fmt.Errorf("Invalid stylus arch: %v", arch) + } + } + return nil +} + var DefaultValidationClientConfig = ValidationClientConfig{ Name: "redis validation client", Room: 2, RedisURL: "", + StylusArchs: []string{string(rawdb.TargetWavm)}, ProducerConfig: pubsub.DefaultProducerConfig, CreateStreams: true, } @@ -42,6 +55,8 @@ var TestValidationClientConfig = ValidationClientConfig{ Name: "test redis validation client", Room: 2, RedisURL: "", + StreamPrefix: "test-", + StylusArchs: []string{string(rawdb.TargetWavm)}, ProducerConfig: pubsub.TestProducerConfig, CreateStreams: false, } @@ -50,6 +65,8 @@ 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") f.String(prefix+".redis-url", DefaultValidationClientConfig.RedisURL, "redis url") + f.String(prefix+".stream-prefix", DefaultValidationClientConfig.StreamPrefix, "prefix for stream name") + f.StringSlice(prefix+".stylus-archs", DefaultValidationClientConfig.StylusArchs, "archs required for stylus workers") pubsub.ProducerAddConfigAddOptions(prefix+".producer-config", f) f.Bool(prefix+".create-streams", DefaultValidationClientConfig.CreateStreams, "create redis streams if it does not exist") } @@ -57,14 +74,12 @@ func ValidationClientConfigAddOptions(prefix string, f *pflag.FlagSet) { // ValidationClient implements validation client through redis streams. type ValidationClient struct { stopwaiter.StopWaiter - name string - room int32 + config *ValidationClientConfig + room atomic.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 + producers map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState] + redisClient redis.UniversalClient + moduleRoots []common.Hash } func NewValidationClient(cfg *ValidationClientConfig) (*ValidationClient, error) { @@ -75,20 +90,19 @@ func NewValidationClient(cfg *ValidationClientConfig) (*ValidationClient, error) 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 + validationClient := &ValidationClient{ + config: cfg, + producers: make(map[common.Hash]*pubsub.Producer[*validator.ValidationInput, validator.GoGlobalState]), + redisClient: redisClient, + } + validationClient.room.Store(cfg.Room) + return validationClient, 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 { + if c.config.CreateStreams { + if err := pubsub.CreateStream(ctx, server_api.RedisStreamForRoot(c.config.StreamPrefix, mr), c.redisClient); err != nil { return fmt.Errorf("creating redis stream: %w", err) } } @@ -97,7 +111,7 @@ func (c *ValidationClient) Initialize(ctx context.Context, moduleRoots []common. continue } p, err := pubsub.NewProducer[*validator.ValidationInput, validator.GoGlobalState]( - c.redisClient, server_api.RedisStreamForRoot(mr), &c.producerConfig) + c.redisClient, server_api.RedisStreamForRoot(c.config.StreamPrefix, mr), &c.config.ProducerConfig) if err != nil { log.Warn("failed init redis for %v: %w", mr, err) continue @@ -114,8 +128,8 @@ func (c *ValidationClient) WasmModuleRoots() ([]common.Hash, error) { } func (c *ValidationClient) Launch(entry *validator.ValidationInput, moduleRoot common.Hash) validator.ValidationRun { - atomic.AddInt32(&c.room, -1) - defer atomic.AddInt32(&c.room, 1) + c.room.Add(-1) + defer c.room.Add(1) producer, found := c.producers[moduleRoot] if !found { errPromise := containers.NewReadyPromise(validator.GoGlobalState{}, fmt.Errorf("no validation is configured for wasm root %v", moduleRoot)) @@ -145,12 +159,17 @@ func (c *ValidationClient) Stop() { } func (c *ValidationClient) Name() string { - if c.Started() { - return c.name + return c.config.Name +} + +func (c *ValidationClient) StylusArchs() []rawdb.Target { + stylusArchs := make([]rawdb.Target, 0, len(c.config.StylusArchs)) + for _, arch := range c.config.StylusArchs { + stylusArchs = append(stylusArchs, rawdb.Target(arch)) } - return "(not started)" + return stylusArchs } func (c *ValidationClient) Room() int { - return int(c.room) + return int(c.room.Load()) } diff --git a/validator/client/validation_client.go b/validator/client/validation_client.go index 949260002d..80cff66675 100644 --- a/validator/client/validation_client.go +++ b/validator/client/validation_client.go @@ -21,39 +21,42 @@ import ( "github.com/offchainlabs/nitro/validator/server_common" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/rpc" ) type ValidationClient struct { stopwaiter.StopWaiter client *rpcclient.RpcClient name string - room int32 + stylusArchs []rawdb.Target + room atomic.Int32 wasmModuleRoots []common.Hash } func NewValidationClient(config rpcclient.ClientConfigFetcher, stack *node.Node) *ValidationClient { return &ValidationClient{ - client: rpcclient.NewRpcClient(config, stack), + client: rpcclient.NewRpcClient(config, stack), + name: "not started", + stylusArchs: []rawdb.Target{"not started"}, } } func (c *ValidationClient) Launch(entry *validator.ValidationInput, moduleRoot common.Hash) validator.ValidationRun { - atomic.AddInt32(&c.room, -1) + c.room.Add(-1) promise := stopwaiter.LaunchPromiseThread[validator.GoGlobalState](c, func(ctx context.Context) (validator.GoGlobalState, error) { input := server_api.ValidationInputToJson(entry) var res validator.GoGlobalState err := c.client.CallContext(ctx, &res, server_api.Namespace+"_validate", input, moduleRoot) - atomic.AddInt32(&c.room, 1) + c.room.Add(1) return res, err }) return server_common.NewValRun(promise, moduleRoot) } -func (c *ValidationClient) Start(ctx_in context.Context) error { - c.StopWaiter.Start(ctx_in, c) - ctx := c.GetContext() +func (c *ValidationClient) Start(ctx context.Context) error { if err := c.client.Start(ctx); err != nil { return err } @@ -64,15 +67,33 @@ func (c *ValidationClient) Start(ctx_in context.Context) error { if len(name) == 0 { return errors.New("couldn't read name from server") } + var stylusArchs []rawdb.Target + if err := c.client.CallContext(ctx, &stylusArchs, server_api.Namespace+"_stylusArchs"); err != nil { + var rpcError rpc.Error + ok := errors.As(err, &rpcError) + if !ok || rpcError.ErrorCode() != -32601 { + return fmt.Errorf("could not read stylus arch from server: %w", err) + } + stylusArchs = []rawdb.Target{rawdb.Target("pre-stylus")} // invalid, will fail if trying to validate block with stylus + } else { + if len(stylusArchs) == 0 { + return fmt.Errorf("could not read stylus archs from validation server") + } + for _, stylusArch := range stylusArchs { + if stylusArch != rawdb.TargetWavm && stylusArch != rawdb.LocalTarget() && stylusArch != "mock" { + return fmt.Errorf("unsupported stylus architecture: %v", stylusArch) + } + } + } var moduleRoots []common.Hash - if err := c.client.CallContext(c.GetContext(), &moduleRoots, server_api.Namespace+"_wasmModuleRoots"); err != nil { + if err := c.client.CallContext(ctx, &moduleRoots, server_api.Namespace+"_wasmModuleRoots"); err != nil { return err } if len(moduleRoots) == 0 { return fmt.Errorf("server reported no wasmModuleRoots") } var room int - if err := c.client.CallContext(c.GetContext(), &room, server_api.Namespace+"_room"); err != nil { + if err := c.client.CallContext(ctx, &room, server_api.Namespace+"_room"); err != nil { return err } if room < 2 { @@ -81,9 +102,11 @@ func (c *ValidationClient) Start(ctx_in context.Context) error { } else { log.Info("connected to validation server", "name", name, "room", room) } - atomic.StoreInt32(&c.room, int32(room)) + c.room.Store(int32(room)) c.wasmModuleRoots = moduleRoots c.name = name + c.stylusArchs = stylusArchs + c.StopWaiter.Start(ctx, c) return nil } @@ -94,6 +117,13 @@ func (c *ValidationClient) WasmModuleRoots() ([]common.Hash, error) { return nil, errors.New("not started") } +func (c *ValidationClient) StylusArchs() []rawdb.Target { + if c.Started() { + return c.stylusArchs + } + return []rawdb.Target{"not started"} +} + func (c *ValidationClient) Stop() { c.StopWaiter.StopOnly() if c.client != nil { @@ -106,7 +136,7 @@ func (c *ValidationClient) Name() string { } func (c *ValidationClient) Room() int { - room32 := atomic.LoadInt32(&c.room) + room32 := c.room.Load() if room32 < 0 { return 0 } diff --git a/validator/interface.go b/validator/interface.go index 91668a3771..81b40ae5cf 100644 --- a/validator/interface.go +++ b/validator/interface.go @@ -4,6 +4,7 @@ import ( "context" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/offchainlabs/nitro/util/containers" ) @@ -13,6 +14,7 @@ type ValidationSpawner interface { Start(context.Context) error Stop() Name() string + StylusArchs() []rawdb.Target Room() int } diff --git a/validator/server_api/json.go b/validator/server_api/json.go index 3dd817d5ae..dbe2bb1fee 100644 --- a/validator/server_api/json.go +++ b/validator/server_api/json.go @@ -6,11 +6,13 @@ package server_api import ( "encoding/base64" "encoding/json" + "errors" "fmt" "os" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/offchainlabs/nitro/arbcompress" "github.com/offchainlabs/nitro/arbutil" "github.com/offchainlabs/nitro/util/jsonapi" @@ -45,8 +47,8 @@ func MachineStepResultFromJson(resultJson *MachineStepResultJson) (*validator.Ma }, nil } -func RedisStreamForRoot(moduleRoot common.Hash) string { - return fmt.Sprintf("stream:%s", moduleRoot.Hex()) +func RedisStreamForRoot(prefix string, moduleRoot common.Hash) string { + return fmt.Sprintf("%sstream:%s", prefix, moduleRoot.Hex()) } type Request struct { @@ -62,7 +64,7 @@ type InputJSON struct { BatchInfo []BatchInfoJson DelayedMsgB64 string StartState validator.GoGlobalState - UserWasms map[common.Hash]UserWasmJson + UserWasms map[rawdb.Target]map[common.Hash]string DebugChain bool } @@ -77,11 +79,6 @@ func (i *InputJSON) WriteToFile() error { return nil } -type UserWasmJson struct { - Module string - Asm string -} - type BatchInfoJson struct { Number uint64 DataB64 string @@ -99,19 +96,23 @@ func ValidationInputToJson(entry *validator.ValidationInput) *InputJSON { DelayedMsgB64: base64.StdEncoding.EncodeToString(entry.DelayedMsg), StartState: entry.StartState, PreimagesB64: jsonPreimagesMap, - UserWasms: make(map[common.Hash]UserWasmJson), + UserWasms: make(map[rawdb.Target]map[common.Hash]string), DebugChain: entry.DebugChain, } for _, binfo := range entry.BatchInfo { encData := base64.StdEncoding.EncodeToString(binfo.Data) 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), + for target, wasms := range entry.UserWasms { + archWasms := make(map[common.Hash]string) + for moduleHash, data := range wasms { + compressed, err := arbcompress.CompressLevel(data, 1) + if err != nil { + continue + } + archWasms[moduleHash] = base64.StdEncoding.EncodeToString(compressed) } - res.UserWasms[moduleHash] = encWasm + res.UserWasms[target] = archWasms } return res } @@ -127,7 +128,7 @@ func ValidationInputFromJson(entry *InputJSON) (*validator.ValidationInput, erro DelayedMsgNr: entry.DelayedMsgNr, StartState: entry.StartState, Preimages: preimages, - UserWasms: make(state.UserWasms), + UserWasms: make(map[rawdb.Target]map[common.Hash][]byte), DebugChain: entry.DebugChain, } delayed, err := base64.StdEncoding.DecodeString(entry.DelayedMsgB64) @@ -146,20 +147,32 @@ func ValidationInputFromJson(entry *InputJSON) (*validator.ValidationInput, erro } valInput.BatchInfo = append(valInput.BatchInfo, decInfo) } - 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, + for target, wasms := range entry.UserWasms { + archWasms := make(map[common.Hash][]byte) + for moduleHash, encoded := range wasms { + decoded, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + return nil, err + } + maxSize := 2_000_000 + var uncompressed []byte + for { + uncompressed, err = arbcompress.Decompress(decoded, maxSize) + if errors.Is(err, arbcompress.ErrOutputWontFit) { + if maxSize >= 512_000_000 { + return nil, errors.New("failed decompression: too large") + } + maxSize = maxSize * 4 + continue + } + if err != nil { + return nil, err + } + break + } + archWasms[moduleHash] = uncompressed } - valInput.UserWasms[moduleHash] = decInfo + valInput.UserWasms[target] = archWasms } return valInput, nil } diff --git a/validator/server_arb/execution_run.go b/validator/server_arb/execution_run.go index 8bdce145a2..d29a88d34d 100644 --- a/validator/server_arb/execution_run.go +++ b/validator/server_arb/execution_run.go @@ -24,7 +24,7 @@ type executionRun struct { close sync.Once } -// NewExecutionChallengeBackend creates a backend with the given arguments. +// NewExecutionRun creates a backend with the given arguments. // Note: machineCache may be nil, but if present, it must not have a restricted range. func NewExecutionRun( ctxIn context.Context, diff --git a/validator/server_arb/machine.go b/validator/server_arb/machine.go index cffd3db0ee..adca9695e2 100644 --- a/validator/server_arb/machine.go +++ b/validator/server_arb/machine.go @@ -59,7 +59,7 @@ type ArbitratorMachine struct { var _ MachineInterface = (*ArbitratorMachine)(nil) var preimageResolvers containers.SyncMap[int64, GoPreimageResolver] -var lastPreimageResolverId int64 // atomic +var lastPreimageResolverId atomic.Int64 // atomic // Any future calls to this machine will result in a panic func (m *ArbitratorMachine) Destroy() { @@ -382,7 +382,7 @@ func (m *ArbitratorMachine) SetPreimageResolver(resolver GoPreimageResolver) err if m.frozen { return errors.New("machine frozen") } - id := atomic.AddInt64(&lastPreimageResolverId, 1) + id := lastPreimageResolverId.Add(1) preimageResolvers.Store(id, resolver) m.contextId = &id runtime.SetFinalizer(m.contextId, freeContextId) diff --git a/validator/server_arb/validator_spawner.go b/validator/server_arb/validator_spawner.go index dca15c369e..844a988d28 100644 --- a/validator/server_arb/validator_spawner.go +++ b/validator/server_arb/validator_spawner.go @@ -21,6 +21,7 @@ import ( "github.com/offchainlabs/nitro/validator/valnode/redis" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" ) @@ -59,7 +60,7 @@ func DefaultArbitratorSpawnerConfigFetcher() *ArbitratorSpawnerConfig { type ArbitratorSpawner struct { stopwaiter.StopWaiter - count int32 + count atomic.Int32 locator *server_common.MachineLocator machineLoader *ArbMachineLoader config ArbitratorSpawnerConfigFecher @@ -88,6 +89,10 @@ func (s *ArbitratorSpawner) WasmModuleRoots() ([]common.Hash, error) { return s.locator.ModuleRoots(), nil } +func (s *ArbitratorSpawner) StylusArchs() []rawdb.Target { + return []rawdb.Target{rawdb.TargetWavm} +} + func (s *ArbitratorSpawner) Name() string { return "arbitrator" } @@ -118,8 +123,15 @@ 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 len(entry.UserWasms[rawdb.TargetWavm]) == 0 { + for stylusArch, wasms := range entry.UserWasms { + if len(wasms) > 0 { + return fmt.Errorf("bad stylus arch loaded to machine. Expected wavm. Got: %s", stylusArch) + } + } + } + for moduleHash, module := range entry.UserWasms[rawdb.TargetWavm] { + err = mach.AddUserWasm(moduleHash, module) if err != nil { log.Error( "error adding user wasm for proving", @@ -176,9 +188,9 @@ func (v *ArbitratorSpawner) execute( } func (v *ArbitratorSpawner) Launch(entry *validator.ValidationInput, moduleRoot common.Hash) validator.ValidationRun { - atomic.AddInt32(&v.count, 1) + v.count.Add(1) promise := stopwaiter.LaunchPromiseThread[validator.GoGlobalState](v, func(ctx context.Context) (validator.GoGlobalState, error) { - defer atomic.AddInt32(&v.count, -1) + defer v.count.Add(-1) return v.execute(ctx, entry, moduleRoot) }) return server_common.NewValRun(promise, moduleRoot) diff --git a/validator/server_jit/jit_machine.go b/validator/server_jit/jit_machine.go index 1a3ccfa340..23a75bba83 100644 --- a/validator/server_jit/jit_machine.go +++ b/validator/server_jit/jit_machine.go @@ -15,6 +15,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/metrics" "github.com/offchainlabs/nitro/util/arbmath" @@ -211,16 +212,26 @@ func (machine *JitMachine) prove( } } - // send user wasms - userWasms := entry.UserWasms + localTarget := rawdb.LocalTarget() + userWasms := entry.UserWasms[localTarget] + + // if there are user wasms, but only for wrong architecture - error + if len(userWasms) == 0 { + for arch, userWasms := range entry.UserWasms { + if len(userWasms) != 0 { + return state, fmt.Errorf("bad stylus arch for validation input. got: %v, expected: %v", arch, localTarget) + } + } + } + if err := writeUint32(uint32(len(userWasms))); err != nil { return state, err } - for moduleHash, info := range userWasms { + for moduleHash, program := range userWasms { if err := writeExact(moduleHash[:]); err != nil { return state, err } - if err := writeBytes(info.Asm); err != nil { + if err := writeBytes(program); err != nil { return state, err } } diff --git a/validator/server_jit/machine_loader.go b/validator/server_jit/machine_loader.go index b2bdb65322..cfa475370c 100644 --- a/validator/server_jit/machine_loader.go +++ b/validator/server_jit/machine_loader.go @@ -27,16 +27,13 @@ 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") || 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 703e761af5..92b50b17cb 100644 --- a/validator/server_jit/spawner.go +++ b/validator/server_jit/spawner.go @@ -9,6 +9,7 @@ import ( flag "github.com/spf13/pflag" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" @@ -39,7 +40,7 @@ func JitSpawnerConfigAddOptions(prefix string, f *flag.FlagSet) { type JitSpawner struct { stopwaiter.StopWaiter - count int32 + count atomic.Int32 locator *server_common.MachineLocator machineLoader *JitMachineLoader config JitSpawnerConfigFecher @@ -71,6 +72,10 @@ func (v *JitSpawner) WasmModuleRoots() ([]common.Hash, error) { return v.locator.ModuleRoots(), nil } +func (v *JitSpawner) StylusArchs() []rawdb.Target { + return []rawdb.Target{rawdb.LocalTarget()} +} + func (v *JitSpawner) execute( ctx context.Context, entry *validator.ValidationInput, moduleRoot common.Hash, ) (validator.GoGlobalState, error) { @@ -91,9 +96,9 @@ func (s *JitSpawner) Name() string { } func (v *JitSpawner) Launch(entry *validator.ValidationInput, moduleRoot common.Hash) validator.ValidationRun { - atomic.AddInt32(&v.count, 1) + v.count.Add(1) promise := stopwaiter.LaunchPromiseThread[validator.GoGlobalState](v, func(ctx context.Context) (validator.GoGlobalState, error) { - defer atomic.AddInt32(&v.count, -1) + defer v.count.Add(-1) return v.execute(ctx, entry, moduleRoot) }) return server_common.NewValRun(promise, moduleRoot) diff --git a/validator/validation_entry.go b/validator/validation_entry.go index 446f84ca62..2c357659ad 100644 --- a/validator/validation_entry.go +++ b/validator/validation_entry.go @@ -2,7 +2,7 @@ package validator import ( "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/offchainlabs/nitro/arbutil" ) @@ -17,7 +17,7 @@ type ValidationInput struct { HasDelayedMsg bool DelayedMsgNr uint64 Preimages map[arbutil.PreimageType]map[common.Hash][]byte - UserWasms state.UserWasms + UserWasms map[rawdb.Target]map[common.Hash][]byte BatchInfo []BatchInfo DelayedMsg []byte StartState GoGlobalState diff --git a/validator/valnode/redis/consumer.go b/validator/valnode/redis/consumer.go index 84f597c095..fb7db1e870 100644 --- a/validator/valnode/redis/consumer.go +++ b/validator/valnode/redis/consumer.go @@ -37,7 +37,7 @@ func NewValidationServer(cfg *ValidationServerConfig, spawner validator.Validati 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) + c, err := pubsub.NewConsumer[*validator.ValidationInput, validator.GoGlobalState](redisClient, server_api.RedisStreamForRoot(cfg.StreamPrefix, mr), &cfg.ConsumerConfig) if err != nil { return nil, fmt.Errorf("creating consumer for validation: %w", err) } @@ -130,10 +130,12 @@ type ValidationServerConfig struct { ModuleRoots []string `koanf:"module-roots"` // Timeout on polling for existence of each redis stream. StreamTimeout time.Duration `koanf:"stream-timeout"` + StreamPrefix string `koanf:"stream-prefix"` } var DefaultValidationServerConfig = ValidationServerConfig{ RedisURL: "", + StreamPrefix: "", ConsumerConfig: pubsub.DefaultConsumerConfig, ModuleRoots: []string{}, StreamTimeout: 10 * time.Minute, @@ -141,6 +143,7 @@ var DefaultValidationServerConfig = ValidationServerConfig{ var TestValidationServerConfig = ValidationServerConfig{ RedisURL: "", + StreamPrefix: "test-", ConsumerConfig: pubsub.TestConsumerConfig, ModuleRoots: []string{}, StreamTimeout: time.Minute, @@ -150,6 +153,7 @@ func ValidationServerConfigAddOptions(prefix string, f *pflag.FlagSet) { pubsub.ConsumerConfigAddOptions(prefix+".consumer-config", f) f.StringSlice(prefix+".module-roots", nil, "Supported module root hashes") f.String(prefix+".redis-url", DefaultValidationServerConfig.RedisURL, "url of redis server") + f.String(prefix+".stream-prefix", DefaultValidationServerConfig.StreamPrefix, "prefix for stream name") f.Duration(prefix+".stream-timeout", DefaultValidationServerConfig.StreamTimeout, "Timeout on polling for existence of redis streams") } diff --git a/validator/valnode/validation_api.go b/validator/valnode/validation_api.go index 3299366821..a79ac7fa55 100644 --- a/validator/valnode/validation_api.go +++ b/validator/valnode/validation_api.go @@ -12,6 +12,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/rawdb" "github.com/offchainlabs/nitro/util/stopwaiter" "github.com/offchainlabs/nitro/validator" @@ -44,6 +45,10 @@ func (a *ValidationServerAPI) WasmModuleRoots() ([]common.Hash, error) { return a.spawner.WasmModuleRoots() } +func (a *ValidationServerAPI) StylusArchs() ([]rawdb.Target, error) { + return a.spawner.StylusArchs(), nil +} + func NewValidationServerAPI(spawner validator.ValidationSpawner) *ValidationServerAPI { return &ValidationServerAPI{spawner} } diff --git a/wsbroadcastserver/clientconnection.go b/wsbroadcastserver/clientconnection.go index ba70756c98..16a8f64daf 100644 --- a/wsbroadcastserver/clientconnection.go +++ b/wsbroadcastserver/clientconnection.go @@ -51,7 +51,7 @@ type ClientConnection struct { requestedSeqNum arbutil.MessageIndex LastSentSeqNum atomic.Uint64 - lastHeardUnix int64 + lastHeardUnix atomic.Int64 out chan message backlog backlog.Backlog registered chan bool @@ -74,7 +74,7 @@ func NewClientConnection( delay time.Duration, bklg backlog.Backlog, ) *ClientConnection { - return &ClientConnection{ + clientConnection := &ClientConnection{ conn: conn, clientIp: connectingIP, desc: desc, @@ -82,7 +82,6 @@ func NewClientConnection( Name: fmt.Sprintf("%s@%s-%d", connectingIP, conn.RemoteAddr(), rand.Intn(10)), clientAction: clientAction, requestedSeqNum: requestedSeqNum, - lastHeardUnix: time.Now().Unix(), out: make(chan message, maxSendQueue), compression: compression, flateReader: NewFlateReader(), @@ -91,6 +90,8 @@ func NewClientConnection( registered: make(chan bool, 1), backlogSent: false, } + clientConnection.lastHeardUnix.Store(time.Now().Unix()) + return clientConnection } func (cc *ClientConnection) Age() time.Duration { @@ -192,6 +193,7 @@ func (cc *ClientConnection) Start(parentCtx context.Context) { t := time.NewTimer(cc.delay) select { case <-ctx.Done(): + t.Stop() return case <-t.C: } @@ -221,8 +223,10 @@ func (cc *ClientConnection) Start(parentCtx context.Context) { timer := time.NewTimer(5 * time.Second) select { case <-ctx.Done(): + timer.Stop() return case <-cc.registered: + timer.Stop() log.Debug("ClientConnection registered with ClientManager", "client", cc.Name) case <-timer.C: log.Error("timed out waiting for ClientConnection to register with ClientManager", "client", cc.Name) @@ -287,7 +291,7 @@ func (cc *ClientConnection) RequestedSeqNum() arbutil.MessageIndex { } func (cc *ClientConnection) GetLastHeard() time.Time { - return time.Unix(atomic.LoadInt64(&cc.lastHeardUnix), 0) + return time.Unix(cc.lastHeardUnix.Load(), 0) } // Receive reads next message from client's underlying connection. @@ -307,7 +311,7 @@ func (cc *ClientConnection) readRequest(ctx context.Context, timeout time.Durati cc.ioMutex.Lock() defer cc.ioMutex.Unlock() - atomic.StoreInt64(&cc.lastHeardUnix, time.Now().Unix()) + cc.lastHeardUnix.Store(time.Now().Unix()) var data []byte var opCode ws.OpCode diff --git a/wsbroadcastserver/clientmanager.go b/wsbroadcastserver/clientmanager.go index a88716756a..4b7b7a2bcb 100644 --- a/wsbroadcastserver/clientmanager.go +++ b/wsbroadcastserver/clientmanager.go @@ -44,7 +44,7 @@ type ClientManager struct { stopwaiter.StopWaiter clientPtrMap map[*ClientConnection]bool - clientCount int32 + clientCount atomic.Int32 pool *gopool.Pool poller netpoll.Poller broadcastChan chan *m.BroadcastMessage @@ -85,7 +85,7 @@ func (cm *ClientManager) registerClient(ctx context.Context, clientConnection *C clientsCurrentGauge.Inc(1) clientsConnectCount.Inc(1) - atomic.AddInt32(&cm.clientCount, 1) + cm.clientCount.Add(1) cm.clientPtrMap[clientConnection] = true clientsTotalSuccessCounter.Inc(1) @@ -120,7 +120,7 @@ func (cm *ClientManager) removeClientImpl(clientConnection *ClientConnection) { clientsDurationHistogram.Update(clientConnection.Age().Microseconds()) clientsCurrentGauge.Dec(1) clientsDisconnectCount.Inc(1) - atomic.AddInt32(&cm.clientCount, -1) + cm.clientCount.Add(-1) } func (cm *ClientManager) removeClient(clientConnection *ClientConnection) { @@ -137,7 +137,7 @@ func (cm *ClientManager) removeClient(clientConnection *ClientConnection) { } func (cm *ClientManager) ClientCount() int32 { - return atomic.LoadInt32(&cm.clientCount) + return cm.clientCount.Load() } // Broadcast sends batch item to all clients.