diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 00000000000..eb8a14bd9c4 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,4 @@ +[env] +LLVM_SYS_181_PREFIX = "/usr/lib/llvm-18/" +MLIR_SYS_180_PREFIX = "/usr/lib/llvm-18/" +TABLEGEN_180_PREFIX = "/usr/lib/llvm-18/" diff --git a/.github/actions/bootstrap/action.yml b/.github/actions/bootstrap/action.yml index b8366c9836b..5201006e79e 100644 --- a/.github/actions/bootstrap/action.yml +++ b/.github/actions/bootstrap/action.yml @@ -3,3 +3,5 @@ runs: steps: - name: Install rust. uses: ./.github/actions/install_rust + - name: Install cairo native. + uses: ./.github/actions/setup_native_deps diff --git a/.github/actions/install_rust/rust_version.txt b/.github/actions/install_rust/rust_version.txt deleted file mode 100644 index 2bf5ad0447d..00000000000 --- a/.github/actions/install_rust/rust_version.txt +++ /dev/null @@ -1 +0,0 @@ -stable diff --git a/.github/actions/setup_native_deps/action.yml b/.github/actions/setup_native_deps/action.yml new file mode 100644 index 00000000000..e0e4c7e55d2 --- /dev/null +++ b/.github/actions/setup_native_deps/action.yml @@ -0,0 +1,16 @@ +name: "Setup Cairo Native Dependencies" +description: "Sets up LLVM and GMP libraries" + +outputs: + cairo-native-runtime-library: + description: "The path to the cairo native runtime library" + value: ${{ steps.set-env-vars.outputs.cairo-native-runtime-library }} + +runs: + using: "composite" + steps: + - name: Install Cairo Native Runtime Dependencies + id: set-runtime-deps + shell: bash + run: | + sudo ./scripts/dependencies.sh diff --git a/.github/workflows/blockifier_ci.yml b/.github/workflows/blockifier_ci.yml index b7857358ee6..335a6cedbb0 100644 --- a/.github/workflows/blockifier_ci.yml +++ b/.github/workflows/blockifier_ci.yml @@ -23,18 +23,20 @@ on: # Other than code-related changes, all changes related to the native-blockifier build-and-push # process should trigger the build (e.g., changes to the Dockerfile, build scripts, etc.). - '.github/workflows/blockifier_ci.yml' + - '.github/workflows/upload_artifacts_workflow.yml' - 'build_native_in_docker.sh' - 'Cargo.lock' - 'Cargo.toml' - 'crates/blockifier/**' - 'crates/native_blockifier/**' - 'scripts/build_native_blockifier.sh' + - 'scripts/dependencies.sh' - 'scripts/install_build_tools.sh' - 'scripts/sequencer-ci.Dockerfile' # On PR events, cancel existing CI runs on this same PR for this workflow. concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: @@ -45,60 +47,3 @@ jobs: - uses: ./.github/actions/bootstrap - run: cargo build -p blockifier - run: cargo test -p blockifier - - native-blockifier-artifacts-push: - runs-on: starkware-ubuntu-20-04-medium - steps: - - uses: actions/checkout@v4 - - uses: ./.github/actions/bootstrap - - name: Build native blockifier - run: ./build_native_in_docker.sh scripts/build_native_blockifier.sh - - # Commit hash on pull request event would be the head commit of the branch. - - name: Get commit hash prefix for PR update - if: ${{ github.event_name == 'pull_request' }} - env: - COMMIT_SHA: ${{ github.event.pull_request.head.sha }} - run: echo "SHORT_HASH=${COMMIT_SHA:0:7}" >> $GITHUB_ENV - - # On push event (to main, for example) we should take the commit post-push. - - name: Get commit hash prefix for merge - if: ${{ github.event_name != 'pull_request' }} - env: - COMMIT_SHA: ${{ github.event.after }} - run: echo "SHORT_HASH=${COMMIT_SHA:0:7}" >> $GITHUB_ENV - - # Rename is required; see https://pyo3.rs/v0.19.2/building_and_distribution#manual-builds. - - name: Rename shared object - run: | - mv \ - target/release/libnative_blockifier.so \ - target/release/native_blockifier.pypy39-pp73-x86_64-linux-gnu.so - - # Check if the user has the required permission to upload the artifact. - - name: Get User Permission - id: checkAccess - uses: actions-cool/check-user-permission@v2 - with: - require: write - username: ${{ github.triggering_actor }} - - - name: Check User Permission - if: steps.checkAccess.outputs.require-result == 'false' - run: | - echo "${{ github.triggering_actor }} does not have permissions on this repo." - echo "Current permission level is ${{ steps.checkAccess.outputs.user-permission }}" - echo "Job originally triggered by ${{ github.actor }}" - exit 1 - - - name: Authenticate with GCS - uses: "google-github-actions/auth@v2" - with: - credentials_json: ${{ secrets.SA_NATIVE_BLOCKIFIER_ARTIFACTS_BUCKET_WRITER_ACCESS_KEY }} - - - name: Upload binary to GCP - id: upload_file - uses: "google-github-actions/upload-cloud-storage@v2" - with: - path: "target/release/native_blockifier.pypy39-pp73-x86_64-linux-gnu.so" - destination: "native_blockifier_artifacts/${{ env.SHORT_HASH }}/release/" diff --git a/.github/workflows/blockifier_compiled_cairo.yml b/.github/workflows/blockifier_compiled_cairo.yml index 916837c8972..5703f3e3e10 100644 --- a/.github/workflows/blockifier_compiled_cairo.yml +++ b/.github/workflows/blockifier_compiled_cairo.yml @@ -1,18 +1,6 @@ name: Blockifier-Compiled-Cairo on: - push: - branches: - - main - - main-v[0-9].** - tags: - - v[0-9].** - paths: - - '.github/workflows/blockifier_compiled_cairo.yml' - - 'crates/blockifier/feature_contracts/**' - - 'crates/blockifier/src/test_utils/cairo_compile.rs' - - 'crates/blockifier/tests/feature_contracts_compatibility_test.rs' - - 'crates/blockifier/tests/requirements.txt' pull_request: types: - opened @@ -24,10 +12,11 @@ on: - 'crates/blockifier/src/test_utils/cairo_compile.rs' - 'crates/blockifier/tests/feature_contracts_compatibility_test.rs' - 'crates/blockifier/tests/requirements.txt' + - 'scripts/dependencies.sh' # On PR events, cancel existing CI runs on this same PR for this workflow. concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: diff --git a/.github/workflows/blockifier_post-merge.yml b/.github/workflows/blockifier_post-merge.yml index 1afff5e93e1..a001ccb6595 100644 --- a/.github/workflows/blockifier_post-merge.yml +++ b/.github/workflows/blockifier_post-merge.yml @@ -8,6 +8,7 @@ on: - '.github/workflows/blockifier_post-merge.yml' - 'crates/blockifier/**' - 'crates/native_blockifier/**' + - 'scripts/dependencies.sh' jobs: if_merged: @@ -16,7 +17,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/bootstrap - - uses: Noelware/setup-protoc@1.1.0 # Setup pypy and link to the location expected by .cargo/config.toml. - uses: actions/setup-python@v5 diff --git a/.github/workflows/committer_ci.yml b/.github/workflows/committer_ci.yml index 358c17f1c7b..c4cf79d2fe4 100644 --- a/.github/workflows/committer_ci.yml +++ b/.github/workflows/committer_ci.yml @@ -15,6 +15,7 @@ on: - 'crates/starknet_api/**' - 'crates/starknet_committer/**' - 'crates/starknet_patricia/**' + - 'scripts/dependencies.sh' pull_request: types: @@ -31,10 +32,11 @@ on: - 'crates/starknet_api/**' - 'crates/starknet_committer/**' - 'crates/starknet_patricia/**' + - 'scripts/dependencies.sh' # On PR events, cancel existing CI runs on this same PR for this workflow. concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: @@ -74,7 +76,7 @@ jobs: # List the existing benchmarks. - run: | - cargo bench -p committer_cli -- --list | grep ': benchmark$' | sed -e "s/: benchmark$//" > benchmarks_list.txt + cargo bench -p committer_cli -- --list | grep ': benchmark$' | sed -e "s/: benchmark$//" > benchmarks_list.txt # Benchmark the old code. - run: cargo bench -p committer_cli @@ -117,6 +119,7 @@ jobs: owner: context.repo.owner, repo: context.repo.repo, body: fs.readFileSync('bench_new.txt', 'utf8'), + path: 'Commits' }) gcs-push: diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1de2c3603b6..a87db9c686e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -18,7 +18,7 @@ on: # On PR events, cancel existing CI runs on this same PR for this workflow. concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: @@ -50,7 +50,6 @@ jobs: steps: # Enviorment setup. - uses: actions/checkout@v4 - - uses: Noelware/setup-protoc@1.1.0 - uses: baptiste0928/cargo-install@v3 with: crate: taplo-cli @@ -95,14 +94,13 @@ jobs: - run: cargo test -p workspace_tests run-tests: - runs-on: starkware-ubuntu-20-04-medium + runs-on: starkware-ubuntu-latest-large steps: - uses: actions/checkout@v4 with: # Fetch the entire history. fetch-depth: 0 - uses: ./.github/actions/bootstrap - - uses: Noelware/setup-protoc@1.1.0 # Setup pypy and link to the location expected by .cargo/config.toml. - uses: actions/setup-python@v5 @@ -145,7 +143,7 @@ jobs: uses: upsidr/merge-gatekeeper@v1 with: token: ${{ secrets.GITHUB_TOKEN }} - timeout: 1200 + timeout: 1500 interval: 30 ignored: "code-review/reviewable" @@ -155,7 +153,7 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} ref: ${{github.ref}} - timeout: 1200 + timeout: 1500 interval: 30 ignored: "code-review/reviewable" @@ -167,13 +165,6 @@ jobs: # Fetch the entire history. fetch-depth: 0 - uses: ./.github/actions/bootstrap - - name: Set-Up - run: | - sudo apt-get update - sudo apt-get install -y clang llvm libudev-dev - - uses: Noelware/setup-protoc@1.1.0 - with: - version: ${{env.PROTOC_VERSION}} - name: Install cargo-llvm-cov uses: taiki-e/install-action@cargo-llvm-cov diff --git a/.github/workflows/merge_paths_ci.yml b/.github/workflows/merge_paths_ci.yml index 65e11b51f24..200aa1b5737 100644 --- a/.github/workflows/merge_paths_ci.yml +++ b/.github/workflows/merge_paths_ci.yml @@ -1,19 +1,6 @@ name: Merge-paths test on: - push: - branches: - - main - - main-v[0-9].** - tags: - - v[0-9].** - paths: - - '.github/workflows/merge_paths_ci.yml' - - 'scripts/merge_branches.py' - - 'scripts/merge_paths.json' - - 'scripts/merge_paths_test.py' - - 'scripts/merge_status.py' - pull_request: types: - opened @@ -23,6 +10,7 @@ on: - edited paths: - '.github/workflows/merge_paths_ci.yml' + - 'scripts/dependencies.sh' - 'scripts/merge_branches.py' - 'scripts/merge_paths.json' - 'scripts/merge_paths_test.py' @@ -30,7 +18,7 @@ on: # On PR events, cancel existing CI runs on this same PR for this workflow. concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: diff --git a/.github/workflows/papyrus_ci.yml b/.github/workflows/papyrus_ci.yml index 19e1b2199d0..b93df499232 100644 --- a/.github/workflows/papyrus_ci.yml +++ b/.github/workflows/papyrus_ci.yml @@ -12,6 +12,7 @@ on: - 'crates/papyrus**/**' - 'crates/sequencing/**' - 'crates/starknet_client/**' + - 'scripts/dependencies.sh' pull_request: types: @@ -29,27 +30,22 @@ on: - 'crates/papyrus**/**' - 'crates/sequencing/**' - 'crates/starknet_client/**' + - 'scripts/dependencies.sh' merge_group: types: [checks_requested] # On PR events, cancel existing CI runs on this same PR for this workflow. concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} -env: - PROTOC_VERSION: v25.1 - jobs: executable-run: runs-on: starkware-ubuntu-latest-medium steps: - uses: actions/checkout@v4 - uses: ./.github/actions/bootstrap - - uses: Noelware/setup-protoc@1.1.0 - with: - version: ${{env.PROTOC_VERSION}} - name: Build node run: | mkdir data @@ -65,9 +61,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/bootstrap - - uses: Noelware/setup-protoc@1.1.0 - with: - version: ${{env.PROTOC_VERSION}} - name: Build node run: | mkdir data @@ -82,28 +75,19 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - - uses: Noelware/setup-protoc@1.1.0 - with: - version: ${{env.PROTOC_VERSION}} + - uses: ./.github/actions/bootstrap - name: Build node - run: | - cargo build -r -p papyrus_node + run: cargo build -r -p papyrus_node - name: Run p2p sync end-to-end test - run: | - scripts/papyrus/p2p_sync_e2e_test/main.sh ${{ secrets.CI_BASE_LAYER_NODE_URL }} + run: scripts/papyrus/p2p_sync_e2e_test/main.sh ${{ secrets.CI_BASE_LAYER_NODE_URL }} integration-test: runs-on: starkware-ubuntu-latest-medium steps: - uses: actions/checkout@v4 - uses: ./.github/actions/bootstrap - - uses: Noelware/setup-protoc@1.1.0 - with: - version: ${{env.PROTOC_VERSION}} - run: > cargo test -r --test latency_histogram @@ -117,8 +101,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: ./.github/actions/bootstrap - - uses: Noelware/setup-protoc@1.1.0 - - run: | cargo test -p papyrus_node --no-default-features env: diff --git a/.github/workflows/papyrus_docker-publish.yml b/.github/workflows/papyrus_docker-publish.yml index 2035a801d0c..294bd71fb72 100644 --- a/.github/workflows/papyrus_docker-publish.yml +++ b/.github/workflows/papyrus_docker-publish.yml @@ -8,15 +8,17 @@ on: paths: - '.github/workflows/papyrus_docker-publish.yml' - 'crates/papyrus**/**' + - 'scripts/dependencies.sh' pull_request: paths: - '.github/workflows/papyrus_docker-publish.yml' - 'crates/papyrus**/**' + - 'scripts/dependencies.sh' # On PR events, cancel existing CI runs on this same PR for this workflow. concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.job }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: @@ -25,7 +27,7 @@ env: jobs: docker-build-push: - runs-on: starkware-ubuntu-latest-medium + runs-on: starkware-ubuntu-latest-large steps: - name: Checkout repository diff --git a/.github/workflows/papyrus_nightly-tests.yml b/.github/workflows/papyrus_nightly-tests.yml index 927dcb9b7b3..0592f28e3e5 100644 --- a/.github/workflows/papyrus_nightly-tests.yml +++ b/.github/workflows/papyrus_nightly-tests.yml @@ -6,9 +6,6 @@ on: - cron: '30 0 * * *' # Uses macos runner. workflow_dispatch: # Uses ubuntu runner. -env: - PROTOC_VERSION: 25 # homebrew doesn't support minor versions - jobs: GW-integration-test-ubuntu: uses: ./.github/workflows/papyrus_nightly-tests-call.yml @@ -41,8 +38,6 @@ jobs: - run: mkdir data - - run: brew install protobuf@$PROTOC_VERSION - - name: Build node run: cargo build -r -p papyrus_node @@ -59,7 +54,6 @@ jobs: - uses: ./.github/actions/bootstrap - run: npm install -g ganache@7.4.3 - - run: brew install protobuf@$PROTOC_VERSION - run: | cargo test -r -p papyrus_node diff --git a/.github/workflows/upload_artifacts_workflow.yml b/.github/workflows/upload_artifacts_workflow.yml new file mode 100644 index 00000000000..838065f94ee --- /dev/null +++ b/.github/workflows/upload_artifacts_workflow.yml @@ -0,0 +1,52 @@ +name: Upload-Artifacts + +on: + workflow_run: + workflows: [Blockifier-CI] + types: [completed] + +jobs: + native-blockifier-artifacts-push: + runs-on: starkware-ubuntu-20-04-medium + steps: + - name: Get commit hash prefix for PR update + env: + COMMIT_SHA: ${{ github.event.workflow_run.head_commit.id }} + run: | + echo "SHORT_HASH=${COMMIT_SHA:0:7}" >> $GITHUB_ENV + echo "COMMIT_SHA=${COMMIT_SHA}" >> $GITHUB_ENV + + - name: Check tests status + if: ${{ github.event.workflow_run.conclusion == 'failure' }} + run: | + echo 'Blockifier-CI workflow failed' + exit 1 + - uses: actions/checkout@v4 + with: + ref: ${{ env.COMMIT_SHA }} + - uses: ./.github/actions/bootstrap + - name: Build native blockifier + run: ./build_native_in_docker.sh scripts/build_native_blockifier.sh + + # Commit hash on pull request event would be the head commit of the branch. + + # TODO: Add post merge event to get the commit hash. + + # Rename is required; see https://pyo3.rs/v0.19.2/building_and_distribution#manual-builds. + - name: Rename shared object + run: | + mv \ + target/release/libnative_blockifier.so \ + target/release/native_blockifier.pypy39-pp73-x86_64-linux-gnu.so + + - name: Authenticate with GCS + uses: "google-github-actions/auth@v2" + with: + credentials_json: ${{ secrets.SA_NATIVE_BLOCKIFIER_ARTIFACTS_BUCKET_WRITER_ACCESS_KEY }} + + - name: Upload binary to GCP + id: upload_file + uses: "google-github-actions/upload-cloud-storage@v2" + with: + path: "target/release/native_blockifier.pypy39-pp73-x86_64-linux-gnu.so" + destination: "native_blockifier_artifacts/${{ env.SHORT_HASH }}/release/" diff --git a/Cargo.lock b/Cargo.lock index 1c772b9ae94..ace0f443047 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -576,6 +576,20 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +[[package]] +name = "aquamarine" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21cc1548309245035eb18aa7f0967da6bc65587005170c56e6ef2788a4cf3f4e" +dependencies = [ + "include_dir", + "itertools 0.10.5", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.61", +] + [[package]] name = "arc-swap" version = "1.7.1" @@ -936,8 +950,8 @@ dependencies = [ "futures-lite 1.13.0", "log", "parking", - "polling 2.8.0", - "rustix 0.37.27", + "polling 2.5.2", + "rustix 0.37.13", "slab", "socket2 0.4.10", "waker-fn", @@ -1192,7 +1206,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", - "bitflags 1.3.2", + "bitflags 1.2.1", "bytes", "futures-util", "http 0.2.12", @@ -1361,12 +1375,15 @@ dependencies = [ "itertools 0.12.1", "lazy_static", "lazycell", + "log", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", "syn 2.0.61", + "which", ] [[package]] @@ -1386,9 +1403,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" -version = "1.3.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "bitflags" @@ -1450,6 +1467,7 @@ dependencies = [ "cairo-lang-runner", "cairo-lang-starknet-classes", "cairo-lang-utils", + "cairo-native", "cairo-vm", "criterion", "derive_more 0.99.17", @@ -1489,7 +1507,18 @@ dependencies = [ name = "blockifier_regression_test" version = "0.0.0" dependencies = [ + "assert_matches", "blockifier", + "cairo-lang-starknet-classes", + "cairo-lang-utils", + "cairo-vm", + "flate2", + "papyrus_execution", + "pretty_assertions", + "rstest", + "serde", + "serde_json", + "starknet-core", "starknet-types-core", "starknet_api", "starknet_gateway", @@ -2119,6 +2148,33 @@ dependencies = [ "xshell", ] +[[package]] +name = "cairo-lang-test-plugin" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f1597b8229a3649183ff33b19f0aeca5d86505253ebbbce377b271d1732835" +dependencies = [ + "anyhow", + "cairo-lang-compiler", + "cairo-lang-debug", + "cairo-lang-defs", + "cairo-lang-filesystem", + "cairo-lang-lowering", + "cairo-lang-semantic", + "cairo-lang-sierra", + "cairo-lang-sierra-generator", + "cairo-lang-starknet", + "cairo-lang-starknet-classes", + "cairo-lang-syntax", + "cairo-lang-utils", + "indoc 2.0.5", + "itertools 0.12.1", + "num-bigint 0.4.5", + "num-traits 0.2.19", + "serde", + "starknet-types-core", +] + [[package]] name = "cairo-lang-test-utils" version = "2.8.2" @@ -2148,6 +2204,71 @@ dependencies = [ "serde", ] +[[package]] +name = "cairo-native" +version = "0.2.0" +source = "git+https://github.com/lambdaclass/cairo_native?rev=2be717cba74c63628cb68b619ff2022c70d0cdd2#2be717cba74c63628cb68b619ff2022c70d0cdd2" +dependencies = [ + "anyhow", + "aquamarine", + "bumpalo", + "cairo-lang-compiler", + "cairo-lang-defs", + "cairo-lang-diagnostics", + "cairo-lang-filesystem", + "cairo-lang-runner", + "cairo-lang-semantic", + "cairo-lang-sierra", + "cairo-lang-sierra-ap-change", + "cairo-lang-sierra-gas", + "cairo-lang-sierra-generator", + "cairo-lang-starknet", + "cairo-lang-starknet-classes", + "cairo-lang-test-plugin", + "cairo-lang-utils", + "cairo-native-runtime", + "cc", + "clap", + "colored", + "educe", + "itertools 0.13.0", + "k256", + "keccak", + "lazy_static", + "libc", + "libloading", + "llvm-sys", + "melior", + "mlir-sys", + "num-bigint 0.4.5", + "num-traits 0.2.19", + "p256", + "sec1", + "serde", + "serde_json", + "sha2", + "starknet-types-core", + "stats_alloc", + "tempfile", + "thiserror", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "cairo-native-runtime" +version = "0.2.0" +source = "git+https://github.com/lambdaclass/cairo_native?rev=2be717cba74c63628cb68b619ff2022c70d0cdd2#2be717cba74c63628cb68b619ff2022c70d0cdd2" +dependencies = [ + "cairo-lang-sierra-gas", + "lazy_static", + "libc", + "rand 0.8.5", + "starknet-crypto 0.7.1", + "starknet-curve 0.5.0", + "starknet-types-core", +] + [[package]] name = "cairo-vm" version = "1.0.1" @@ -2217,6 +2338,16 @@ dependencies = [ "thiserror", ] +[[package]] +name = "caseless" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808dab3318747be122cb31d36de18d4d1c81277a76f8332a02b81a3d73463d7f" +dependencies = [ + "regex", + "unicode-normalization", +] + [[package]] name = "cast" version = "0.3.0" @@ -2225,13 +2356,13 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.97" +version = "1.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" dependencies = [ "jobserver", "libc", - "once_cell", + "shlex", ] [[package]] @@ -2389,7 +2520,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", ] [[package]] @@ -2489,6 +2620,23 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "comrak" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d061c6d53fe98c25efda0d91b7f6b4b4020a51dad78a3eac5028710aa26f8e7" +dependencies = [ + "caseless", + "derive_builder", + "entities", + "memchr", + "once_cell", + "regex", + "slug", + "typed-arena", + "unicode_categories", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -2926,6 +3074,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] @@ -2963,6 +3112,37 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_builder" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd33f37ee6a119146a1781d3356a7c26028f83d779b2e04ecd45fdc75c76877b" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7431fa049613920234f22c47fdc33e6cf3ee83067091ea4277a3f8c4587aae38" +dependencies = [ + "darling 0.20.8", + "proc-macro2", + "quote", + "syn 2.0.61", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4abae7035bf79b9877b779505d8cf3749285b80c43941eda66604841889451dc" +dependencies = [ + "derive_builder_core", + "syn 2.0.61", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -2997,6 +3177,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "deunicode" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00" + [[package]] name = "diff" version = "0.1.13" @@ -3155,6 +3341,18 @@ dependencies = [ "zeroize", ] +[[package]] +name = "educe" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.61", +] + [[package]] name = "either" version = "1.11.0" @@ -3173,6 +3371,7 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", @@ -3222,6 +3421,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "entities" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" + [[package]] name = "enum-as-inner" version = "0.6.0" @@ -3265,6 +3470,26 @@ dependencies = [ "syn 2.0.61", ] +[[package]] +name = "enum-ordinalize" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.61", +] + [[package]] name = "env_filter" version = "0.1.0" @@ -4815,6 +5040,25 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indent" version = "0.1.1" @@ -5324,11 +5568,11 @@ 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" dependencies = [ - "spin 0.5.2", + "spin 0.9.8", ] [[package]] @@ -5339,9 +5583,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libloading" @@ -5860,6 +6104,20 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "llvm-sys" +version = "181.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d320f9d2723c97d4b78f9190a61ed25cc7cfbe456668c08e6e7dd8e50ceb8500" +dependencies = [ + "anyhow", + "cc", + "lazy_static", + "libc", + "regex-lite", + "semver 1.0.23", +] + [[package]] name = "lock_api" version = "0.4.12" @@ -5963,6 +6221,35 @@ dependencies = [ "libc", ] +[[package]] +name = "melior" +version = "0.18.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3085c0169aa3b735d8e7df582baee23c2aeb280ea62cc7f71effda28d8e281" +dependencies = [ + "dashmap", + "melior-macro", + "mlir-sys", + "once_cell", +] + +[[package]] +name = "melior-macro" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d58c356ebaa7855da67aad1306a0d032b68919d3c65b0b5dcecf10d9bdf6a9" +dependencies = [ + "comrak", + "convert_case 0.6.0", + "once_cell", + "proc-macro2", + "quote", + "regex", + "syn 2.0.61", + "tblgen-alt", + "unindent 0.2.3", +] + [[package]] name = "memchr" version = "2.7.2" @@ -5978,6 +6265,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg 1.3.0", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -5993,6 +6289,7 @@ version = "0.0.0" dependencies = [ "assert_matches", "blockifier", + "pretty_assertions", "serde_json", "starknet-types-core", "starknet_api", @@ -6120,6 +6417,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "mlir-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1b5806a63bc959cd5c4e5db8cadd20e40045d41969d325132748db8af11b7f" +dependencies = [ + "bindgen 0.69.4", +] + [[package]] name = "mockall" version = "0.12.1" @@ -6327,7 +6633,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9ea4302b9759a7a88242299225ea3688e63c85ea136371bb6cf94fd674efaab" dependencies = [ "anyhow", - "bitflags 1.3.2", + "bitflags 1.2.1", "byteorder", "libc", "netlink-packet-core", @@ -6381,13 +6687,26 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e06129fb611568ef4e868c14b326274959aa70ff7776e9d55323531c374945" +dependencies = [ + "bitflags 1.2.1", + "cc", + "cfg-if", + "libc", + "memoffset 0.6.5", +] + [[package]] name = "nix" version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "cfg-if", "libc", ] @@ -6789,6 +7108,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "page_size" version = "0.6.0" @@ -6875,9 +7206,11 @@ dependencies = [ "lru", "metrics 0.21.1", "mockall", + "nix 0.20.2", "papyrus_common", "papyrus_config", "papyrus_network", + "papyrus_network_types", "papyrus_protobuf", "papyrus_storage", "papyrus_test_utils", @@ -6984,6 +7317,7 @@ version = "0.0.0" dependencies = [ "assert_matches", "async-stream", + "async-trait", "bytes", "deadqueue", "defaultmap", @@ -6996,6 +7330,7 @@ dependencies = [ "mockall", "papyrus_common", "papyrus_config", + "papyrus_network_types", "pretty_assertions", "replace_with", "serde", @@ -7010,6 +7345,16 @@ dependencies = [ "void", ] +[[package]] +name = "papyrus_network_types" +version = "0.0.0" +dependencies = [ + "libp2p", + "papyrus_test_utils", + "rand_chacha 0.3.1", + "serde", +] + [[package]] name = "papyrus_node" version = "0.0.0" @@ -7409,6 +7754,15 @@ dependencies = [ "serde", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -7609,18 +7963,16 @@ dependencies = [ [[package]] name = "polling" -version = "2.8.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" dependencies = [ "autocfg 1.3.0", - "bitflags 1.3.2", "cfg-if", - "concurrent-queue", "libc", "log", - "pin-project-lite", - "windows-sys 0.48.0", + "wepoll-ffi", + "windows-sys 0.42.0", ] [[package]] @@ -7731,6 +8083,15 @@ dependencies = [ "syn 2.0.61", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "primitive-types" version = "0.12.2" @@ -7986,13 +8347,13 @@ dependencies = [ "hashbrown 0.14.5", "indoc 1.0.9", "libc", - "memoffset", + "memoffset 0.9.1", "num-bigint 0.4.5", "parking_lot", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", - "unindent", + "unindent 0.1.11", ] [[package]] @@ -8330,7 +8691,7 @@ version = "10.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", ] [[package]] @@ -8386,7 +8747,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", ] [[package]] @@ -8411,9 +8772,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.4" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -8441,6 +8802,12 @@ dependencies = [ "regex-syntax 0.8.3", ] +[[package]] +name = "regex-lite" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" + [[package]] name = "regex-syntax" version = "0.6.29" @@ -8806,11 +9173,11 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.27" +version = "0.37.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +checksum = "f79bef90eb6d984c72722595b5b1348ab39275a5e5123faca6863bf07d75a4e0" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "errno", "io-lifetimes", "libc", @@ -9164,11 +9531,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -9404,6 +9772,16 @@ dependencies = [ "autocfg 1.3.0", ] +[[package]] +name = "slug" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + [[package]] name = "smallvec" version = "1.13.2" @@ -9796,8 +10174,6 @@ dependencies = [ "starknet_batcher_types", "starknet_consensus_manager_types", "starknet_mempool_infra", - "tokio", - "tracing", "validator", ] @@ -9871,7 +10247,6 @@ dependencies = [ name = "starknet_http_server" version = "0.0.0" dependencies = [ - "async-trait", "axum", "papyrus_config", "serde", @@ -9898,6 +10273,7 @@ dependencies = [ "rstest", "starknet-types-core", "starknet_api", + "starknet_mempool", "starknet_mempool_infra", "starknet_mempool_types", "tokio", @@ -9927,11 +10303,11 @@ dependencies = [ name = "starknet_mempool_integration_tests" version = "0.0.0" dependencies = [ + "assert_matches", "axum", "blockifier", "cairo-lang-starknet-classes", "indexmap 2.2.6", - "itertools 0.12.1", "mempool_test_utils", "papyrus_common", "papyrus_rpc", @@ -9942,6 +10318,7 @@ dependencies = [ "serde_json", "starknet-types-core", "starknet_api", + "starknet_batcher", "starknet_client", "starknet_gateway", "starknet_gateway_types", @@ -9951,6 +10328,7 @@ dependencies = [ "starknet_mempool_types", "starknet_task_executor", "strum 0.25.0", + "tempfile", "tokio", ] @@ -10003,7 +10381,7 @@ name = "starknet_mempool_p2p_types" version = "0.0.0" dependencies = [ "async-trait", - "papyrus_network", + "papyrus_network_types", "papyrus_proc_macros", "serde", "starknet_api", @@ -10017,7 +10395,7 @@ version = "0.0.0" dependencies = [ "async-trait", "mockall", - "papyrus_network", + "papyrus_network_types", "papyrus_proc_macros", "serde", "starknet_api", @@ -10092,6 +10470,12 @@ dependencies = [ "rand 0.6.5", ] +[[package]] +name = "stats_alloc" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0e04424e733e69714ca1bbb9204c1a57f09f5493439520f9f68c132ad25eec" + [[package]] name = "string_cache" version = "0.8.7" @@ -10263,7 +10647,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ - "bitflags 1.3.2", + "bitflags 1.2.1", "core-foundation", "system-configuration-sys", ] @@ -10301,6 +10685,18 @@ version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" +[[package]] +name = "tblgen-alt" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae726d43658a13a9cd479de814be1311fea69236cd821e931a4fb9ca4d70e50" +dependencies = [ + "bindgen 0.69.4", + "cc", + "paste", + "thiserror", +] + [[package]] name = "tempfile" version = "3.10.1" @@ -10416,18 +10812,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", @@ -10832,6 +11228,16 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -10842,12 +11248,15 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] @@ -10907,6 +11316,12 @@ dependencies = [ "utf-8", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typenum" version = "1.17.0" @@ -10979,12 +11394,24 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "unindent" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" + [[package]] name = "universal-hash" version = "0.5.1" @@ -11294,6 +11721,15 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + [[package]] name = "which" version = "4.4.2" @@ -11400,6 +11836,21 @@ dependencies = [ "windows-targets 0.52.5", ] +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -11449,6 +11900,12 @@ dependencies = [ "windows_x86_64_msvc 0.52.5", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -11461,6 +11918,12 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -11473,6 +11936,12 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -11491,6 +11960,12 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -11503,6 +11978,12 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -11515,6 +11996,12 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -11527,6 +12014,12 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" diff --git a/Cargo.toml b/Cargo.toml index 1370c27bd0d..807131858ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ members = [ "crates/papyrus_load_test", "crates/papyrus_monitoring_gateway", "crates/papyrus_network", + "crates/papyrus_network_types", "crates/papyrus_node", "crates/papyrus_p2p_sync", "crates/papyrus_proc_macros", @@ -95,6 +96,8 @@ cairo-lang-sierra = "=2.8.2" cairo-lang-sierra-to-casm = "2.8.2" cairo-lang-starknet-classes = "2.8.2" cairo-lang-utils = "2.8.2" +# This is a temporary dependency, will be removed once the new version of cairo-native is released to main. +cairo-native = { git = "https://github.com/lambdaclass/cairo_native", rev = "2be717cba74c63628cb68b619ff2022c70d0cdd2" } cairo-vm = "=1.0.1" camelpaste = "0.1.0" chrono = "0.4.26" @@ -111,6 +114,7 @@ ethers = "2.0.3" ethers-core = "2.0.3" ethnum = "1.5.0" flate2 = "1.0.24" +fs2 = "0.4" futures = "0.3.21" futures-channel = "0.3.21" futures-util = "0.3.21" @@ -128,7 +132,7 @@ itertools = "0.12.1" jsonrpsee = "0.20.3" jsonschema = "0.17.0" keccak = "0.1.3" -lazy_static = "1.4.0" +lazy_static = "1.5.0" libmdbx = "0.3.5" libp2p = "0.53.2" libp2p-swarm-test = "0.3.0" @@ -141,6 +145,7 @@ metrics-exporter-prometheus = "0.12.1" metrics-process = "1.0.11" mockall = "0.12.1" mockito = "1.4.0" +nix = "0.20.0" num-bigint = "0.4" num-integer = "0.1.45" num-rational = "0.4" @@ -156,6 +161,7 @@ papyrus_consensus_orchestrator = { path = "crates/sequencing/papyrus_consensus_o papyrus_execution = { path = "crates/papyrus_execution", version = "0.0.0" } papyrus_monitoring_gateway = { path = "crates/papyrus_monitoring_gateway", version = "0.0.0" } papyrus_network = { path = "crates/papyrus_network", version = "0.0.0" } +papyrus_network_types = { path = "crates/papyrus_network_types", version = "0.0.0" } papyrus_p2p_sync = { path = "crates/papyrus_p2p_sync", version = "0.0.0" } papyrus_proc_macros = { path = "crates/papyrus_proc_macros", version = "0.0.0" } papyrus_protobuf = { path = "crates/papyrus_protobuf", version = "0.0.0" } diff --git a/Dockerfile b/Dockerfile index a4f00d11d94..89652a45f50 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,26 +7,36 @@ # More info on Cargo Chef: https://github.com/LukeMathWalker/cargo-chef # We start by creating a base image using 'clux/muslrust' with additional required tools. -FROM clux/muslrust:1.80.0-stable AS chef +FROM ubuntu:22.04 AS base WORKDIR /app -RUN apt update && apt install -y clang unzip + +COPY scripts/install_build_tools.sh . +COPY scripts/dependencies.sh . +RUN apt update && apt -y install curl bzip2 + + +ENV RUSTUP_HOME=/var/tmp/rust +ENV CARGO_HOME=${RUSTUP_HOME} +ENV PATH=$PATH:${RUSTUP_HOME}/bin + +RUN ./install_build_tools.sh + RUN cargo install cargo-chef -ENV PROTOC_VERSION=25.1 -RUN curl -L "https://github.com/protocolbuffers/protobuf/releases/download/v$PROTOC_VERSION/protoc-$PROTOC_VERSION-linux-x86_64.zip" -o protoc.zip && unzip ./protoc.zip -d $HOME/.local && rm ./protoc.zip -ENV PROTOC=/root/.local/bin/protoc +RUN apt update && apt -y install unzip + -# Reinstalling the stable Rust toolchain to ensure a clean environment -RUN rustup toolchain uninstall stable-x86_64-unknown-linux-gnu && rustup toolchain install stable-x86_64-unknown-linux-gnu +# # Reinstalling the stable Rust toolchain to ensure a clean environment +# RUN rustup toolchain uninstall stable-x86_64-unknown-linux-gnu && rustup toolchain install stable-x86_64-unknown-linux-gnu -# Add the x86_64-unknown-linux-musl target to rustup for compiling statically linked binaries. -# This enables the creation of fully self-contained binaries that do not depend on the system's dynamic libraries, -# resulting in more portable executables that can run on any Linux distribution. -RUN rustup target add x86_64-unknown-linux-musl +# # Add the x86_64-unknown-linux-musl target to rustup for compiling statically linked binaries. +# # This enables the creation of fully self-contained binaries that do not depend on the system's dynamic libraries, +# # resulting in more portable executables that can run on any Linux distribution. +# RUN rustup target add x86_64-unknown-linux-musl ##################### # Stage 1 (planer): # ##################### -FROM chef AS planner +FROM base AS planner COPY . . # * Running Cargo Chef prepare that will generate recipe.json which will be used in the next stage. RUN cargo chef prepare @@ -35,43 +45,39 @@ RUN cargo chef prepare # Stage 2 (cacher): # ##################### # Compile all the dependecies using Cargo Chef cook. -FROM chef AS cacher +FROM base AS cacher # Copy recipe.json from planner stage COPY --from=planner /app/recipe.json recipe.json # Build dependencies - this is the caching Docker layer! -RUN cargo chef cook --target x86_64-unknown-linux-musl --release --package papyrus_node +RUN cargo chef cook --release --package papyrus_node ###################### # Stage 3 (builder): # ###################### -FROM chef AS builder +FROM base AS builder COPY . . COPY --from=cacher /app/target target # Disable incremental compilation for a cleaner build. ENV CARGO_INCREMENTAL=0 # Compile the papyrus_node crate for the x86_64-unknown-linux-musl target in release mode, ensuring dependencies are locked. -RUN cargo build --target x86_64-unknown-linux-musl --release --package papyrus_node --locked +RUN cargo build --release --package papyrus_node --locked ########################### # Stage 4 (papyrus_node): # ########################### -# Uses Alpine Linux to run a lightweight and secure container. -FROM alpine:3.17.0 AS papyrus_node +FROM base AS papyrus_node ENV ID=1000 WORKDIR /app # Copy the node executable and its configuration. -COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/papyrus_node /app/target/release/papyrus_node +COPY --from=builder /app/target/release/papyrus_node /app/target/release/papyrus_node COPY config config # Install tini, a lightweight init system, to call our executable. -RUN set -ex; \ - apk update; \ - apk add --no-cache tini; \ - mkdir data +RUN apt install tini # Create a new user "papyrus". RUN set -ex; \ @@ -86,4 +92,4 @@ EXPOSE 8080 8081 USER ${ID} # Set the entrypoint to use tini to manage the process. -ENTRYPOINT ["/sbin/tini", "--", "/app/target/release/papyrus_node"] +ENTRYPOINT ["tini", "--", "/app/target/release/papyrus_node"] diff --git a/README.md b/README.md index 4a18d3e7a31..1aede288c44 100644 --- a/README.md +++ b/README.md @@ -1 +1,7 @@ # Sequencer + +Sequencer for Starknet, currently in development. + +## Development + +Run [the dependencies script](scripts/dependencies.sh) to setup your environment. diff --git a/commitlint.config.js b/commitlint.config.js index 5911bc7aa52..7d36875ed47 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -38,9 +38,11 @@ const Configuration = { 'JSON-RPC', 'l1-provider', 'load_test', + 'mempool', 'mempool_infra', 'mempool_node', - 'mempool', + 'mempool_test_utils', + 'mempool_types', 'monitoring', 'native_blockifier', 'network', diff --git a/config/mempool/default_config.json b/config/mempool/default_config.json index 523067652ec..e97eeb25c25 100644 --- a/config/mempool/default_config.json +++ b/config/mempool/default_config.json @@ -59,16 +59,16 @@ "privacy": "Public", "value": 81920 }, - "components.batcher.component_type": { - "description": "The component type.", - "privacy": "Public", - "value": "Reactive" - }, "components.batcher.execute": { "description": "The component execution flag.", "privacy": "Public", "value": true }, + "components.batcher.execution_mode": { + "description": "The component execution mode.", + "privacy": "Public", + "value": "Local" + }, "components.batcher.local_config.#is_none": { "description": "Flag for an optional field.", "privacy": "TemporaryValue", @@ -79,41 +79,41 @@ "privacy": "Public", "value": 32 }, - "components.batcher.location": { - "description": "The component location.", - "privacy": "Public", - "value": "Local" - }, "components.batcher.remote_config.#is_none": { "description": "Flag for an optional field.", "privacy": "TemporaryValue", "value": true }, - "components.batcher.remote_config.ip": { - "description": "The remote component server ip.", + "components.batcher.remote_config.idle_connections": { + "description": "The maximum number of idle connections to keep alive.", "privacy": "Public", - "value": "0.0.0.0" + "value": 18446744073709551615 }, - "components.batcher.remote_config.port": { - "description": "The remote component server port.", + "components.batcher.remote_config.idle_timeout": { + "description": "The duration in seconds to keep an idle connection open before closing.", "privacy": "Public", - "value": 8080 + "value": 90 }, "components.batcher.remote_config.retries": { "description": "The max number of retries for sending a message.", "privacy": "Public", "value": 3 }, - "components.consensus_manager.component_type": { - "description": "The component type.", + "components.batcher.remote_config.socket": { + "description": "The remote component server socket.", "privacy": "Public", - "value": "Reactive" + "value": "0.0.0.0:8080" }, "components.consensus_manager.execute": { "description": "The component execution flag.", "privacy": "Public", "value": true }, + "components.consensus_manager.execution_mode": { + "description": "The component execution mode.", + "privacy": "Public", + "value": "Local" + }, "components.consensus_manager.local_config.#is_none": { "description": "Flag for an optional field.", "privacy": "TemporaryValue", @@ -124,41 +124,41 @@ "privacy": "Public", "value": 32 }, - "components.consensus_manager.location": { - "description": "The component location.", - "privacy": "Public", - "value": "Local" - }, "components.consensus_manager.remote_config.#is_none": { "description": "Flag for an optional field.", "privacy": "TemporaryValue", "value": true }, - "components.consensus_manager.remote_config.ip": { - "description": "The remote component server ip.", + "components.consensus_manager.remote_config.idle_connections": { + "description": "The maximum number of idle connections to keep alive.", "privacy": "Public", - "value": "0.0.0.0" + "value": 18446744073709551615 }, - "components.consensus_manager.remote_config.port": { - "description": "The remote component server port.", + "components.consensus_manager.remote_config.idle_timeout": { + "description": "The duration in seconds to keep an idle connection open before closing.", "privacy": "Public", - "value": 8080 + "value": 90 }, "components.consensus_manager.remote_config.retries": { "description": "The max number of retries for sending a message.", "privacy": "Public", "value": 3 }, - "components.gateway.component_type": { - "description": "The component type.", + "components.consensus_manager.remote_config.socket": { + "description": "The remote component server socket.", "privacy": "Public", - "value": "Autonomous" + "value": "0.0.0.0:8080" }, "components.gateway.execute": { "description": "The component execution flag.", "privacy": "Public", "value": true }, + "components.gateway.execution_mode": { + "description": "The component execution mode.", + "privacy": "Public", + "value": "Local" + }, "components.gateway.local_config.#is_none": { "description": "Flag for an optional field.", "privacy": "TemporaryValue", @@ -169,41 +169,86 @@ "privacy": "Public", "value": 32 }, - "components.gateway.location": { - "description": "The component location.", - "privacy": "Public", - "value": "Local" - }, "components.gateway.remote_config.#is_none": { "description": "Flag for an optional field.", "privacy": "TemporaryValue", "value": true }, - "components.gateway.remote_config.ip": { - "description": "The remote component server ip.", + "components.gateway.remote_config.idle_connections": { + "description": "The maximum number of idle connections to keep alive.", "privacy": "Public", - "value": "0.0.0.0" + "value": 18446744073709551615 }, - "components.gateway.remote_config.port": { - "description": "The remote component server port.", + "components.gateway.remote_config.idle_timeout": { + "description": "The duration in seconds to keep an idle connection open before closing.", "privacy": "Public", - "value": 8080 + "value": 90 }, "components.gateway.remote_config.retries": { "description": "The max number of retries for sending a message.", "privacy": "Public", "value": 3 }, - "components.mempool.component_type": { - "description": "The component type.", + "components.gateway.remote_config.socket": { + "description": "The remote component server socket.", + "privacy": "Public", + "value": "0.0.0.0:8080" + }, + "components.http_server.execute": { + "description": "The component execution flag.", + "privacy": "Public", + "value": true + }, + "components.http_server.execution_mode": { + "description": "The component execution mode.", "privacy": "Public", - "value": "Reactive" + "value": "Remote" + }, + "components.http_server.local_config.#is_none": { + "description": "Flag for an optional field.", + "privacy": "TemporaryValue", + "value": true + }, + "components.http_server.local_config.channel_buffer_size": { + "description": "The communication channel buffer size.", + "privacy": "Public", + "value": 32 + }, + "components.http_server.remote_config.#is_none": { + "description": "Flag for an optional field.", + "privacy": "TemporaryValue", + "value": false + }, + "components.http_server.remote_config.idle_connections": { + "description": "The maximum number of idle connections to keep alive.", + "privacy": "Public", + "value": 18446744073709551615 + }, + "components.http_server.remote_config.idle_timeout": { + "description": "The duration in seconds to keep an idle connection open before closing.", + "privacy": "Public", + "value": 90 + }, + "components.http_server.remote_config.retries": { + "description": "The max number of retries for sending a message.", + "privacy": "Public", + "value": 3 + }, + "components.http_server.remote_config.socket": { + "description": "The remote component server socket.", + "privacy": "Public", + "value": "0.0.0.0:8080" }, "components.mempool.execute": { "description": "The component execution flag.", "privacy": "Public", "value": true }, + "components.mempool.execution_mode": { + "description": "The component execution mode.", + "privacy": "Public", + "value": "Local" + }, "components.mempool.local_config.#is_none": { "description": "Flag for an optional field.", "privacy": "TemporaryValue", @@ -214,31 +259,31 @@ "privacy": "Public", "value": 32 }, - "components.mempool.location": { - "description": "The component location.", - "privacy": "Public", - "value": "Local" - }, "components.mempool.remote_config.#is_none": { "description": "Flag for an optional field.", "privacy": "TemporaryValue", "value": true }, - "components.mempool.remote_config.ip": { - "description": "The remote component server ip.", + "components.mempool.remote_config.idle_connections": { + "description": "The maximum number of idle connections to keep alive.", "privacy": "Public", - "value": "0.0.0.0" + "value": 18446744073709551615 }, - "components.mempool.remote_config.port": { - "description": "The remote component server port.", + "components.mempool.remote_config.idle_timeout": { + "description": "The duration in seconds to keep an idle connection open before closing.", "privacy": "Public", - "value": 8080 + "value": 90 }, "components.mempool.remote_config.retries": { "description": "The max number of retries for sending a message.", "privacy": "Public", "value": 3 }, + "components.mempool.remote_config.socket": { + "description": "The remote component server socket.", + "privacy": "Public", + "value": "0.0.0.0:8080" + }, "consensus_manager_config.consensus_config_param_1": { "description": "The first consensus manager configuration parameter", "privacy": "Public", diff --git a/crates/batcher/src/batcher.rs b/crates/batcher/src/batcher.rs index b25abd74582..3abafcf4ea1 100644 --- a/crates/batcher/src/batcher.rs +++ b/crates/batcher/src/batcher.rs @@ -1,9 +1,8 @@ use std::sync::Arc; -use async_trait::async_trait; #[cfg(test)] use mockall::automock; -use starknet_mempool_infra::component_runner::ComponentStarter; +use starknet_mempool_infra::component_definitions::ComponentStarter; use starknet_mempool_types::communication::SharedMempoolClient; use crate::config::BatcherConfig; @@ -31,10 +30,8 @@ pub fn create_batcher(config: BatcherConfig, mempool_client: SharedMempoolClient Batcher::new(config, mempool_client, Arc::new(storage_reader)) } -#[async_trait] -impl ComponentStarter for Batcher {} - #[cfg_attr(test, automock)] pub trait BatcherStorageReaderTrait: Send + Sync {} impl BatcherStorageReaderTrait for papyrus_storage::StorageReader {} +impl ComponentStarter for Batcher {} diff --git a/crates/batcher_types/src/batcher_types.rs b/crates/batcher_types/src/batcher_types.rs index 0204c463f0f..aed082ed3ae 100644 --- a/crates/batcher_types/src/batcher_types.rs +++ b/crates/batcher_types/src/batcher_types.rs @@ -9,7 +9,7 @@ pub use starknet_consensus_manager_types::consensus_manager_types::ProposalId; use crate::errors::BatcherError; #[derive(Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)] -pub struct ProposalContentId { +pub struct ProposalCommitment { pub tx_commitment: TransactionCommitment, pub state_diff: ThinStateDiff, } @@ -36,7 +36,7 @@ pub struct GetProposalContentResponse { #[derive(Clone, Debug, Serialize, Deserialize)] pub enum GetProposalContent { Txs(Vec), - Finished(ProposalContentId), + Finished(ProposalCommitment), } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -68,7 +68,7 @@ pub struct SendProposalContentResponse { pub enum ProposalStatus { Processing, // Only sent in response to `Finish`. - Finished(ProposalContentId), + Finished(ProposalCommitment), // May be caused due to handling of a previous item of the new proposal. // In this case, the propsal is aborted and no additional content will be processed. InvalidProposal, diff --git a/crates/blockifier/Cargo.toml b/crates/blockifier/Cargo.toml index b5a2e89f7a6..b739798d7db 100644 --- a/crates/blockifier/Cargo.toml +++ b/crates/blockifier/Cargo.toml @@ -28,6 +28,7 @@ cairo-lang-casm = { workspace = true, features = ["parity-scale-codec"] } cairo-lang-runner.workspace = true cairo-lang-starknet-classes.workspace = true cairo-lang-utils.workspace = true +cairo-native.workspace = true cairo-vm.workspace = true derive_more.workspace = true indexmap.workspace = true diff --git a/crates/blockifier/feature_contracts/cairo1/compiled/test_contract.casm.json b/crates/blockifier/feature_contracts/cairo1/compiled/test_contract.casm.json index ae5ce4d90fb..e83fe5605aa 100644 --- a/crates/blockifier/feature_contracts/cairo1/compiled/test_contract.casm.json +++ b/crates/blockifier/feature_contracts/cairo1/compiled/test_contract.casm.json @@ -100,9 +100,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x4795", + "0x4a6e", "0x482480017fff8000", - "0x4794", + "0x4a6d", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -275,9 +275,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x46e6", + "0x49bf", "0x482480017fff8000", - "0x46e5", + "0x49be", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -536,7 +536,7 @@ "0x48127ffa7fff8000", "0x480080007ff88000", "0x1104800180018000", - "0x1643", + "0x1810", "0x20680017fff7ffa", "0xb", "0x48127ff87fff8000", @@ -588,9 +588,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x45ad", + "0x4886", "0x482480017fff8000", - "0x45ac", + "0x4885", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -715,6 +715,467 @@ "0x1", "0x208b7fff7fff7ffe", "0x40780017fff7fff", + "0x2", + "0xa0680017fff8000", + "0x7", + "0x482680017ffa8000", + "0x100000000000000000000000000000000", + "0x400280007ff97fff", + "0x10780017fff7fff", + "0x10c", + "0x4825800180007ffa", + "0x0", + "0x400280007ff97fff", + "0x482680017ff98000", + "0x1", + "0x48297ffc80007ffd", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0xa", + "0x482680017ffc8000", + "0x1", + "0x480a7ffd7fff8000", + "0x480680017fff8000", + "0x0", + "0x480280007ffc8000", + "0x10780017fff7fff", + "0x8", + "0x480a7ffc7fff8000", + "0x480a7ffd7fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffe", + "0xe1", + "0x40137fff7fff8001", + "0xa0680017fff8004", + "0xe", + "0x4825800180048001", + "0x800000000000000000000000000000000000000000000000000000000000000", + "0x484480017ffe8000", + "0x110000000000000000", + "0x48307ffe7fff8002", + "0x480080007ff67ffc", + "0x480080017ff57ffc", + "0x402480017ffb7ffd", + "0xffffffffffffffeeffffffffffffffff", + "0x400080027ff47ffd", + "0x10780017fff7fff", + "0xce", + "0x484480017fff8001", + "0x8000000000000000000000000000000", + "0x48317fff80008001", + "0x480080007ff77ffd", + "0x480080017ff67ffd", + "0x402480017ffc7ffe", + "0xf8000000000000000000000000000000", + "0x400080027ff57ffe", + "0x482480017ff58000", + "0x3", + "0x48307ff680007ff7", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0xa", + "0x482480017ff58000", + "0x1", + "0x48127ff57fff8000", + "0x480680017fff8000", + "0x0", + "0x480080007ff28000", + "0x10780017fff7fff", + "0x8", + "0x48127ff57fff8000", + "0x48127ff57fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffe", + "0xa1", + "0x40137fff7fff8000", + "0x48307ffc80007ffd", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0xa", + "0x482480017ffb8000", + "0x1", + "0x48127ffb7fff8000", + "0x480680017fff8000", + "0x0", + "0x48127ff87fff8000", + "0x10780017fff7fff", + "0x8", + "0x48127ffb7fff8000", + "0x48127ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffe", + "0x20", + "0x40780017fff7fff", + "0x1", + "0x48127ff47fff8000", + "0x48127fe77fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x48127ffb7fff8000", + "0x48127ffa7fff8000", + "0x480080007ff88000", + "0x1104800180018000", + "0x16ed", + "0x20680017fff7ffa", + "0xb", + "0x48127ff87fff8000", + "0x48127ff87fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x10780017fff7fff", + "0x14", + "0x48127ff87fff8000", + "0x48127ff87fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x208b7fff7fff7ffe", + "0x48127ff57fff8000", + "0x48127fe87fff8000", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffd", + "0x53", + "0x48307ffb80007ffc", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0x10", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473", + "0x400080007ffe7fff", + "0x48127ff67fff8000", + "0x48127ff67fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x1104800180018000", + "0x4763", + "0x482480017fff8000", + "0x4762", + "0x480080007fff8000", + "0xa0680017fff8000", + "0x9", + "0x4824800180007ff4", + "0x602c", + "0x482480017fff8000", + "0x100000000000000000000000000000000", + "0x400080007ff17fff", + "0x10780017fff7fff", + "0x23", + "0x4824800180007ff4", + "0x602c", + "0x400080007ff27fff", + "0x482480017ff28000", + "0x1", + "0x48127ffe7fff8000", + "0x480a7ffb7fff8000", + "0x480a80017fff8000", + "0x480a80007fff8000", + "0x48127ff27fff8000", + "0x48127ff27fff8000", + "0x1104800180018000", + "0x16fc", + "0x20680017fff7ffd", + "0xc", + "0x40780017fff7fff", + "0x1", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x480680017fff8000", + "0x0", + "0x48127ffb7fff8000", + "0x48127ffa7fff8000", + "0x208b7fff7fff7ffe", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4f7574206f6620676173", + "0x400080007ffe7fff", + "0x482480017fef8000", + "0x1", + "0x48127fef7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4661696c656420746f20646573657269616c697a6520706172616d202333", + "0x400080007ffe7fff", + "0x48127ff77fff8000", + "0x48127ff77fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4661696c656420746f20646573657269616c697a6520706172616d202332", + "0x400080007ffe7fff", + "0x48127ff87fff8000", + "0x48127feb7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x482480017ff48000", + "0x3", + "0x10780017fff7fff", + "0x5", + "0x40780017fff7fff", + "0x6", + "0x48127ff47fff8000", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4661696c656420746f20646573657269616c697a6520706172616d202331", + "0x400080007ffe7fff", + "0x48127ffd7fff8000", + "0x48127fef7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4f7574206f6620676173", + "0x400080007ffe7fff", + "0x482680017ff98000", + "0x1", + "0x480a7ffa7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0xa0680017fff8000", + "0x7", + "0x482680017ffa8000", + "0x100000000000000000000000000000000", + "0x400280007ff97fff", + "0x10780017fff7fff", + "0x97", + "0x4825800180007ffa", + "0x0", + "0x400280007ff97fff", + "0x482680017ff98000", + "0x1", + "0x48297ffc80007ffd", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0xa", + "0x482680017ffc8000", + "0x1", + "0x480a7ffd7fff8000", + "0x480680017fff8000", + "0x0", + "0x480280007ffc8000", + "0x10780017fff7fff", + "0x8", + "0x480a7ffc7fff8000", + "0x480a7ffd7fff8000", + "0x480680017fff8000", + "0x1", + "0x480680017fff8000", + "0x0", + "0x20680017fff7ffe", + "0x6c", + "0xa0680017fff8004", + "0xe", + "0x4824800180047ffe", + "0x800000000000000000000000000000000000000000000000000000000000000", + "0x484480017ffe8000", + "0x110000000000000000", + "0x48307ffe7fff8002", + "0x480080007ff67ffc", + "0x480080017ff57ffc", + "0x402480017ffb7ffd", + "0xffffffffffffffeeffffffffffffffff", + "0x400080027ff47ffd", + "0x10780017fff7fff", + "0x5a", + "0x484480017fff8001", + "0x8000000000000000000000000000000", + "0x48307fff80007ffd", + "0x480080007ff77ffd", + "0x480080017ff67ffd", + "0x402480017ffc7ffe", + "0xf8000000000000000000000000000000", + "0x400080027ff57ffe", + "0x482480017ff58000", + "0x3", + "0x48307ff680007ff7", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0x10", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x496e70757420746f6f206c6f6e6720666f7220617267756d656e7473", + "0x400080007ffe7fff", + "0x48127ffc7fff8000", + "0x48127fef7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x1104800180018000", + "0x4699", + "0x482480017fff8000", + "0x4698", + "0x480080007fff8000", + "0xa0680017fff8000", + "0x9", + "0x4824800180007fed", + "0x9e66", + "0x482480017fff8000", + "0x100000000000000000000000000000000", + "0x400080007ff77fff", + "0x10780017fff7fff", + "0x20", + "0x4824800180007fed", + "0x9e66", + "0x400080007ff87fff", + "0x482480017ff88000", + "0x1", + "0x48127ffe7fff8000", + "0x480a7ffb7fff8000", + "0x48127fef7fff8000", + "0x1104800180018000", + "0x16c3", + "0x20680017fff7ffd", + "0xc", + "0x40780017fff7fff", + "0x1", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x48127ff97fff8000", + "0x480680017fff8000", + "0x0", + "0x48127ffb7fff8000", + "0x48127ffa7fff8000", + "0x208b7fff7fff7ffe", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x48127ffa7fff8000", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4f7574206f6620676173", + "0x400080007ffe7fff", + "0x482480017ff58000", + "0x1", + "0x48127fe87fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x482480017ff48000", + "0x3", + "0x10780017fff7fff", + "0x5", + "0x40780017fff7fff", + "0x6", + "0x48127ff47fff8000", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4661696c656420746f20646573657269616c697a6520706172616d202331", + "0x400080007ffe7fff", + "0x48127ffd7fff8000", + "0x48127fef7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4f7574206f6620676173", + "0x400080007ffe7fff", + "0x482680017ff98000", + "0x1", + "0x480a7ffa7fff8000", + "0x480a7ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", "0x3", "0xa0680017fff8000", "0x7", @@ -922,9 +1383,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x445f", + "0x456b", "0x482480017fff8000", - "0x445e", + "0x456a", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -950,7 +1411,7 @@ "0x480a80017fff8000", "0x480a80027fff8000", "0x1104800180018000", - "0x1501", + "0x160d", "0x20680017fff7ffd", "0xc", "0x40780017fff7fff", @@ -1128,9 +1589,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x4391", + "0x449d", "0x482480017fff8000", - "0x4390", + "0x449c", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -1245,7 +1706,7 @@ "0x480a7ffc7fff8000", "0x480a7ffd7fff8000", "0x1104800180018000", - "0x1444", + "0x1550", "0x20680017fff7ffc", "0xf2", "0x48127ff97fff8000", @@ -1256,7 +1717,7 @@ "0x40137ffa7fff8001", "0x40137ffb7fff8002", "0x1104800180018000", - "0x1506", + "0x1612", "0x20680017fff7feb", "0xdf", "0x20680017fff7fee", @@ -1344,9 +1805,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x42b9", + "0x43c5", "0x482480017fff8000", - "0x42b8", + "0x43c4", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -1388,7 +1849,7 @@ "0x48127fdb7fff8000", "0x48127fdf7fff8000", "0x1104800180018000", - "0x1937", + "0x1a43", "0x20680017fff7ffd", "0xc", "0x40780017fff7fff", @@ -1682,9 +2143,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x4167", + "0x4273", "0x482480017fff8000", - "0x4166", + "0x4272", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -1969,9 +2430,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x4048", + "0x4154", "0x482480017fff8000", - "0x4047", + "0x4153", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -1993,7 +2454,7 @@ "0x48127fee7fff8000", "0x48127ff27fff8000", "0x1104800180018000", - "0x184a", + "0x1956", "0x482480017fc88000", "0x1", "0x48127ffa7fff8000", @@ -2186,9 +2647,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3f6f", + "0x407b", "0x482480017fff8000", - "0x3f6e", + "0x407a", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -2402,9 +2863,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3e97", + "0x3fa3", "0x482480017fff8000", - "0x3e96", + "0x3fa2", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -2541,9 +3002,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3e0c", + "0x3f18", "0x482480017fff8000", - "0x3e0b", + "0x3f17", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -2575,7 +3036,7 @@ "0x48127ff77fff8000", "0x480080007ffc8000", "0x1104800180018000", - "0x164c", + "0x1758", "0x40780017fff7fff", "0x1", "0x48127ffa7fff8000", @@ -2814,9 +3275,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3cfb", + "0x3e07", "0x482480017fff8000", - "0x3cfa", + "0x3e06", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -2992,9 +3453,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3c49", + "0x3d55", "0x482480017fff8000", - "0x3c48", + "0x3d54", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -3013,7 +3474,7 @@ "0x48127ffe7fff8000", "0x480a7ffb7fff8000", "0x1104800180018000", - "0x1554", + "0x1660", "0x20680017fff7ffd", "0xc", "0x40780017fff7fff", @@ -3096,9 +3557,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3be1", + "0x3ced", "0x482480017fff8000", - "0x3be0", + "0x3cec", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -3117,7 +3578,7 @@ "0x48127ffe7fff8000", "0x480a7ffb7fff8000", "0x1104800180018000", - "0x15c0", + "0x16cc", "0x20680017fff7ffd", "0xc", "0x40780017fff7fff", @@ -3201,9 +3662,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3b78", + "0x3c84", "0x482480017fff8000", - "0x3b77", + "0x3c83", "0x480080007fff8000", "0x480080017fff8000", "0x484480017fff8000", @@ -3226,7 +3687,7 @@ "0x480a7ff97fff8000", "0x480a7ffb7fff8000", "0x1104800180018000", - "0x15a6", + "0x16b2", "0x20680017fff7ffd", "0xd", "0x40780017fff7fff", @@ -3313,9 +3774,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3b08", + "0x3c14", "0x482480017fff8000", - "0x3b07", + "0x3c13", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -3334,7 +3795,7 @@ "0x48127ffe7fff8000", "0x480a7ffb7fff8000", "0x1104800180018000", - "0x169a", + "0x17a6", "0x20680017fff7ffd", "0xc", "0x40780017fff7fff", @@ -3459,9 +3920,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3a76", + "0x3b82", "0x482480017fff8000", - "0x3a75", + "0x3b81", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -3661,9 +4122,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x39ac", + "0x3ab8", "0x482480017fff8000", - "0x39ab", + "0x3ab7", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -3684,7 +4145,7 @@ "0x48127ff47fff8000", "0x48127ff47fff8000", "0x1104800180018000", - "0x16c5", + "0x17d1", "0x20680017fff7ffd", "0xe", "0x40780017fff7fff", @@ -3783,9 +4244,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3932", + "0x3a3e", "0x482480017fff8000", - "0x3931", + "0x3a3d", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -3897,9 +4358,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x38c0", + "0x39cc", "0x482480017fff8000", - "0x38bf", + "0x39cb", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -3918,7 +4379,7 @@ "0x48127ffe7fff8000", "0x48127ff67fff8000", "0x1104800180018000", - "0x172a", + "0x1836", "0x20680017fff7ffd", "0xc", "0x40780017fff7fff", @@ -4036,9 +4497,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3835", + "0x3941", "0x482480017fff8000", - "0x3834", + "0x3940", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -4057,7 +4518,7 @@ "0x48127ffe7fff8000", "0x48127ff67fff8000", "0x1104800180018000", - "0x16cf", + "0x17db", "0x20680017fff7ffd", "0xc", "0x40780017fff7fff", @@ -4241,9 +4702,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3768", + "0x3874", "0x482480017fff8000", - "0x3767", + "0x3873", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -4485,9 +4946,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3674", + "0x3780", "0x482480017fff8000", - "0x3673", + "0x377f", "0x480080007fff8000", "0x480080007fff8000", "0x484480017fff8000", @@ -4513,7 +4974,7 @@ "0x48127feb7fff8000", "0x48127fef7fff8000", "0x1104800180018000", - "0x1533", + "0x163f", "0x20680017fff7ffd", "0xd", "0x40780017fff7fff", @@ -4628,7 +5089,7 @@ "0x480a7ffc7fff8000", "0x480a7ffd7fff8000", "0x1104800180018000", - "0x1583", + "0x168f", "0x20680017fff7ffc", "0x63", "0x48307ffa80007ffb", @@ -4653,9 +5114,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x35cc", + "0x36d8", "0x482480017fff8000", - "0x35cb", + "0x36d7", "0x480080007fff8000", "0x480080007fff8000", "0x484480017fff8000", @@ -4686,7 +5147,7 @@ "0x48127feb7fff8000", "0x48127feb7fff8000", "0x1104800180018000", - "0x160d", + "0x1719", "0x20680017fff7ffd", "0xe", "0x40780017fff7fff", @@ -4821,7 +5282,7 @@ "0x48127ff67fff8000", "0x48127ff67fff8000", "0x1104800180018000", - "0x14c2", + "0x15ce", "0x20680017fff7ffc", "0x60", "0x48307ffa80007ffb", @@ -4844,9 +5305,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x350d", + "0x3619", "0x482480017fff8000", - "0x350c", + "0x3618", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -5001,9 +5462,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3470", + "0x357c", "0x482480017fff8000", - "0x346f", + "0x357b", "0x480080007fff8000", "0x480080027fff8000", "0x482480017fff8000", @@ -5024,7 +5485,7 @@ "0x48127ffd7fff8000", "0x480a7ffb7fff8000", "0x1104800180018000", - "0x15dd", + "0x16e9", "0x20680017fff7ffd", "0xd", "0x40780017fff7fff", @@ -5133,9 +5594,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x33ec", + "0x34f8", "0x482480017fff8000", - "0x33eb", + "0x34f7", "0x480080007fff8000", "0x480080007fff8000", "0x484480017fff8000", @@ -5159,7 +5620,7 @@ "0x480a7ffb7fff8000", "0x48127ff17fff8000", "0x1104800180018000", - "0x1678", + "0x1784", "0x20680017fff7ffd", "0xd", "0x40780017fff7fff", @@ -5282,9 +5743,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3357", + "0x3463", "0x482480017fff8000", - "0x3356", + "0x3462", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -5420,9 +5881,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x32cd", + "0x33d9", "0x482480017fff8000", - "0x32cc", + "0x33d8", "0x480080007fff8000", "0x480080047fff8000", "0x484480017fff8000", @@ -5450,7 +5911,7 @@ "0x480a7ff77fff8000", "0x48127ffb7fff8000", "0x1104800180018000", - "0x1683", + "0x178f", "0x20680017fff7ffd", "0xf", "0x40780017fff7fff", @@ -5546,9 +6007,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x324f", + "0x335b", "0x482480017fff8000", - "0x324e", + "0x335a", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -5564,14 +6025,14 @@ "0x400080007ff87fff", "0x480a7ff97fff8000", "0x1104800180018000", - "0x17e3", + "0x18ef", "0x482480017fe88000", "0x1", "0x20680017fff7ffc", "0x17", "0x48127ffb7fff8000", "0x1104800180018000", - "0x17dc", + "0x18e8", "0x20680017fff7ffd", "0xd", "0x40780017fff7fff", @@ -5710,9 +6171,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x31ab", + "0x32b7", "0x482480017fff8000", - "0x31aa", + "0x32b6", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -5916,9 +6377,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x30dd", + "0x31e9", "0x482480017fff8000", - "0x30dc", + "0x31e8", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -6121,9 +6582,9 @@ "0x1", "0x208b7fff7fff7ffe", "0x1104800180018000", - "0x3010", + "0x311c", "0x482480017fff8000", - "0x300f", + "0x311b", "0x480080007fff8000", "0xa0680017fff8000", "0x9", @@ -6326,6 +6787,274 @@ "0x482480017ff78000", "0x1", "0x208b7fff7fff7ffe", + "0x480680017fff8000", + "0x43616c6c436f6e7472616374", + "0x400280007ff97fff", + "0x400380017ff97ff8", + "0x400380027ff97ffa", + "0x400380037ff97ffb", + "0x400380047ff97ffc", + "0x400380057ff97ffd", + "0x480280077ff98000", + "0x20680017fff7fff", + "0x1c", + "0x40780017fff7fff", + "0xc", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x46a6158a16a947e5916b2a2ca68501a45e93d7110e81aa2d6438b1c57c879a3", + "0x400080007ffe7fff", + "0x480680017fff8000", + "0x0", + "0x400080017ffd7fff", + "0x480680017fff8000", + "0x457870656374656420726576657274", + "0x400080027ffc7fff", + "0x480680017fff8000", + "0xf", + "0x400080037ffb7fff", + "0x480a7ff77fff8000", + "0x480280067ff98000", + "0x482680017ff98000", + "0xa", + "0x480680017fff8000", + "0x1", + "0x48127ff77fff8000", + "0x482480017ff68000", + "0x4", + "0x208b7fff7fff7ffe", + "0x480280087ff98000", + "0x480280097ff98000", + "0x480280067ff98000", + "0x482680017ff98000", + "0xa", + "0x48307ffc80007ffd", + "0x20680017fff7fff", + "0x4", + "0x10780017fff7fff", + "0x51", + "0x4824800180007ffc", + "0x1", + "0x480080007fff8000", + "0x4824800180007fff", + "0x454e545259504f494e545f4641494c4544", + "0x20680017fff7fff", + "0x3a", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x1275130f95dda36bcbb6e9d28796c1d7e10b6e9fd5ed083e0ede4b12f613528", + "0x480680017fff8000", + "0x53746f7261676552656164", + "0x400080007ff87fff", + "0x400080017ff87ff7", + "0x400080027ff87ffd", + "0x400080037ff87ffe", + "0x480080057ff88000", + "0x20680017fff7fff", + "0x22", + "0x480080067ff78000", + "0x480080047ff68000", + "0x482480017ff58000", + "0x7", + "0x20680017fff7ffd", + "0xe", + "0x40780017fff7fff", + "0x2", + "0x480a7ff77fff8000", + "0x48127ffb7fff8000", + "0x48127ffb7fff8000", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x0", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x76616c7565732073686f756c64206e6f74206368616e67652e", + "0x400080007ffe7fff", + "0x480a7ff77fff8000", + "0x48127ffb7fff8000", + "0x48127ffb7fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x5", + "0x480a7ff77fff8000", + "0x480080047ff18000", + "0x482480017ff08000", + "0x8", + "0x480680017fff8000", + "0x1", + "0x480080067fee8000", + "0x480080077fed8000", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x7", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x556e6578706563746564206572726f72", + "0x400080007ffe7fff", + "0x480a7ff77fff8000", + "0x48127ff07fff8000", + "0x48127ff07fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0xa", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x4f7074696f6e3a3a756e77726170206661696c65642e", + "0x400080007ffe7fff", + "0x480a7ff77fff8000", + "0x48127ff07fff8000", + "0x48127ff07fff8000", + "0x480680017fff8000", + "0x1", + "0x48127ffa7fff8000", + "0x482480017ff98000", + "0x1", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x0", + "0x400080007ffe7fff", + "0x48127ffe7fff8000", + "0x482480017ffd8000", + "0x1", + "0x480680017fff8000", + "0x456d69744576656e74", + "0x400280007ffc7fff", + "0x400380017ffc7ffb", + "0x400280027ffc7ffd", + "0x400280037ffc7ffe", + "0x400280047ffc7ffd", + "0x400280057ffc7ffe", + "0x480280077ffc8000", + "0x20680017fff7fff", + "0x62", + "0x480280067ffc8000", + "0x480680017fff8000", + "0x5265706c616365436c617373", + "0x400280087ffc7fff", + "0x400280097ffc7ffe", + "0x4003800a7ffc7ffd", + "0x4802800c7ffc8000", + "0x20680017fff7fff", + "0x4e", + "0x4802800b7ffc8000", + "0x480680017fff8000", + "0x11", + "0x480680017fff8000", + "0x53656e644d657373616765546f4c31", + "0x4002800d7ffc7fff", + "0x4002800e7ffc7ffd", + "0x4002800f7ffc7ffe", + "0x400280107ffc7ff6", + "0x400280117ffc7ff7", + "0x480280137ffc8000", + "0x20680017fff7fff", + "0x36", + "0x480280127ffc8000", + "0x480680017fff8000", + "0x0", + "0x480680017fff8000", + "0x1275130f95dda36bcbb6e9d28796c1d7e10b6e9fd5ed083e0ede4b12f613528", + "0x480680017fff8000", + "0x11", + "0x480680017fff8000", + "0x53746f726167655772697465", + "0x400280147ffc7fff", + "0x400280157ffc7ffb", + "0x400280167ffc7ffc", + "0x400280177ffc7ffd", + "0x400280187ffc7ffe", + "0x4802801a7ffc8000", + "0x20680017fff7fff", + "0x1a", + "0x40780017fff7fff", + "0x1", + "0x480680017fff8000", + "0x46a6158a16a947e5916b2a2ca68501a45e93d7110e81aa2d6438b1c57c879a3", + "0x400080007ffe7fff", + "0x480680017fff8000", + "0x0", + "0x400080017ffd7fff", + "0x480680017fff8000", + "0x746573745f7265766572745f68656c706572", + "0x400080027ffc7fff", + "0x480680017fff8000", + "0x12", + "0x400080037ffb7fff", + "0x480a7ffa7fff8000", + "0x480280197ffc8000", + "0x482680017ffc8000", + "0x1b", + "0x480680017fff8000", + "0x1", + "0x48127ff77fff8000", + "0x482480017ff68000", + "0x4", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x5", + "0x480a7ffa7fff8000", + "0x480280197ffc8000", + "0x482680017ffc8000", + "0x1d", + "0x480680017fff8000", + "0x1", + "0x4802801b7ffc8000", + "0x4802801c7ffc8000", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0xb", + "0x480a7ffa7fff8000", + "0x480280127ffc8000", + "0x482680017ffc8000", + "0x16", + "0x480680017fff8000", + "0x1", + "0x480280147ffc8000", + "0x480280157ffc8000", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0xf", + "0x480a7ffa7fff8000", + "0x4802800b7ffc8000", + "0x482680017ffc8000", + "0xf", + "0x480680017fff8000", + "0x1", + "0x4802800d7ffc8000", + "0x4802800e7ffc8000", + "0x208b7fff7fff7ffe", + "0x40780017fff7fff", + "0x12", + "0x480a7ffa7fff8000", + "0x480280067ffc8000", + "0x482680017ffc8000", + "0xa", + "0x480680017fff8000", + "0x1", + "0x480280087ffc8000", + "0x480280097ffc8000", + "0x208b7fff7fff7ffe", "0xa0680017fff8000", "0x7", "0x482680017ff68000", @@ -18429,6 +19158,8 @@ 241, 180, 291, + 290, + 171, 336, 178, 289, @@ -18460,6 +19191,8 @@ 250, 187, 92, + 142, + 126, 106, 205, 1205, @@ -18825,7 +19558,655 @@ ] ], [ - 295, + 295, + [ + { + "LinearSplit": { + "value": { + "Deref": { + "register": "AP", + "offset": 3 + } + }, + "scalar": { + "Immediate": "0x110000000000000000" + }, + "max_x": { + "Immediate": "0xffffffffffffffffffffffffffffffff" + }, + "x": { + "register": "AP", + "offset": -2 + }, + "y": { + "register": "AP", + "offset": -1 + } + } + } + ] + ], + [ + 305, + [ + { + "LinearSplit": { + "value": { + "Deref": { + "register": "AP", + "offset": -2 + } + }, + "scalar": { + "Immediate": "0x8000000000000000000000000000000" + }, + "max_x": { + "Immediate": "0xffffffffffffffffffffffffffffffff" + }, + "x": { + "register": "AP", + "offset": -1 + }, + "y": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 326, + [ + { + "SystemCall": { + "system": { + "Deref": { + "register": "FP", + "offset": -5 + } + } + } + } + ] + ], + [ + 341, + [ + { + "SystemCall": { + "system": { + "BinOp": { + "op": "Add", + "a": { + "register": "FP", + "offset": -5 + }, + "b": { + "Immediate": "0x7" + } + } + } + } + } + ] + ], + [ + 344, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 371, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 391, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 406, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 423, + [ + { + "TestLessThanOrEqual": { + "lhs": { + "Immediate": "0x0" + }, + "rhs": { + "Deref": { + "register": "FP", + "offset": -6 + } + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 457, + [ + { + "TestLessThan": { + "lhs": { + "Deref": { + "register": "FP", + "offset": 1 + } + }, + "rhs": { + "Immediate": "0x800000000000000000000000000000000000000000000000000000000000000" + }, + "dst": { + "register": "AP", + "offset": 4 + } + } + } + ] + ], + [ + 461, + [ + { + "LinearSplit": { + "value": { + "Deref": { + "register": "AP", + "offset": 3 + } + }, + "scalar": { + "Immediate": "0x110000000000000000" + }, + "max_x": { + "Immediate": "0xffffffffffffffffffffffffffffffff" + }, + "x": { + "register": "AP", + "offset": -2 + }, + "y": { + "register": "AP", + "offset": -1 + } + } + } + ] + ], + [ + 471, + [ + { + "LinearSplit": { + "value": { + "Deref": { + "register": "FP", + "offset": 1 + } + }, + "scalar": { + "Immediate": "0x8000000000000000000000000000000" + }, + "max_x": { + "Immediate": "0xffffffffffffffffffffffffffffffff" + }, + "x": { + "register": "AP", + "offset": -1 + }, + "y": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 524, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 571, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 590, + [ + { + "TestLessThanOrEqual": { + "lhs": { + "Immediate": "0x28b4" + }, + "rhs": { + "Deref": { + "register": "AP", + "offset": -11 + } + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 612, + [ + { + "SystemCall": { + "system": { + "Deref": { + "register": "FP", + "offset": -5 + } + } + } + } + ] + ], + [ + 633, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 648, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 662, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 683, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 697, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 714, + [ + { + "TestLessThanOrEqual": { + "lhs": { + "Immediate": "0x0" + }, + "rhs": { + "Deref": { + "register": "FP", + "offset": -6 + } + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 748, + [ + { + "TestLessThan": { + "lhs": { + "Deref": { + "register": "FP", + "offset": 1 + } + }, + "rhs": { + "Immediate": "0x800000000000000000000000000000000000000000000000000000000000000" + }, + "dst": { + "register": "AP", + "offset": 4 + } + } + } + ] + ], + [ + 752, + [ + { + "LinearSplit": { + "value": { + "Deref": { + "register": "AP", + "offset": 3 + } + }, + "scalar": { + "Immediate": "0x110000000000000000" + }, + "max_x": { + "Immediate": "0xffffffffffffffffffffffffffffffff" + }, + "x": { + "register": "AP", + "offset": -2 + }, + "y": { + "register": "AP", + "offset": -1 + } + } + } + ] + ], + [ + 762, + [ + { + "LinearSplit": { + "value": { + "Deref": { + "register": "FP", + "offset": 1 + } + }, + "scalar": { + "Immediate": "0x8000000000000000000000000000000" + }, + "max_x": { + "Immediate": "0xffffffffffffffffffffffffffffffff" + }, + "x": { + "register": "AP", + "offset": -1 + }, + "y": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 815, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 862, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 881, + [ + { + "TestLessThanOrEqual": { + "lhs": { + "Immediate": "0x602c" + }, + "rhs": { + "Deref": { + "register": "AP", + "offset": -11 + } + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 905, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 923, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 938, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 952, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 973, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 987, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 1002, + [ + { + "TestLessThanOrEqual": { + "lhs": { + "Immediate": "0x0" + }, + "rhs": { + "Deref": { + "register": "FP", + "offset": -6 + } + }, + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 1035, + [ + { + "TestLessThan": { + "lhs": { + "Deref": { + "register": "AP", + "offset": -1 + } + }, + "rhs": { + "Immediate": "0x800000000000000000000000000000000000000000000000000000000000000" + }, + "dst": { + "register": "AP", + "offset": 4 + } + } + } + ] + ], + [ + 1039, [ { "LinearSplit": { @@ -18854,7 +20235,7 @@ ] ], [ - 305, + 1049, [ { "LinearSplit": { @@ -18883,56 +20264,7 @@ ] ], [ - 326, - [ - { - "SystemCall": { - "system": { - "Deref": { - "register": "FP", - "offset": -5 - } - } - } - } - ] - ], - [ - 341, - [ - { - "SystemCall": { - "system": { - "BinOp": { - "op": "Add", - "a": { - "register": "FP", - "offset": -5 - }, - "b": { - "Immediate": "0x7" - } - } - } - } - } - ] - ], - [ - 344, - [ - { - "AllocSegment": { - "dst": { - "register": "AP", - "offset": 0 - } - } - } - ] - ], - [ - 371, + 1064, [ { "AllocSegment": { @@ -18945,171 +20277,17 @@ ] ], [ - 391, - [ - { - "AllocSegment": { - "dst": { - "register": "AP", - "offset": 0 - } - } - } - ] - ], - [ - 406, - [ - { - "AllocSegment": { - "dst": { - "register": "AP", - "offset": 0 - } - } - } - ] - ], - [ - 423, + 1083, [ { "TestLessThanOrEqual": { "lhs": { - "Immediate": "0x0" - }, - "rhs": { - "Deref": { - "register": "FP", - "offset": -6 - } - }, - "dst": { - "register": "AP", - "offset": 0 - } - } - } - ] - ], - [ - 457, - [ - { - "TestLessThan": { - "lhs": { - "Deref": { - "register": "FP", - "offset": 1 - } + "Immediate": "0x9e66" }, "rhs": { - "Immediate": "0x800000000000000000000000000000000000000000000000000000000000000" - }, - "dst": { - "register": "AP", - "offset": 4 - } - } - } - ] - ], - [ - 461, - [ - { - "LinearSplit": { - "value": { "Deref": { "register": "AP", - "offset": 3 - } - }, - "scalar": { - "Immediate": "0x110000000000000000" - }, - "max_x": { - "Immediate": "0xffffffffffffffffffffffffffffffff" - }, - "x": { - "register": "AP", - "offset": -2 - }, - "y": { - "register": "AP", - "offset": -1 - } - } - } - ] - ], - [ - 471, - [ - { - "LinearSplit": { - "value": { - "Deref": { - "register": "FP", - "offset": 1 - } - }, - "scalar": { - "Immediate": "0x8000000000000000000000000000000" - }, - "max_x": { - "Immediate": "0xffffffffffffffffffffffffffffffff" - }, - "x": { - "register": "AP", - "offset": -1 - }, - "y": { - "register": "AP", - "offset": 0 - } - } - } - ] - ], - [ - 524, - [ - { - "AllocSegment": { - "dst": { - "register": "AP", - "offset": 0 - } - } - } - ] - ], - [ - 571, - [ - { - "AllocSegment": { - "dst": { - "register": "AP", - "offset": 0 - } - } - } - ] - ], - [ - 590, - [ - { - "TestLessThanOrEqual": { - "lhs": { - "Immediate": "0x28b4" - }, - "rhs": { - "Deref": { - "register": "AP", - "offset": -11 + "offset": -18 } }, "dst": { @@ -19121,35 +20299,7 @@ ] ], [ - 612, - [ - { - "SystemCall": { - "system": { - "Deref": { - "register": "FP", - "offset": -5 - } - } - } - } - ] - ], - [ - 633, - [ - { - "AllocSegment": { - "dst": { - "register": "AP", - "offset": 0 - } - } - } - ] - ], - [ - 648, + 1104, [ { "AllocSegment": { @@ -19162,7 +20312,7 @@ ] ], [ - 662, + 1122, [ { "AllocSegment": { @@ -19175,7 +20325,7 @@ ] ], [ - 683, + 1144, [ { "AllocSegment": { @@ -19188,7 +20338,7 @@ ] ], [ - 697, + 1158, [ { "AllocSegment": { @@ -19201,7 +20351,7 @@ ] ], [ - 714, + 1175, [ { "TestLessThanOrEqual": { @@ -19223,7 +20373,7 @@ ] ], [ - 748, + 1209, [ { "TestLessThan": { @@ -19251,7 +20401,7 @@ ] ], [ - 752, + 1213, [ { "LinearSplit": { @@ -19280,7 +20430,7 @@ ] ], [ - 793, + 1254, [ { "AllocSegment": { @@ -19293,7 +20443,7 @@ ] ], [ - 858, + 1319, [ { "AllocSegment": { @@ -19306,7 +20456,7 @@ ] ], [ - 905, + 1366, [ { "AllocSegment": { @@ -19319,7 +20469,7 @@ ] ], [ - 924, + 1385, [ { "TestLessThanOrEqual": { @@ -19341,7 +20491,7 @@ ] ], [ - 951, + 1412, [ { "AllocSegment": { @@ -19354,7 +20504,7 @@ ] ], [ - 969, + 1430, [ { "AllocSegment": { @@ -19367,7 +20517,7 @@ ] ], [ - 984, + 1445, [ { "AllocSegment": { @@ -19380,7 +20530,7 @@ ] ], [ - 998, + 1459, [ { "AllocSegment": { @@ -19393,7 +20543,7 @@ ] ], [ - 1019, + 1480, [ { "AllocSegment": { @@ -19406,7 +20556,7 @@ ] ], [ - 1033, + 1494, [ { "AllocSegment": { @@ -19419,7 +20569,7 @@ ] ], [ - 1048, + 1509, [ { "TestLessThanOrEqual": { @@ -19441,7 +20591,7 @@ ] ], [ - 1082, + 1543, [ { "TestLessThan": { @@ -19469,7 +20619,7 @@ ] ], [ - 1086, + 1547, [ { "LinearSplit": { @@ -19498,7 +20648,7 @@ ] ], [ - 1111, + 1572, [ { "AllocSegment": { @@ -19511,7 +20661,7 @@ ] ], [ - 1130, + 1591, [ { "TestLessThanOrEqual": { @@ -19533,7 +20683,7 @@ ] ], [ - 1149, + 1610, [ { "SystemCall": { @@ -19548,7 +20698,7 @@ ] ], [ - 1152, + 1613, [ { "AllocSegment": { @@ -19561,7 +20711,7 @@ ] ], [ - 1175, + 1636, [ { "AllocSegment": { @@ -19574,7 +20724,7 @@ ] ], [ - 1197, + 1658, [ { "AllocSegment": { @@ -19587,7 +20737,7 @@ ] ], [ - 1211, + 1672, [ { "AllocSegment": { @@ -19600,7 +20750,7 @@ ] ], [ - 1228, + 1689, [ { "TestLessThanOrEqual": { @@ -19622,7 +20772,7 @@ ] ], [ - 1327, + 1788, [ { "AllocSegment": { @@ -19635,7 +20785,7 @@ ] ], [ - 1346, + 1807, [ { "TestLessThanOrEqual": { @@ -19657,7 +20807,7 @@ ] ], [ - 1389, + 1850, [ { "AllocSegment": { @@ -19670,7 +20820,7 @@ ] ], [ - 1407, + 1868, [ { "AllocSegment": { @@ -19683,7 +20833,7 @@ ] ], [ - 1422, + 1883, [ { "AllocSegment": { @@ -19696,7 +20846,7 @@ ] ], [ - 1436, + 1897, [ { "AllocSegment": { @@ -19709,7 +20859,7 @@ ] ], [ - 1450, + 1911, [ { "AllocSegment": { @@ -19722,7 +20872,7 @@ ] ], [ - 1464, + 1925, [ { "AllocSegment": { @@ -19735,7 +20885,7 @@ ] ], [ - 1486, + 1947, [ { "AllocSegment": { @@ -19748,7 +20898,7 @@ ] ], [ - 1500, + 1961, [ { "AllocSegment": { @@ -19761,7 +20911,7 @@ ] ], [ - 1517, + 1978, [ { "TestLessThanOrEqual": { @@ -19783,7 +20933,7 @@ ] ], [ - 1551, + 2012, [ { "TestLessThan": { @@ -19805,7 +20955,7 @@ ] ], [ - 1555, + 2016, [ { "LinearSplit": { @@ -19834,7 +20984,7 @@ ] ], [ - 1565, + 2026, [ { "LinearSplit": { @@ -19863,7 +21013,7 @@ ] ], [ - 1618, + 2079, [ { "AllocSegment": { @@ -19876,7 +21026,7 @@ ] ], [ - 1665, + 2126, [ { "AllocSegment": { @@ -19889,7 +21039,7 @@ ] ], [ - 1684, + 2145, [ { "TestLessThanOrEqual": { @@ -19911,7 +21061,7 @@ ] ], [ - 1706, + 2167, [ { "SystemCall": { @@ -19926,7 +21076,7 @@ ] ], [ - 1727, + 2188, [ { "AllocSegment": { @@ -19939,7 +21089,7 @@ ] ], [ - 1742, + 2203, [ { "AllocSegment": { @@ -19952,7 +21102,7 @@ ] ], [ - 1756, + 2217, [ { "AllocSegment": { @@ -19965,7 +21115,7 @@ ] ], [ - 1777, + 2238, [ { "AllocSegment": { @@ -19978,7 +21128,7 @@ ] ], [ - 1791, + 2252, [ { "AllocSegment": { @@ -19991,7 +21141,7 @@ ] ], [ - 1806, + 2267, [ { "TestLessThanOrEqual": { @@ -20013,7 +21163,7 @@ ] ], [ - 1839, + 2300, [ { "TestLessThan": { @@ -20035,7 +21185,7 @@ ] ], [ - 1843, + 2304, [ { "LinearSplit": { @@ -20064,7 +21214,7 @@ ] ], [ - 1853, + 2314, [ { "LinearSplit": { @@ -20093,7 +21243,7 @@ ] ], [ - 1952, + 2413, [ { "AllocSegment": { @@ -20106,7 +21256,7 @@ ] ], [ - 1971, + 2432, [ { "TestLessThanOrEqual": { @@ -20128,7 +21278,7 @@ ] ], [ - 2000, + 2461, [ { "AllocSegment": { @@ -20141,7 +21291,7 @@ ] ], [ - 2015, + 2476, [ { "AllocSegment": { @@ -20154,7 +21304,7 @@ ] ], [ - 2029, + 2490, [ { "AllocSegment": { @@ -20167,7 +21317,7 @@ ] ], [ - 2043, + 2504, [ { "AllocSegment": { @@ -20180,7 +21330,7 @@ ] ], [ - 2057, + 2518, [ { "AllocSegment": { @@ -20193,7 +21343,7 @@ ] ], [ - 2078, + 2539, [ { "AllocSegment": { @@ -20206,7 +21356,7 @@ ] ], [ - 2092, + 2553, [ { "AllocSegment": { @@ -20219,7 +21369,7 @@ ] ], [ - 2107, + 2568, [ { "TestLessThanOrEqual": { @@ -20241,7 +21391,7 @@ ] ], [ - 2140, + 2601, [ { "TestLessThan": { @@ -20263,7 +21413,7 @@ ] ], [ - 2144, + 2605, [ { "LinearSplit": { @@ -20292,7 +21442,7 @@ ] ], [ - 2154, + 2615, [ { "LinearSplit": { @@ -20321,7 +21471,7 @@ ] ], [ - 2169, + 2630, [ { "AllocSegment": { @@ -20334,7 +21484,7 @@ ] ], [ - 2188, + 2649, [ { "TestLessThanOrEqual": { @@ -20356,7 +21506,7 @@ ] ], [ - 2207, + 2668, [ { "SystemCall": { @@ -20371,7 +21521,7 @@ ] ], [ - 2210, + 2671, [ { "AllocSegment": { @@ -20384,7 +21534,7 @@ ] ], [ - 2230, + 2691, [ { "AllocSegment": { @@ -20397,7 +21547,7 @@ ] ], [ - 2252, + 2713, [ { "AllocSegment": { @@ -20410,7 +21560,7 @@ ] ], [ - 2266, + 2727, [ { "AllocSegment": { @@ -20423,7 +21573,7 @@ ] ], [ - 2283, + 2744, [ { "TestLessThanOrEqual": { @@ -20445,7 +21595,7 @@ ] ], [ - 2338, + 2799, [ { "AllocSegment": { @@ -20458,7 +21608,7 @@ ] ], [ - 2385, + 2846, [ { "AllocSegment": { @@ -20471,7 +21621,7 @@ ] ], [ - 2404, + 2865, [ { "TestLessThanOrEqual": { @@ -20493,7 +21643,7 @@ ] ], [ - 2425, + 2886, [ { "SystemCall": { @@ -20508,7 +21658,7 @@ ] ], [ - 2428, + 2889, [ { "AllocSegment": { @@ -20521,7 +21671,7 @@ ] ], [ - 2448, + 2909, [ { "AllocSegment": { @@ -20534,7 +21684,7 @@ ] ], [ - 2463, + 2924, [ { "AllocSegment": { @@ -20547,7 +21697,7 @@ ] ], [ - 2477, + 2938, [ { "AllocSegment": { @@ -20560,7 +21710,7 @@ ] ], [ - 2491, + 2952, [ { "AllocSegment": { @@ -20573,7 +21723,7 @@ ] ], [ - 2506, + 2967, [ { "TestLessThanOrEqual": { @@ -20595,7 +21745,7 @@ ] ], [ - 2523, + 2984, [ { "AllocSegment": { @@ -20608,7 +21758,7 @@ ] ], [ - 2543, + 3004, [ { "TestLessThanOrEqual": { @@ -20630,7 +21780,7 @@ ] ], [ - 2555, + 3016, [ { "AllocFelt252Dict": { @@ -20645,7 +21795,7 @@ ] ], [ - 2574, + 3035, [ { "AllocSegment": { @@ -20658,7 +21808,7 @@ ] ], [ - 2585, + 3046, [ { "AllocSegment": { @@ -20671,7 +21821,7 @@ ] ], [ - 2601, + 3062, [ { "AllocSegment": { @@ -20684,7 +21834,7 @@ ] ], [ - 2619, + 3080, [ { "TestLessThanOrEqual": { @@ -20706,7 +21856,7 @@ ] ], [ - 2653, + 3114, [ { "TestLessThan": { @@ -20728,7 +21878,7 @@ ] ], [ - 2657, + 3118, [ { "LinearSplit": { @@ -20757,7 +21907,7 @@ ] ], [ - 2667, + 3128, [ { "LinearSplit": { @@ -20786,7 +21936,7 @@ ] ], [ - 2720, + 3181, [ { "AllocSegment": { @@ -20799,7 +21949,7 @@ ] ], [ - 2797, + 3258, [ { "AllocSegment": { @@ -20812,7 +21962,7 @@ ] ], [ - 2816, + 3277, [ { "TestLessThanOrEqual": { @@ -20834,7 +21984,7 @@ ] ], [ - 2842, + 3303, [ { "SystemCall": { @@ -20849,7 +21999,7 @@ ] ], [ - 2845, + 3306, [ { "AllocSegment": { @@ -20862,7 +22012,7 @@ ] ], [ - 2865, + 3326, [ { "AllocSegment": { @@ -20875,7 +22025,7 @@ ] ], [ - 2880, + 3341, [ { "AllocSegment": { @@ -20888,7 +22038,7 @@ ] ], [ - 2894, + 3355, [ { "AllocSegment": { @@ -20901,7 +22051,7 @@ ] ], [ - 2908, + 3369, [ { "AllocSegment": { @@ -20914,7 +22064,7 @@ ] ], [ - 2929, + 3390, [ { "AllocSegment": { @@ -20927,7 +22077,7 @@ ] ], [ - 2943, + 3404, [ { "AllocSegment": { @@ -20940,7 +22090,7 @@ ] ], [ - 2958, + 3419, [ { "TestLessThanOrEqual": { @@ -20962,7 +22112,7 @@ ] ], [ - 2975, + 3436, [ { "AllocSegment": { @@ -20975,7 +22125,7 @@ ] ], [ - 2994, + 3455, [ { "TestLessThanOrEqual": { @@ -20997,7 +22147,7 @@ ] ], [ - 3014, + 3475, [ { "AllocSegment": { @@ -21010,7 +22160,7 @@ ] ], [ - 3032, + 3493, [ { "AllocSegment": { @@ -21023,7 +22173,7 @@ ] ], [ - 3047, + 3508, [ { "AllocSegment": { @@ -21036,7 +22186,7 @@ ] ], [ - 3062, + 3523, [ { "TestLessThanOrEqual": { @@ -21058,7 +22208,7 @@ ] ], [ - 3079, + 3540, [ { "AllocSegment": { @@ -21071,7 +22221,7 @@ ] ], [ - 3098, + 3559, [ { "TestLessThanOrEqual": { @@ -21093,7 +22243,7 @@ ] ], [ - 3118, + 3579, [ { "AllocSegment": { @@ -21106,7 +22256,7 @@ ] ], [ - 3136, + 3597, [ { "AllocSegment": { @@ -21119,7 +22269,7 @@ ] ], [ - 3151, + 3612, [ { "AllocSegment": { @@ -21132,7 +22282,7 @@ ] ], [ - 3166, + 3627, [ { "TestLessThanOrEqual": { @@ -21154,7 +22304,7 @@ ] ], [ - 3183, + 3644, [ { "AllocSegment": { @@ -21167,7 +22317,7 @@ ] ], [ - 3208, + 3669, [ { "TestLessThanOrEqual": { @@ -21192,7 +22342,7 @@ ] ], [ - 3227, + 3688, [ { "AllocSegment": { @@ -21205,7 +22355,7 @@ ] ], [ - 3247, + 3708, [ { "AllocSegment": { @@ -21218,7 +22368,7 @@ ] ], [ - 3263, + 3724, [ { "AllocSegment": { @@ -21231,7 +22381,7 @@ ] ], [ - 3279, + 3740, [ { "TestLessThanOrEqual": { @@ -21253,7 +22403,7 @@ ] ], [ - 3296, + 3757, [ { "AllocSegment": { @@ -21266,7 +22416,7 @@ ] ], [ - 3315, + 3776, [ { "TestLessThanOrEqual": { @@ -21288,7 +22438,7 @@ ] ], [ - 3335, + 3796, [ { "AllocSegment": { @@ -21301,7 +22451,7 @@ ] ], [ - 3353, + 3814, [ { "AllocSegment": { @@ -21314,7 +22464,7 @@ ] ], [ - 3368, + 3829, [ { "AllocSegment": { @@ -21327,7 +22477,7 @@ ] ], [ - 3383, + 3844, [ { "TestLessThanOrEqual": { @@ -21349,7 +22499,7 @@ ] ], [ - 3442, + 3903, [ { "AllocSegment": { @@ -21362,7 +22512,7 @@ ] ], [ - 3461, + 3922, [ { "TestLessThanOrEqual": { @@ -21384,7 +22534,7 @@ ] ], [ - 3478, + 3939, [ { "AllocSegment": { @@ -21397,7 +22547,7 @@ ] ], [ - 3492, + 3953, [ { "AllocSegment": { @@ -21410,7 +22560,7 @@ ] ], [ - 3506, + 3967, [ { "AllocSegment": { @@ -21423,7 +22573,7 @@ ] ], [ - 3521, + 3982, [ { "AllocSegment": { @@ -21436,7 +22586,7 @@ ] ], [ - 3535, + 3996, [ { "AllocSegment": { @@ -21449,7 +22599,7 @@ ] ], [ - 3549, + 4010, [ { "AllocSegment": { @@ -21462,7 +22612,7 @@ ] ], [ - 3564, + 4025, [ { "TestLessThanOrEqual": { @@ -21484,7 +22634,7 @@ ] ], [ - 3597, + 4058, [ { "AllocSegment": { @@ -21497,7 +22647,7 @@ ] ], [ - 3644, + 4105, [ { "AllocSegment": { @@ -21510,7 +22660,7 @@ ] ], [ - 3663, + 4124, [ { "TestLessThanOrEqual": { @@ -21532,7 +22682,7 @@ ] ], [ - 3685, + 4146, [ { "AllocSegment": { @@ -21545,7 +22695,7 @@ ] ], [ - 3705, + 4166, [ { "AllocSegment": { @@ -21558,7 +22708,7 @@ ] ], [ - 3720, + 4181, [ { "AllocSegment": { @@ -21571,7 +22721,7 @@ ] ], [ - 3734, + 4195, [ { "AllocSegment": { @@ -21584,7 +22734,7 @@ ] ], [ - 3749, + 4210, [ { "TestLessThanOrEqual": { @@ -21606,7 +22756,7 @@ ] ], [ - 3766, + 4227, [ { "AllocSegment": { @@ -21619,7 +22769,7 @@ ] ], [ - 3785, + 4246, [ { "TestLessThanOrEqual": { @@ -21641,7 +22791,7 @@ ] ], [ - 3797, + 4258, [ { "AllocSegment": { @@ -21654,7 +22804,7 @@ ] ], [ - 3812, + 4273, [ { "AllocSegment": { @@ -21667,7 +22817,7 @@ ] ], [ - 3827, + 4288, [ { "AllocSegment": { @@ -21680,7 +22830,7 @@ ] ], [ - 3842, + 4303, [ { "TestLessThanOrEqual": { @@ -21702,7 +22852,7 @@ ] ], [ - 3880, + 4341, [ { "AllocSegment": { @@ -21715,7 +22865,7 @@ ] ], [ - 3899, + 4360, [ { "TestLessThanOrEqual": { @@ -21737,7 +22887,7 @@ ] ], [ - 3919, + 4380, [ { "AllocSegment": { @@ -21750,7 +22900,7 @@ ] ], [ - 3937, + 4398, [ { "AllocSegment": { @@ -21763,7 +22913,7 @@ ] ], [ - 3952, + 4413, [ { "AllocSegment": { @@ -21776,7 +22926,7 @@ ] ], [ - 3966, + 4427, [ { "AllocSegment": { @@ -21789,7 +22939,7 @@ ] ], [ - 3981, + 4442, [ { "TestLessThanOrEqual": { @@ -21811,7 +22961,7 @@ ] ], [ - 4019, + 4480, [ { "AllocSegment": { @@ -21824,7 +22974,7 @@ ] ], [ - 4038, + 4499, [ { "TestLessThanOrEqual": { @@ -21846,7 +22996,7 @@ ] ], [ - 4058, + 4519, [ { "AllocSegment": { @@ -21859,7 +23009,7 @@ ] ], [ - 4076, + 4537, [ { "AllocSegment": { @@ -21872,7 +23022,7 @@ ] ], [ - 4091, + 4552, [ { "AllocSegment": { @@ -21885,7 +23035,7 @@ ] ], [ - 4105, + 4566, [ { "AllocSegment": { @@ -21898,7 +23048,7 @@ ] ], [ - 4120, + 4581, [ { "TestLessThanOrEqual": { @@ -21920,7 +23070,7 @@ ] ], [ - 4153, + 4614, [ { "TestLessThan": { @@ -21942,7 +23092,7 @@ ] ], [ - 4157, + 4618, [ { "LinearSplit": { @@ -21971,7 +23121,7 @@ ] ], [ - 4167, + 4628, [ { "LinearSplit": { @@ -22000,7 +23150,7 @@ ] ], [ - 4224, + 4685, [ { "AllocSegment": { @@ -22013,7 +23163,7 @@ ] ], [ - 4243, + 4704, [ { "TestLessThanOrEqual": { @@ -22035,7 +23185,7 @@ ] ], [ - 4265, + 4726, [ { "AllocSegment": { @@ -22048,7 +23198,7 @@ ] ], [ - 4283, + 4744, [ { "SystemCall": { @@ -22063,7 +23213,7 @@ ] ], [ - 4289, + 4750, [ { "AllocSegment": { @@ -22076,7 +23226,7 @@ ] ], [ - 4308, + 4769, [ { "AllocSegment": { @@ -22089,7 +23239,7 @@ ] ], [ - 4323, + 4784, [ { "AllocSegment": { @@ -22102,7 +23252,7 @@ ] ], [ - 4337, + 4798, [ { "AllocSegment": { @@ -22115,7 +23265,7 @@ ] ], [ - 4358, + 4819, [ { "AllocSegment": { @@ -22128,7 +23278,7 @@ ] ], [ - 4372, + 4833, [ { "AllocSegment": { @@ -22141,7 +23291,7 @@ ] ], [ - 4387, + 4848, [ { "TestLessThanOrEqual": { @@ -22163,7 +23313,7 @@ ] ], [ - 4467, + 4928, [ { "AllocSegment": { @@ -22176,7 +23326,7 @@ ] ], [ - 4492, + 4953, [ { "TestLessThanOrEqual": { @@ -22201,7 +23351,7 @@ ] ], [ - 4514, + 4975, [ { "AllocSegment": { @@ -22214,7 +23364,7 @@ ] ], [ - 4534, + 4995, [ { "AllocSegment": { @@ -22227,7 +23377,7 @@ ] ], [ - 4550, + 5011, [ { "AllocSegment": { @@ -22240,7 +23390,7 @@ ] ], [ - 4565, + 5026, [ { "AllocSegment": { @@ -22253,7 +23403,7 @@ ] ], [ - 4580, + 5041, [ { "AllocSegment": { @@ -22266,7 +23416,7 @@ ] ], [ - 4595, + 5056, [ { "AllocSegment": { @@ -22279,7 +23429,7 @@ ] ], [ - 4611, + 5072, [ { "TestLessThanOrEqual": { @@ -22301,7 +23451,7 @@ ] ], [ - 4634, + 5095, [ { "AllocSegment": { @@ -22314,7 +23464,7 @@ ] ], [ - 4664, + 5125, [ { "TestLessThanOrEqual": { @@ -22339,7 +23489,7 @@ ] ], [ - 4687, + 5148, [ { "AllocSegment": { @@ -22352,7 +23502,7 @@ ] ], [ - 4709, + 5170, [ { "AllocSegment": { @@ -22365,7 +23515,7 @@ ] ], [ - 4726, + 5187, [ { "AllocSegment": { @@ -22378,7 +23528,7 @@ ] ], [ - 4742, + 5203, [ { "AllocSegment": { @@ -22391,7 +23541,7 @@ ] ], [ - 4759, + 5220, [ { "TestLessThanOrEqual": { @@ -22413,7 +23563,7 @@ ] ], [ - 4792, + 5253, [ { "TestLessThan": { @@ -22435,7 +23585,7 @@ ] ], [ - 4796, + 5257, [ { "LinearSplit": { @@ -22464,7 +23614,7 @@ ] ], [ - 4806, + 5267, [ { "LinearSplit": { @@ -22493,7 +23643,7 @@ ] ], [ - 4827, + 5288, [ { "AllocSegment": { @@ -22506,7 +23656,7 @@ ] ], [ - 4846, + 5307, [ { "TestLessThanOrEqual": { @@ -22528,7 +23678,7 @@ ] ], [ - 4858, + 5319, [ { "AllocSegment": { @@ -22541,7 +23691,7 @@ ] ], [ - 4878, + 5339, [ { "SystemCall": { @@ -22556,7 +23706,7 @@ ] ], [ - 4881, + 5342, [ { "AllocSegment": { @@ -22569,7 +23719,7 @@ ] ], [ - 4901, + 5362, [ { "AllocSegment": { @@ -22582,7 +23732,7 @@ ] ], [ - 4916, + 5377, [ { "AllocSegment": { @@ -22595,7 +23745,7 @@ ] ], [ - 4937, + 5398, [ { "AllocSegment": { @@ -22608,7 +23758,7 @@ ] ], [ - 4951, + 5412, [ { "AllocSegment": { @@ -22621,7 +23771,7 @@ ] ], [ - 4966, + 5427, [ { "TestLessThanOrEqual": { @@ -22643,7 +23793,7 @@ ] ], [ - 4983, + 5444, [ { "AllocSegment": { @@ -22656,7 +23806,7 @@ ] ], [ - 5006, + 5467, [ { "TestLessThanOrEqual": { @@ -22681,7 +23831,7 @@ ] ], [ - 5025, + 5486, [ { "AllocSegment": { @@ -22694,7 +23844,7 @@ ] ], [ - 5045, + 5506, [ { "AllocSegment": { @@ -22707,7 +23857,7 @@ ] ], [ - 5061, + 5522, [ { "AllocSegment": { @@ -22720,7 +23870,7 @@ ] ], [ - 5077, + 5538, [ { "TestLessThanOrEqual": { @@ -22742,7 +23892,7 @@ ] ], [ - 5115, + 5576, [ { "AllocSegment": { @@ -22755,7 +23905,7 @@ ] ], [ - 5140, + 5601, [ { "TestLessThanOrEqual": { @@ -22780,7 +23930,7 @@ ] ], [ - 5160, + 5621, [ { "AllocSegment": { @@ -22793,7 +23943,7 @@ ] ], [ - 5180, + 5641, [ { "AllocSegment": { @@ -22806,7 +23956,7 @@ ] ], [ - 5196, + 5657, [ { "AllocSegment": { @@ -22819,7 +23969,7 @@ ] ], [ - 5211, + 5672, [ { "AllocSegment": { @@ -22832,7 +23982,7 @@ ] ], [ - 5227, + 5688, [ { "TestLessThanOrEqual": { @@ -22854,7 +24004,7 @@ ] ], [ - 5265, + 5726, [ { "AllocSegment": { @@ -22867,7 +24017,7 @@ ] ], [ - 5284, + 5745, [ { "TestLessThanOrEqual": { @@ -22889,7 +24039,7 @@ ] ], [ - 5296, + 5757, [ { "AllocSegment": { @@ -22902,7 +24052,7 @@ ] ], [ - 5316, + 5777, [ { "SystemCall": { @@ -22917,7 +24067,7 @@ ] ], [ - 5319, + 5780, [ { "AllocSegment": { @@ -22930,7 +24080,7 @@ ] ], [ - 5339, + 5800, [ { "AllocSegment": { @@ -22943,7 +24093,7 @@ ] ], [ - 5354, + 5815, [ { "AllocSegment": { @@ -22956,7 +24106,7 @@ ] ], [ - 5368, + 5829, [ { "AllocSegment": { @@ -22969,7 +24119,7 @@ ] ], [ - 5383, + 5844, [ { "TestLessThanOrEqual": { @@ -22991,7 +24141,7 @@ ] ], [ - 5400, + 5861, [ { "AllocSegment": { @@ -23004,7 +24154,7 @@ ] ], [ - 5431, + 5892, [ { "TestLessThanOrEqual": { @@ -23029,7 +24179,7 @@ ] ], [ - 5451, + 5912, [ { "AllocSegment": { @@ -23042,7 +24192,7 @@ ] ], [ - 5475, + 5936, [ { "AllocSegment": { @@ -23055,7 +24205,7 @@ ] ], [ - 5493, + 5954, [ { "AllocSegment": { @@ -23068,7 +24218,7 @@ ] ], [ - 5511, + 5972, [ { "TestLessThanOrEqual": { @@ -23090,7 +24240,7 @@ ] ], [ - 5528, + 5989, [ { "AllocSegment": { @@ -23103,7 +24253,7 @@ ] ], [ - 5548, + 6009, [ { "TestLessThanOrEqual": { @@ -23125,7 +24275,7 @@ ] ], [ - 5572, + 6033, [ { "AllocSegment": { @@ -23138,7 +24288,7 @@ ] ], [ - 5602, + 6063, [ { "AllocSegment": { @@ -23151,7 +24301,7 @@ ] ], [ - 5618, + 6079, [ { "AllocSegment": { @@ -23164,7 +24314,7 @@ ] ], [ - 5634, + 6095, [ { "TestLessThanOrEqual": { @@ -23186,7 +24336,7 @@ ] ], [ - 5693, + 6154, [ { "AllocSegment": { @@ -23199,7 +24349,7 @@ ] ], [ - 5712, + 6173, [ { "TestLessThanOrEqual": { @@ -23221,7 +24371,7 @@ ] ], [ - 5724, + 6185, [ { "AllocSegment": { @@ -23234,7 +24384,7 @@ ] ], [ - 5737, + 6198, [ { "AllocSegment": { @@ -23247,7 +24397,7 @@ ] ], [ - 5752, + 6213, [ { "AllocSegment": { @@ -23260,7 +24410,7 @@ ] ], [ - 5766, + 6227, [ { "AllocSegment": { @@ -23273,7 +24423,7 @@ ] ], [ - 5780, + 6241, [ { "AllocSegment": { @@ -23286,7 +24436,7 @@ ] ], [ - 5795, + 6256, [ { "TestLessThanOrEqual": { @@ -23308,7 +24458,7 @@ ] ], [ - 5849, + 6310, [ { "TestLessThan": { @@ -23330,7 +24480,7 @@ ] ], [ - 5853, + 6314, [ { "LinearSplit": { @@ -23359,7 +24509,7 @@ ] ], [ - 5863, + 6324, [ { "LinearSplit": { @@ -23388,7 +24538,7 @@ ] ], [ - 5899, + 6360, [ { "AllocSegment": { @@ -23401,7 +24551,7 @@ ] ], [ - 5918, + 6379, [ { "TestLessThanOrEqual": { @@ -23423,7 +24573,7 @@ ] ], [ - 5941, + 6402, [ { "SystemCall": { @@ -23438,7 +24588,7 @@ ] ], [ - 5944, + 6405, [ { "AllocSegment": { @@ -23451,7 +24601,7 @@ ] ], [ - 5966, + 6427, [ { "AllocSegment": { @@ -23464,7 +24614,7 @@ ] ], [ - 5981, + 6442, [ { "AllocSegment": { @@ -23477,7 +24627,7 @@ ] ], [ - 6002, + 6463, [ { "AllocSegment": { @@ -23490,7 +24640,7 @@ ] ], [ - 6016, + 6477, [ { "AllocSegment": { @@ -23503,7 +24653,7 @@ ] ], [ - 6030, + 6491, [ { "AllocSegment": { @@ -23516,7 +24666,7 @@ ] ], [ - 6045, + 6506, [ { "TestLessThanOrEqual": { @@ -23538,7 +24688,7 @@ ] ], [ - 6104, + 6565, [ { "AllocSegment": { @@ -23551,7 +24701,7 @@ ] ], [ - 6123, + 6584, [ { "TestLessThanOrEqual": { @@ -23573,7 +24723,7 @@ ] ], [ - 6149, + 6610, [ { "SystemCall": { @@ -23588,7 +24738,7 @@ ] ], [ - 6152, + 6613, [ { "AllocSegment": { @@ -23601,7 +24751,7 @@ ] ], [ - 6174, + 6635, [ { "AllocSegment": { @@ -23614,7 +24764,7 @@ ] ], [ - 6189, + 6650, [ { "AllocSegment": { @@ -23627,7 +24777,7 @@ ] ], [ - 6203, + 6664, [ { "AllocSegment": { @@ -23640,7 +24790,7 @@ ] ], [ - 6217, + 6678, [ { "AllocSegment": { @@ -23653,7 +24803,7 @@ ] ], [ - 6232, + 6693, [ { "TestLessThanOrEqual": { @@ -23675,7 +24825,7 @@ ] ], [ - 6304, + 6765, [ { "AllocSegment": { @@ -23688,7 +24838,193 @@ ] ], [ - 6324, + 6793, + [ + { + "SystemCall": { + "system": { + "Deref": { + "register": "FP", + "offset": -7 + } + } + } + } + ] + ], + [ + 6798, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 6849, + [ + { + "SystemCall": { + "system": { + "Deref": { + "register": "AP", + "offset": -8 + } + } + } + } + ] + ], + [ + 6870, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 6897, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 6913, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 6927, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 6943, + [ + { + "SystemCall": { + "system": { + "Deref": { + "register": "FP", + "offset": -4 + } + } + } + } + ] + ], + [ + 6952, + [ + { + "SystemCall": { + "system": { + "BinOp": { + "op": "Add", + "a": { + "register": "FP", + "offset": -4 + }, + "b": { + "Immediate": "0x8" + } + } + } + } + } + ] + ], + [ + 6965, + [ + { + "SystemCall": { + "system": { + "BinOp": { + "op": "Add", + "a": { + "register": "FP", + "offset": -4 + }, + "b": { + "Immediate": "0xd" + } + } + } + } + } + ] + ], + [ + 6982, + [ + { + "SystemCall": { + "system": { + "BinOp": { + "op": "Add", + "a": { + "register": "FP", + "offset": -4 + }, + "b": { + "Immediate": "0x14" + } + } + } + } + } + ] + ], + [ + 6985, + [ + { + "AllocSegment": { + "dst": { + "register": "AP", + "offset": 0 + } + } + } + ] + ], + [ + 7053, [ { "TestLessThanOrEqual": { @@ -23710,7 +25046,7 @@ ] ], [ - 6349, + 7078, [ { "SystemCall": { @@ -23725,7 +25061,7 @@ ] ], [ - 6357, + 7086, [ { "TestLessThan": { @@ -23756,7 +25092,7 @@ ] ], [ - 6382, + 7111, [ { "AllocSegment": { @@ -23769,7 +25105,7 @@ ] ], [ - 6415, + 7144, [ { "AllocSegment": { @@ -23782,7 +25118,7 @@ ] ], [ - 6452, + 7181, [ { "TestLessThan": { @@ -23810,7 +25146,7 @@ ] ], [ - 6456, + 7185, [ { "LinearSplit": { @@ -23839,7 +25175,7 @@ ] ], [ - 6498, + 7227, [ { "TestLessThan": { @@ -23867,7 +25203,7 @@ ] ], [ - 6502, + 7231, [ { "LinearSplit": { @@ -23896,7 +25232,7 @@ ] ], [ - 6543, + 7272, [ { "TestLessThan": { @@ -23918,7 +25254,7 @@ ] ], [ - 6547, + 7276, [ { "LinearSplit": { @@ -23947,7 +25283,7 @@ ] ], [ - 6557, + 7286, [ { "LinearSplit": { @@ -23976,7 +25312,7 @@ ] ], [ - 6681, + 7410, [ { "TestLessThan": { @@ -23998,7 +25334,7 @@ ] ], [ - 6685, + 7414, [ { "LinearSplit": { @@ -24027,7 +25363,7 @@ ] ], [ - 6695, + 7424, [ { "LinearSplit": { @@ -24056,7 +25392,7 @@ ] ], [ - 6727, + 7456, [ { "TestLessThan": { @@ -24078,7 +25414,7 @@ ] ], [ - 6729, + 7458, [ { "DivMod": { @@ -24104,7 +25440,7 @@ ] ], [ - 6849, + 7578, [ { "AllocSegment": { @@ -24117,7 +25453,7 @@ ] ], [ - 6963, + 7692, [ { "TestLessThan": { @@ -24139,7 +25475,7 @@ ] ], [ - 6965, + 7694, [ { "DivMod": { @@ -24165,7 +25501,7 @@ ] ], [ - 7018, + 7747, [ { "TestLessThan": { @@ -24193,7 +25529,7 @@ ] ], [ - 7022, + 7751, [ { "LinearSplit": { @@ -24222,7 +25558,7 @@ ] ], [ - 7064, + 7793, [ { "TestLessThan": { @@ -24250,7 +25586,7 @@ ] ], [ - 7068, + 7797, [ { "LinearSplit": { @@ -24279,7 +25615,7 @@ ] ], [ - 7846, + 8575, [ { "SystemCall": { @@ -24294,7 +25630,7 @@ ] ], [ - 8079, + 8808, [ { "AllocSegment": { @@ -24307,7 +25643,7 @@ ] ], [ - 8093, + 8822, [ { "AllocSegment": { @@ -24320,7 +25656,7 @@ ] ], [ - 8107, + 8836, [ { "AllocSegment": { @@ -24333,7 +25669,7 @@ ] ], [ - 8171, + 8900, [ { "AllocSegment": { @@ -24346,7 +25682,7 @@ ] ], [ - 8185, + 8914, [ { "AllocSegment": { @@ -24359,7 +25695,7 @@ ] ], [ - 8208, + 8937, [ { "AllocSegment": { @@ -24372,7 +25708,7 @@ ] ], [ - 8232, + 8961, [ { "SystemCall": { @@ -24387,7 +25723,7 @@ ] ], [ - 8235, + 8964, [ { "AllocSegment": { @@ -24400,7 +25736,7 @@ ] ], [ - 8251, + 8980, [ { "SystemCall": { @@ -24421,7 +25757,7 @@ ] ], [ - 8288, + 9017, [ { "GetSegmentArenaIndex": { @@ -24440,7 +25776,7 @@ ] ], [ - 8329, + 9058, [ { "AllocSegment": { @@ -24453,7 +25789,7 @@ ] ], [ - 8337, + 9066, [ { "InitSquashData": { @@ -24488,7 +25824,7 @@ ] ], [ - 8356, + 9085, [ { "GetCurrentAccessIndex": { @@ -24503,7 +25839,7 @@ ] ], [ - 8369, + 9098, [ { "ShouldSkipSquashLoop": { @@ -24516,7 +25852,7 @@ ] ], [ - 8371, + 9100, [ { "GetCurrentAccessDelta": { @@ -24529,7 +25865,7 @@ ] ], [ - 8382, + 9111, [ { "ShouldContinueSquashLoop": { @@ -24542,7 +25878,7 @@ ] ], [ - 8396, + 9125, [ { "GetNextDictKey": { @@ -24555,7 +25891,7 @@ ] ], [ - 8415, + 9144, [ { "AssertLeFindSmallArcs": { @@ -24588,7 +25924,7 @@ ] ], [ - 8427, + 9156, [ { "AssertLeIsFirstArcExcluded": { @@ -24601,7 +25937,7 @@ ] ], [ - 8439, + 9168, [ { "AssertLeIsSecondArcExcluded": { @@ -24614,7 +25950,7 @@ ] ], [ - 8470, + 9199, [ { "AllocSegment": { @@ -24627,7 +25963,7 @@ ] ], [ - 8478, + 9207, [ { "AllocSegment": { @@ -24640,7 +25976,7 @@ ] ], [ - 8509, + 9238, [ { "SystemCall": { @@ -24655,7 +25991,7 @@ ] ], [ - 8523, + 9252, [ { "AllocSegment": { @@ -24668,7 +26004,7 @@ ] ], [ - 8543, + 9272, [ { "AllocSegment": { @@ -24681,7 +26017,7 @@ ] ], [ - 8557, + 9286, [ { "AllocSegment": { @@ -24694,7 +26030,7 @@ ] ], [ - 8571, + 9300, [ { "SystemCall": { @@ -24709,7 +26045,7 @@ ] ], [ - 8574, + 9303, [ { "AllocSegment": { @@ -24722,7 +26058,7 @@ ] ], [ - 8597, + 9326, [ { "TestLessThan": { @@ -24747,7 +26083,7 @@ ] ], [ - 8625, + 9354, [ { "AllocSegment": { @@ -24760,7 +26096,7 @@ ] ], [ - 8639, + 9368, [ { "AllocSegment": { @@ -24773,7 +26109,7 @@ ] ], [ - 8682, + 9411, [ { "AllocSegment": { @@ -24786,7 +26122,7 @@ ] ], [ - 8721, + 9450, [ { "AllocSegment": { @@ -24799,7 +26135,7 @@ ] ], [ - 8781, + 9510, [ { "SystemCall": { @@ -24814,7 +26150,7 @@ ] ], [ - 8791, + 9520, [ { "AllocSegment": { @@ -24827,7 +26163,7 @@ ] ], [ - 8822, + 9551, [ { "SystemCall": { @@ -24842,7 +26178,7 @@ ] ], [ - 8825, + 9554, [ { "AllocSegment": { @@ -24855,7 +26191,7 @@ ] ], [ - 8849, + 9578, [ { "TestLessThan": { @@ -24880,7 +26216,7 @@ ] ], [ - 8883, + 9612, [ { "SystemCall": { @@ -24895,7 +26231,7 @@ ] ], [ - 8898, + 9627, [ { "SystemCall": { @@ -24910,7 +26246,7 @@ ] ], [ - 8944, + 9673, [ { "AllocSegment": { @@ -24923,7 +26259,7 @@ ] ], [ - 8963, + 9692, [ { "DivMod": { @@ -24952,7 +26288,7 @@ ] ], [ - 8969, + 9698, [ { "TestLessThan": { @@ -24974,7 +26310,7 @@ ] ], [ - 9020, + 9749, [ { "AllocSegment": { @@ -24987,7 +26323,7 @@ ] ], [ - 9051, + 9780, [ { "AllocSegment": { @@ -25000,7 +26336,7 @@ ] ], [ - 9076, + 9805, [ { "AllocSegment": { @@ -25013,7 +26349,7 @@ ] ], [ - 9091, + 9820, [ { "AllocSegment": { @@ -25026,7 +26362,7 @@ ] ], [ - 9133, + 9862, [ { "SystemCall": { @@ -25041,7 +26377,7 @@ ] ], [ - 9145, + 9874, [ { "AllocSegment": { @@ -25054,7 +26390,7 @@ ] ], [ - 9175, + 9904, [ { "SystemCall": { @@ -25069,7 +26405,7 @@ ] ], [ - 9180, + 9909, [ { "AllocSegment": { @@ -25082,7 +26418,7 @@ ] ], [ - 9203, + 9932, [ { "TestLessThan": { @@ -25107,7 +26443,7 @@ ] ], [ - 9237, + 9966, [ { "SystemCall": { @@ -25122,7 +26458,7 @@ ] ], [ - 9252, + 9981, [ { "SystemCall": { @@ -25137,7 +26473,7 @@ ] ], [ - 9300, + 10029, [ { "AllocSegment": { @@ -25150,7 +26486,7 @@ ] ], [ - 9318, + 10047, [ { "DivMod": { @@ -25179,7 +26515,7 @@ ] ], [ - 9324, + 10053, [ { "TestLessThan": { @@ -25201,7 +26537,7 @@ ] ], [ - 9353, + 10082, [ { "SystemCall": { @@ -25216,7 +26552,7 @@ ] ], [ - 9403, + 10132, [ { "AllocSegment": { @@ -25229,7 +26565,7 @@ ] ], [ - 9441, + 10170, [ { "AllocSegment": { @@ -25242,7 +26578,7 @@ ] ], [ - 9468, + 10197, [ { "AllocSegment": { @@ -25255,7 +26591,7 @@ ] ], [ - 9484, + 10213, [ { "AllocSegment": { @@ -25268,7 +26604,7 @@ ] ], [ - 9510, + 10239, [ { "TestLessThanOrEqual": { @@ -25290,7 +26626,7 @@ ] ], [ - 9524, + 10253, [ { "TestLessThan": { @@ -25312,7 +26648,7 @@ ] ], [ - 9599, + 10328, [ { "TestLessThan": { @@ -25334,7 +26670,7 @@ ] ], [ - 9603, + 10332, [ { "LinearSplit": { @@ -25363,7 +26699,7 @@ ] ], [ - 9613, + 10342, [ { "LinearSplit": { @@ -25392,7 +26728,7 @@ ] ], [ - 9631, + 10360, [ { "SystemCall": { @@ -25407,7 +26743,7 @@ ] ], [ - 9649, + 10378, [ { "AllocSegment": { @@ -25420,7 +26756,7 @@ ] ], [ - 9668, + 10397, [ { "TestLessThan": { @@ -25442,7 +26778,7 @@ ] ], [ - 9672, + 10401, [ { "LinearSplit": { @@ -25471,7 +26807,7 @@ ] ], [ - 9682, + 10411, [ { "LinearSplit": { @@ -25500,7 +26836,7 @@ ] ], [ - 9700, + 10429, [ { "SystemCall": { @@ -25515,7 +26851,7 @@ ] ], [ - 9718, + 10447, [ { "AllocSegment": { @@ -25528,7 +26864,7 @@ ] ], [ - 9749, + 10478, [ { "AllocSegment": { @@ -25541,7 +26877,7 @@ ] ], [ - 9773, + 10502, [ { "AllocSegment": { @@ -25554,7 +26890,7 @@ ] ], [ - 9787, + 10516, [ { "AllocSegment": { @@ -25567,7 +26903,7 @@ ] ], [ - 9801, + 10530, [ { "AllocSegment": { @@ -25580,7 +26916,7 @@ ] ], [ - 9815, + 10544, [ { "AllocSegment": { @@ -25593,7 +26929,7 @@ ] ], [ - 9830, + 10559, [ { "AllocSegment": { @@ -25606,7 +26942,7 @@ ] ], [ - 9845, + 10574, [ { "TestLessThanOrEqual": { @@ -25628,7 +26964,7 @@ ] ], [ - 9859, + 10588, [ { "AllocSegment": { @@ -25641,7 +26977,7 @@ ] ], [ - 9879, + 10608, [ { "AllocSegment": { @@ -25654,7 +26990,7 @@ ] ], [ - 9893, + 10622, [ { "TestLessThanOrEqual": { @@ -25676,7 +27012,7 @@ ] ], [ - 9923, + 10652, [ { "AllocSegment": { @@ -25689,7 +27025,7 @@ ] ], [ - 9942, + 10671, [ { "TestLessThan": { @@ -25711,7 +27047,7 @@ ] ], [ - 9946, + 10675, [ { "LinearSplit": { @@ -25740,7 +27076,7 @@ ] ], [ - 9957, + 10686, [ { "LinearSplit": { @@ -25769,7 +27105,7 @@ ] ], [ - 9983, + 10712, [ { "SystemCall": { @@ -25784,7 +27120,7 @@ ] ], [ - 9998, + 10727, [ { "SystemCall": { @@ -25805,7 +27141,7 @@ ] ], [ - 10006, + 10735, [ { "TestLessThan": { @@ -25827,7 +27163,7 @@ ] ], [ - 10010, + 10739, [ { "LinearSplit": { @@ -25856,7 +27192,7 @@ ] ], [ - 10021, + 10750, [ { "LinearSplit": { @@ -25885,7 +27221,7 @@ ] ], [ - 10051, + 10780, [ { "SystemCall": { @@ -25906,7 +27242,7 @@ ] ], [ - 10067, + 10796, [ { "SystemCall": { @@ -25927,7 +27263,7 @@ ] ], [ - 10175, + 10904, [ { "TestLessThan": { @@ -25949,7 +27285,7 @@ ] ], [ - 10177, + 10906, [ { "DivMod": { @@ -25975,7 +27311,7 @@ ] ], [ - 10222, + 10951, [ { "TestLessThan": { @@ -25997,7 +27333,7 @@ ] ], [ - 10224, + 10953, [ { "DivMod": { @@ -26023,7 +27359,7 @@ ] ], [ - 10333, + 11062, [ { "TestLessThan": { @@ -26045,7 +27381,7 @@ ] ], [ - 10337, + 11066, [ { "LinearSplit": { @@ -26074,7 +27410,7 @@ ] ], [ - 10348, + 11077, [ { "LinearSplit": { @@ -26103,7 +27439,7 @@ ] ], [ - 10374, + 11103, [ { "SystemCall": { @@ -26118,7 +27454,7 @@ ] ], [ - 10389, + 11118, [ { "SystemCall": { @@ -26139,7 +27475,7 @@ ] ], [ - 10396, + 11125, [ { "TestLessThan": { @@ -26161,7 +27497,7 @@ ] ], [ - 10398, + 11127, [ { "DivMod": { @@ -26187,7 +27523,7 @@ ] ], [ - 10419, + 11148, [ { "TestLessThan": { @@ -26209,7 +27545,7 @@ ] ], [ - 10421, + 11150, [ { "DivMod": { @@ -26235,7 +27571,7 @@ ] ], [ - 10451, + 11180, [ { "TestLessThan": { @@ -26257,7 +27593,7 @@ ] ], [ - 10455, + 11184, [ { "LinearSplit": { @@ -26286,7 +27622,7 @@ ] ], [ - 10466, + 11195, [ { "LinearSplit": { @@ -26315,7 +27651,7 @@ ] ], [ - 10497, + 11226, [ { "SystemCall": { @@ -26330,7 +27666,7 @@ ] ], [ - 10512, + 11241, [ { "SystemCall": { @@ -26351,7 +27687,7 @@ ] ], [ - 10556, + 11285, [ { "AllocSegment": { @@ -26364,7 +27700,7 @@ ] ], [ - 10575, + 11304, [ { "AllocSegment": { @@ -26377,7 +27713,7 @@ ] ], [ - 10657, + 11386, [ { "RandomEcPoint": { @@ -26405,7 +27741,7 @@ ] ], [ - 10721, + 11450, [ { "RandomEcPoint": { @@ -26433,7 +27769,7 @@ ] ], [ - 10791, + 11520, [ { "AllocSegment": { @@ -26446,7 +27782,7 @@ ] ], [ - 10817, + 11546, [ { "SystemCall": { @@ -26461,7 +27797,7 @@ ] ], [ - 10834, + 11563, [ { "SystemCall": { @@ -26482,7 +27818,7 @@ ] ], [ - 10876, + 11605, [ { "AllocSegment": { @@ -26495,7 +27831,7 @@ ] ], [ - 10893, + 11622, [ { "AllocSegment": { @@ -26508,7 +27844,7 @@ ] ], [ - 10912, + 11641, [ { "SystemCall": { @@ -26523,7 +27859,7 @@ ] ], [ - 10922, + 11651, [ { "TestLessThan": { @@ -26545,7 +27881,7 @@ ] ], [ - 10926, + 11655, [ { "LinearSplit": { @@ -26574,7 +27910,7 @@ ] ], [ - 10937, + 11666, [ { "LinearSplit": { @@ -26603,7 +27939,7 @@ ] ], [ - 10981, + 11710, [ { "SystemCall": { @@ -26624,7 +27960,7 @@ ] ], [ - 10996, + 11725, [ { "SystemCall": { @@ -26645,7 +27981,7 @@ ] ], [ - 11006, + 11735, [ { "TestLessThan": { @@ -26670,7 +28006,7 @@ ] ], [ - 11021, + 11750, [ { "TestLessThan": { @@ -26695,7 +28031,7 @@ ] ], [ - 11037, + 11766, [ { "TestLessThan": { @@ -26717,7 +28053,7 @@ ] ], [ - 11041, + 11770, [ { "LinearSplit": { @@ -26746,7 +28082,7 @@ ] ], [ - 11052, + 11781, [ { "LinearSplit": { @@ -26775,7 +28111,7 @@ ] ], [ - 11081, + 11810, [ { "SystemCall": { @@ -26790,7 +28126,7 @@ ] ], [ - 11097, + 11826, [ { "SystemCall": { @@ -26811,7 +28147,7 @@ ] ], [ - 11139, + 11868, [ { "AllocSegment": { @@ -26824,7 +28160,7 @@ ] ], [ - 11157, + 11886, [ { "AllocSegment": { @@ -26837,7 +28173,7 @@ ] ], [ - 11259, + 11988, [ { "AllocSegment": { @@ -26850,7 +28186,7 @@ ] ], [ - 11334, + 12063, [ { "EvalCircuit": { @@ -26883,7 +28219,7 @@ ] ], [ - 11391, + 12120, [ { "AllocSegment": { @@ -26896,7 +28232,7 @@ ] ], [ - 11447, + 12176, [ { "AllocSegment": { @@ -26909,7 +28245,7 @@ ] ], [ - 11540, + 12269, [ { "AllocSegment": { @@ -26922,7 +28258,7 @@ ] ], [ - 11561, + 12290, [ { "AllocSegment": { @@ -26935,7 +28271,7 @@ ] ], [ - 11632, + 12361, [ { "AllocSegment": { @@ -26948,7 +28284,7 @@ ] ], [ - 11660, + 12389, [ { "AllocSegment": { @@ -26961,7 +28297,7 @@ ] ], [ - 11731, + 12460, [ { "TestLessThan": { @@ -26989,7 +28325,7 @@ ] ], [ - 11735, + 12464, [ { "LinearSplit": { @@ -27018,7 +28354,7 @@ ] ], [ - 11757, + 12486, [ { "TestLessThanOrEqual": { @@ -27043,7 +28379,7 @@ ] ], [ - 11771, + 12500, [ { "TestLessThan": { @@ -27065,7 +28401,7 @@ ] ], [ - 11781, + 12510, [ { "TestLessThanOrEqual": { @@ -27090,7 +28426,7 @@ ] ], [ - 11804, + 12533, [ { "AllocSegment": { @@ -27103,7 +28439,7 @@ ] ], [ - 11825, + 12554, [ { "AllocSegment": { @@ -27116,7 +28452,7 @@ ] ], [ - 11846, + 12575, [ { "AllocSegment": { @@ -27129,7 +28465,7 @@ ] ], [ - 11894, + 12623, [ { "TestLessThanOrEqual": { @@ -27151,7 +28487,7 @@ ] ], [ - 11954, + 12683, [ { "AllocSegment": { @@ -27164,7 +28500,7 @@ ] ], [ - 11974, + 12703, [ { "TestLessThanOrEqual": { @@ -27186,7 +28522,7 @@ ] ], [ - 12053, + 12782, [ { "AllocSegment": { @@ -27199,7 +28535,7 @@ ] ], [ - 12083, + 12812, [ { "AllocSegment": { @@ -27212,7 +28548,7 @@ ] ], [ - 12103, + 12832, [ { "TestLessThanOrEqual": { @@ -27234,7 +28570,7 @@ ] ], [ - 12204, + 12933, [ { "AllocSegment": { @@ -27247,7 +28583,7 @@ ] ], [ - 12234, + 12963, [ { "AllocSegment": { @@ -27260,7 +28596,7 @@ ] ], [ - 12254, + 12983, [ { "TestLessThanOrEqual": { @@ -27282,7 +28618,7 @@ ] ], [ - 12325, + 13054, [ { "AllocSegment": { @@ -27295,7 +28631,7 @@ ] ], [ - 12346, + 13075, [ { "DivMod": { @@ -27324,7 +28660,7 @@ ] ], [ - 12404, + 13133, [ { "AllocSegment": { @@ -27337,7 +28673,7 @@ ] ], [ - 12457, + 13186, [ { "AllocSegment": { @@ -27350,7 +28686,7 @@ ] ], [ - 12470, + 13199, [ { "DivMod": { @@ -27379,7 +28715,7 @@ ] ], [ - 12478, + 13207, [ { "TestLessThan": { @@ -27410,7 +28746,7 @@ ] ], [ - 12495, + 13224, [ { "AllocSegment": { @@ -27423,7 +28759,7 @@ ] ], [ - 12527, + 13256, [ { "TestLessThan": { @@ -27445,7 +28781,7 @@ ] ], [ - 12544, + 13273, [ { "AllocSegment": { @@ -27458,7 +28794,7 @@ ] ], [ - 12560, + 13289, [ { "TestLessThan": { @@ -27489,7 +28825,7 @@ ] ], [ - 12582, + 13311, [ { "AllocSegment": { @@ -27502,7 +28838,7 @@ ] ], [ - 12639, + 13368, [ { "DivMod": { @@ -27531,7 +28867,7 @@ ] ], [ - 12648, + 13377, [ { "TestLessThan": { @@ -27553,7 +28889,7 @@ ] ], [ - 12658, + 13387, [ { "TestLessThan": { @@ -27584,7 +28920,7 @@ ] ], [ - 12680, + 13409, [ { "AllocSegment": { @@ -27597,7 +28933,7 @@ ] ], [ - 12695, + 13424, [ { "AllocSegment": { @@ -27610,7 +28946,7 @@ ] ], [ - 12720, + 13449, [ { "TestLessThan": { @@ -27641,7 +28977,7 @@ ] ], [ - 12734, + 13463, [ { "DivMod": { @@ -27670,7 +29006,7 @@ ] ], [ - 12751, + 13480, [ { "TestLessThan": { @@ -27692,7 +29028,7 @@ ] ], [ - 12763, + 13492, [ { "TestLessThan": { @@ -27714,7 +29050,7 @@ ] ], [ - 12773, + 13502, [ { "TestLessThan": { @@ -27745,7 +29081,7 @@ ] ], [ - 12796, + 13525, [ { "AllocSegment": { @@ -27758,7 +29094,7 @@ ] ], [ - 12811, + 13540, [ { "AllocSegment": { @@ -27771,7 +29107,7 @@ ] ], [ - 12826, + 13555, [ { "AllocSegment": { @@ -27784,7 +29120,7 @@ ] ], [ - 12841, + 13570, [ { "AllocSegment": { @@ -27797,7 +29133,7 @@ ] ], [ - 12854, + 13583, [ { "TestLessThanOrEqual": { @@ -27819,7 +29155,7 @@ ] ], [ - 12864, + 13593, [ { "TestLessThanOrEqualAddress": { @@ -27850,7 +29186,7 @@ ] ], [ - 12901, + 13630, [ { "SystemCall": { @@ -27865,7 +29201,7 @@ ] ], [ - 12934, + 13663, [ { "AllocSegment": { @@ -27878,7 +29214,7 @@ ] ], [ - 12968, + 13697, [ { "TestLessThan": { @@ -27900,7 +29236,7 @@ ] ], [ - 12990, + 13719, [ { "TestLessThan": { @@ -27922,7 +29258,7 @@ ] ], [ - 13027, + 13756, [ { "TestLessThan": { @@ -27944,7 +29280,7 @@ ] ], [ - 13049, + 13778, [ { "TestLessThan": { @@ -27966,7 +29302,7 @@ ] ], [ - 13125, + 13854, [ { "AllocSegment": { @@ -27979,7 +29315,7 @@ ] ], [ - 13190, + 13919, [ { "TestLessThan": { @@ -28001,7 +29337,7 @@ ] ], [ - 13214, + 13943, [ { "TestLessThan": { @@ -28023,7 +29359,7 @@ ] ], [ - 13255, + 13984, [ { "TestLessThan": { @@ -28045,7 +29381,7 @@ ] ], [ - 13281, + 14010, [ { "TestLessThan": { @@ -28067,7 +29403,7 @@ ] ], [ - 13325, + 14054, [ { "U256InvModN": { @@ -28124,7 +29460,7 @@ ] ], [ - 13343, + 14072, [ { "WideMul128": { @@ -28321,7 +29657,7 @@ ] ], [ - 13396, + 14125, [ { "WideMul128": { @@ -28374,7 +29710,7 @@ ] ], [ - 13400, + 14129, [ { "TestLessThan": { @@ -28396,7 +29732,7 @@ ] ], [ - 13414, + 14143, [ { "TestLessThan": { @@ -28418,7 +29754,7 @@ ] ], [ - 13427, + 14156, [ { "DivMod": { @@ -28444,7 +29780,7 @@ ] ], [ - 13437, + 14166, [ { "DivMod": { @@ -28470,7 +29806,7 @@ ] ], [ - 13448, + 14177, [ { "DivMod": { @@ -28496,7 +29832,7 @@ ] ], [ - 13457, + 14186, [ { "DivMod": { @@ -28522,7 +29858,7 @@ ] ], [ - 13467, + 14196, [ { "DivMod": { @@ -28548,7 +29884,7 @@ ] ], [ - 13478, + 14207, [ { "DivMod": { @@ -28574,7 +29910,7 @@ ] ], [ - 13487, + 14216, [ { "DivMod": { @@ -28600,7 +29936,7 @@ ] ], [ - 13497, + 14226, [ { "DivMod": { @@ -28626,7 +29962,7 @@ ] ], [ - 13508, + 14237, [ { "DivMod": { @@ -28652,7 +29988,7 @@ ] ], [ - 13517, + 14246, [ { "DivMod": { @@ -28678,7 +30014,7 @@ ] ], [ - 13527, + 14256, [ { "DivMod": { @@ -28704,7 +30040,7 @@ ] ], [ - 13538, + 14267, [ { "DivMod": { @@ -28730,7 +30066,7 @@ ] ], [ - 13547, + 14276, [ { "DivMod": { @@ -28756,7 +30092,7 @@ ] ], [ - 13557, + 14286, [ { "DivMod": { @@ -28782,7 +30118,7 @@ ] ], [ - 13568, + 14297, [ { "DivMod": { @@ -28808,7 +30144,7 @@ ] ], [ - 13577, + 14306, [ { "DivMod": { @@ -28834,7 +30170,7 @@ ] ], [ - 13587, + 14316, [ { "DivMod": { @@ -28860,7 +30196,7 @@ ] ], [ - 13598, + 14327, [ { "DivMod": { @@ -28886,7 +30222,7 @@ ] ], [ - 13607, + 14336, [ { "DivMod": { @@ -28912,7 +30248,7 @@ ] ], [ - 13617, + 14346, [ { "DivMod": { @@ -28938,7 +30274,7 @@ ] ], [ - 13628, + 14357, [ { "DivMod": { @@ -28964,7 +30300,7 @@ ] ], [ - 13637, + 14366, [ { "DivMod": { @@ -28990,7 +30326,7 @@ ] ], [ - 13647, + 14376, [ { "DivMod": { @@ -29016,7 +30352,7 @@ ] ], [ - 13658, + 14387, [ { "DivMod": { @@ -29042,7 +30378,7 @@ ] ], [ - 13679, + 14408, [ { "Uint512DivModByUint256": { @@ -29111,7 +30447,7 @@ ] ], [ - 13697, + 14426, [ { "WideMul128": { @@ -29236,7 +30572,7 @@ ] ], [ - 13726, + 14455, [ { "TestLessThan": { @@ -29261,7 +30597,7 @@ ] ], [ - 13738, + 14467, [ { "TestLessThan": { @@ -29286,7 +30622,7 @@ ] ], [ - 13753, + 14482, [ { "DivMod": { @@ -29312,7 +30648,7 @@ ] ], [ - 13763, + 14492, [ { "DivMod": { @@ -29338,7 +30674,7 @@ ] ], [ - 13774, + 14503, [ { "DivMod": { @@ -29364,7 +30700,7 @@ ] ], [ - 13783, + 14512, [ { "DivMod": { @@ -29390,7 +30726,7 @@ ] ], [ - 13793, + 14522, [ { "DivMod": { @@ -29416,7 +30752,7 @@ ] ], [ - 13804, + 14533, [ { "DivMod": { @@ -29442,7 +30778,7 @@ ] ], [ - 13813, + 14542, [ { "DivMod": { @@ -29468,7 +30804,7 @@ ] ], [ - 13823, + 14552, [ { "DivMod": { @@ -29494,7 +30830,7 @@ ] ], [ - 13834, + 14563, [ { "DivMod": { @@ -29520,7 +30856,7 @@ ] ], [ - 13843, + 14572, [ { "DivMod": { @@ -29546,7 +30882,7 @@ ] ], [ - 13853, + 14582, [ { "DivMod": { @@ -29572,7 +30908,7 @@ ] ], [ - 13864, + 14593, [ { "DivMod": { @@ -29598,7 +30934,7 @@ ] ], [ - 13873, + 14602, [ { "DivMod": { @@ -29624,7 +30960,7 @@ ] ], [ - 13883, + 14612, [ { "DivMod": { @@ -29650,7 +30986,7 @@ ] ], [ - 13894, + 14623, [ { "DivMod": { @@ -29676,7 +31012,7 @@ ] ], [ - 13915, + 14644, [ { "Uint512DivModByUint256": { @@ -29745,7 +31081,7 @@ ] ], [ - 13933, + 14662, [ { "WideMul128": { @@ -29870,7 +31206,7 @@ ] ], [ - 13962, + 14691, [ { "TestLessThan": { @@ -29895,7 +31231,7 @@ ] ], [ - 13974, + 14703, [ { "TestLessThan": { @@ -29920,7 +31256,7 @@ ] ], [ - 13989, + 14718, [ { "DivMod": { @@ -29946,7 +31282,7 @@ ] ], [ - 13999, + 14728, [ { "DivMod": { @@ -29972,7 +31308,7 @@ ] ], [ - 14010, + 14739, [ { "DivMod": { @@ -29998,7 +31334,7 @@ ] ], [ - 14019, + 14748, [ { "DivMod": { @@ -30024,7 +31360,7 @@ ] ], [ - 14029, + 14758, [ { "DivMod": { @@ -30050,7 +31386,7 @@ ] ], [ - 14040, + 14769, [ { "DivMod": { @@ -30076,7 +31412,7 @@ ] ], [ - 14049, + 14778, [ { "DivMod": { @@ -30102,7 +31438,7 @@ ] ], [ - 14059, + 14788, [ { "DivMod": { @@ -30128,7 +31464,7 @@ ] ], [ - 14070, + 14799, [ { "DivMod": { @@ -30154,7 +31490,7 @@ ] ], [ - 14079, + 14808, [ { "DivMod": { @@ -30180,7 +31516,7 @@ ] ], [ - 14089, + 14818, [ { "DivMod": { @@ -30206,7 +31542,7 @@ ] ], [ - 14100, + 14829, [ { "DivMod": { @@ -30232,7 +31568,7 @@ ] ], [ - 14109, + 14838, [ { "DivMod": { @@ -30258,7 +31594,7 @@ ] ], [ - 14119, + 14848, [ { "DivMod": { @@ -30284,7 +31620,7 @@ ] ], [ - 14130, + 14859, [ { "DivMod": { @@ -30310,7 +31646,7 @@ ] ], [ - 14157, + 14886, [ { "SystemCall": { @@ -30325,7 +31661,7 @@ ] ], [ - 14174, + 14903, [ { "SystemCall": { @@ -30340,7 +31676,7 @@ ] ], [ - 14186, + 14915, [ { "SystemCall": { @@ -30361,7 +31697,7 @@ ] ], [ - 14197, + 14926, [ { "SystemCall": { @@ -30382,7 +31718,7 @@ ] ], [ - 14207, + 14936, [ { "SystemCall": { @@ -30403,7 +31739,7 @@ ] ], [ - 14292, + 15021, [ { "AllocSegment": { @@ -30416,7 +31752,7 @@ ] ], [ - 14321, + 15050, [ { "DivMod": { @@ -30442,7 +31778,7 @@ ] ], [ - 14331, + 15060, [ { "DivMod": { @@ -30468,7 +31804,7 @@ ] ], [ - 14342, + 15071, [ { "DivMod": { @@ -30494,7 +31830,7 @@ ] ], [ - 14351, + 15080, [ { "DivMod": { @@ -30520,7 +31856,7 @@ ] ], [ - 14361, + 15090, [ { "DivMod": { @@ -30546,7 +31882,7 @@ ] ], [ - 14372, + 15101, [ { "DivMod": { @@ -30572,7 +31908,7 @@ ] ], [ - 14381, + 15110, [ { "AllocSegment": { @@ -30585,7 +31921,7 @@ ] ], [ - 14450, + 15179, [ { "TestLessThan": { @@ -30616,7 +31952,7 @@ ] ], [ - 14465, + 15194, [ { "TestLessThan": { @@ -30638,7 +31974,7 @@ ] ], [ - 14484, + 15213, [ { "TestLessThan": { @@ -30660,7 +31996,7 @@ ] ], [ - 14503, + 15232, [ { "TestLessThan": { @@ -30682,7 +32018,7 @@ ] ], [ - 14513, + 15242, [ { "TestLessThan": { @@ -30704,7 +32040,7 @@ ] ], [ - 14515, + 15244, [ { "DivMod": { @@ -30730,7 +32066,7 @@ ] ], [ - 14552, + 15281, [ { "TestLessThan": { @@ -30752,7 +32088,7 @@ ] ], [ - 14571, + 15300, [ { "AllocSegment": { @@ -30765,7 +32101,7 @@ ] ], [ - 14582, + 15311, [ { "DivMod": { @@ -30794,7 +32130,7 @@ ] ], [ - 14588, + 15317, [ { "TestLessThan": { @@ -30816,7 +32152,7 @@ ] ], [ - 14602, + 15331, [ { "TestLessThan": { @@ -30838,7 +32174,7 @@ ] ], [ - 14616, + 15345, [ { "TestLessThan": { @@ -30860,7 +32196,7 @@ ] ], [ - 14627, + 15356, [ { "TestLessThan": { @@ -30882,7 +32218,7 @@ ] ], [ - 14656, + 15385, [ { "AllocSegment": { @@ -30895,7 +32231,7 @@ ] ], [ - 14681, + 15410, [ { "TestLessThan": { @@ -30917,7 +32253,7 @@ ] ], [ - 14685, + 15414, [ { "LinearSplit": { @@ -30946,7 +32282,7 @@ ] ], [ - 14695, + 15424, [ { "LinearSplit": { @@ -30975,7 +32311,7 @@ ] ], [ - 14715, + 15444, [ { "AllocSegment": { @@ -30988,7 +32324,7 @@ ] ], [ - 14736, + 15465, [ { "AllocSegment": { @@ -31001,7 +32337,7 @@ ] ], [ - 14757, + 15486, [ { "AllocSegment": { @@ -31014,7 +32350,7 @@ ] ], [ - 14777, + 15506, [ { "TestLessThan": { @@ -31036,7 +32372,7 @@ ] ], [ - 14779, + 15508, [ { "DivMod": { @@ -31062,7 +32398,7 @@ ] ], [ - 14823, + 15552, [ { "AllocSegment": { @@ -31075,7 +32411,7 @@ ] ], [ - 14834, + 15563, [ { "DivMod": { @@ -31104,7 +32440,7 @@ ] ], [ - 14840, + 15569, [ { "TestLessThan": { @@ -31126,7 +32462,7 @@ ] ], [ - 14854, + 15583, [ { "TestLessThan": { @@ -31148,7 +32484,7 @@ ] ], [ - 14872, + 15601, [ { "TestLessThan": { @@ -31170,7 +32506,7 @@ ] ], [ - 14885, + 15614, [ { "TestLessThan": { @@ -31192,7 +32528,7 @@ ] ], [ - 14896, + 15625, [ { "TestLessThan": { @@ -31214,7 +32550,7 @@ ] ], [ - 14925, + 15654, [ { "AllocSegment": { @@ -31227,7 +32563,7 @@ ] ], [ - 14950, + 15679, [ { "TestLessThan": { @@ -31249,7 +32585,7 @@ ] ], [ - 14954, + 15683, [ { "LinearSplit": { @@ -31278,7 +32614,7 @@ ] ], [ - 14964, + 15693, [ { "LinearSplit": { @@ -31307,7 +32643,7 @@ ] ], [ - 14984, + 15713, [ { "AllocSegment": { @@ -31320,7 +32656,7 @@ ] ], [ - 15005, + 15734, [ { "AllocSegment": { @@ -31333,7 +32669,7 @@ ] ], [ - 15026, + 15755, [ { "AllocSegment": { @@ -31346,7 +32682,7 @@ ] ], [ - 15055, + 15784, [ { "TestLessThan": { @@ -31368,7 +32704,7 @@ ] ], [ - 15057, + 15786, [ { "DivMod": { @@ -31394,7 +32730,7 @@ ] ], [ - 15094, + 15823, [ { "TestLessThan": { @@ -31416,7 +32752,7 @@ ] ], [ - 15105, + 15834, [ { "TestLessThan": { @@ -31438,7 +32774,7 @@ ] ], [ - 15116, + 15845, [ { "TestLessThan": { @@ -31460,7 +32796,7 @@ ] ], [ - 15145, + 15874, [ { "AllocSegment": { @@ -31473,7 +32809,7 @@ ] ], [ - 15170, + 15899, [ { "TestLessThan": { @@ -31495,7 +32831,7 @@ ] ], [ - 15174, + 15903, [ { "LinearSplit": { @@ -31524,7 +32860,7 @@ ] ], [ - 15184, + 15913, [ { "LinearSplit": { @@ -31553,7 +32889,7 @@ ] ], [ - 15210, + 15939, [ { "AllocSegment": { @@ -31566,7 +32902,7 @@ ] ], [ - 15231, + 15960, [ { "AllocSegment": { @@ -31579,7 +32915,7 @@ ] ], [ - 15253, + 15982, [ { "AllocSegment": { @@ -31592,7 +32928,7 @@ ] ], [ - 15275, + 16004, [ { "TestLessThan": { @@ -31614,7 +32950,7 @@ ] ], [ - 15286, + 16015, [ { "TestLessThan": { @@ -31636,7 +32972,7 @@ ] ], [ - 15315, + 16044, [ { "AllocSegment": { @@ -31649,7 +32985,7 @@ ] ], [ - 15340, + 16069, [ { "TestLessThan": { @@ -31671,7 +33007,7 @@ ] ], [ - 15344, + 16073, [ { "LinearSplit": { @@ -31700,7 +33036,7 @@ ] ], [ - 15354, + 16083, [ { "LinearSplit": { @@ -31729,7 +33065,7 @@ ] ], [ - 15377, + 16106, [ { "AllocSegment": { @@ -31742,7 +33078,7 @@ ] ], [ - 15422, + 16151, [ { "TestLessThan": { @@ -31764,7 +33100,7 @@ ] ], [ - 15433, + 16162, [ { "TestLessThan": { @@ -31786,7 +33122,7 @@ ] ], [ - 15462, + 16191, [ { "AllocSegment": { @@ -31799,7 +33135,7 @@ ] ], [ - 15485, + 16214, [ { "TestLessThan": { @@ -31830,7 +33166,7 @@ ] ], [ - 15509, + 16238, [ { "AllocSegment": { @@ -31843,7 +33179,7 @@ ] ], [ - 15553, + 16282, [ { "AllocSegment": { @@ -31856,7 +33192,7 @@ ] ], [ - 15580, + 16309, [ { "TestLessThanOrEqual": { @@ -31878,7 +33214,7 @@ ] ], [ - 15632, + 16361, [ { "AllocSegment": { @@ -31891,7 +33227,7 @@ ] ], [ - 15689, + 16418, [ { "TestLessThan": { @@ -31919,7 +33255,7 @@ ] ], [ - 15693, + 16422, [ { "LinearSplit": { @@ -31948,7 +33284,7 @@ ] ], [ - 15735, + 16464, [ { "TestLessThan": { @@ -31970,7 +33306,7 @@ ] ], [ - 15737, + 16466, [ { "DivMod": { @@ -31996,7 +33332,7 @@ ] ], [ - 15824, + 16553, [ { "DivMod": { @@ -32025,7 +33361,7 @@ ] ], [ - 15830, + 16559, [ { "TestLessThan": { @@ -32047,7 +33383,7 @@ ] ], [ - 15841, + 16570, [ { "TestLessThan": { @@ -32069,7 +33405,7 @@ ] ], [ - 15851, + 16580, [ { "TestLessThan": { @@ -32091,7 +33427,7 @@ ] ], [ - 15865, + 16594, [ { "DivMod": { @@ -32120,7 +33456,7 @@ ] ], [ - 15871, + 16600, [ { "TestLessThan": { @@ -32142,7 +33478,7 @@ ] ], [ - 15885, + 16614, [ { "TestLessThan": { @@ -32164,7 +33500,7 @@ ] ], [ - 15895, + 16624, [ { "TestLessThan": { @@ -32186,7 +33522,7 @@ ] ], [ - 15917, + 16646, [ { "AllocSegment": { @@ -32199,7 +33535,7 @@ ] ], [ - 15931, + 16660, [ { "AllocSegment": { @@ -32212,7 +33548,7 @@ ] ], [ - 15949, + 16678, [ { "AllocSegment": { @@ -32225,7 +33561,7 @@ ] ], [ - 15963, + 16692, [ { "AllocSegment": { @@ -32238,7 +33574,7 @@ ] ], [ - 15979, + 16708, [ { "TestLessThanOrEqual": { @@ -32260,7 +33596,7 @@ ] ], [ - 16006, + 16735, [ { "TestLessThan": { @@ -32282,7 +33618,7 @@ ] ], [ - 16023, + 16752, [ { "AllocSegment": { @@ -32295,7 +33631,7 @@ ] ], [ - 16048, + 16777, [ { "AllocSegment": { @@ -32308,7 +33644,7 @@ ] ], [ - 16308, + 17037, [ { "SystemCall": { @@ -32323,7 +33659,7 @@ ] ], [ - 16334, + 17063, [ { "SystemCall": { @@ -32338,7 +33674,7 @@ ] ], [ - 16348, + 17077, [ { "U256InvModN": { @@ -32395,7 +33731,7 @@ ] ], [ - 16366, + 17095, [ { "WideMul128": { @@ -32592,7 +33928,7 @@ ] ], [ - 16419, + 17148, [ { "WideMul128": { @@ -32645,7 +33981,7 @@ ] ], [ - 16423, + 17152, [ { "TestLessThan": { @@ -32667,7 +34003,7 @@ ] ], [ - 16437, + 17166, [ { "TestLessThan": { @@ -32689,7 +34025,7 @@ ] ], [ - 16450, + 17179, [ { "DivMod": { @@ -32715,7 +34051,7 @@ ] ], [ - 16460, + 17189, [ { "DivMod": { @@ -32741,7 +34077,7 @@ ] ], [ - 16471, + 17200, [ { "DivMod": { @@ -32767,7 +34103,7 @@ ] ], [ - 16480, + 17209, [ { "DivMod": { @@ -32793,7 +34129,7 @@ ] ], [ - 16490, + 17219, [ { "DivMod": { @@ -32819,7 +34155,7 @@ ] ], [ - 16501, + 17230, [ { "DivMod": { @@ -32845,7 +34181,7 @@ ] ], [ - 16510, + 17239, [ { "DivMod": { @@ -32871,7 +34207,7 @@ ] ], [ - 16520, + 17249, [ { "DivMod": { @@ -32897,7 +34233,7 @@ ] ], [ - 16531, + 17260, [ { "DivMod": { @@ -32923,7 +34259,7 @@ ] ], [ - 16540, + 17269, [ { "DivMod": { @@ -32949,7 +34285,7 @@ ] ], [ - 16550, + 17279, [ { "DivMod": { @@ -32975,7 +34311,7 @@ ] ], [ - 16561, + 17290, [ { "DivMod": { @@ -33001,7 +34337,7 @@ ] ], [ - 16570, + 17299, [ { "DivMod": { @@ -33027,7 +34363,7 @@ ] ], [ - 16580, + 17309, [ { "DivMod": { @@ -33053,7 +34389,7 @@ ] ], [ - 16591, + 17320, [ { "DivMod": { @@ -33079,7 +34415,7 @@ ] ], [ - 16600, + 17329, [ { "DivMod": { @@ -33105,7 +34441,7 @@ ] ], [ - 16610, + 17339, [ { "DivMod": { @@ -33131,7 +34467,7 @@ ] ], [ - 16621, + 17350, [ { "DivMod": { @@ -33157,7 +34493,7 @@ ] ], [ - 16630, + 17359, [ { "DivMod": { @@ -33183,7 +34519,7 @@ ] ], [ - 16640, + 17369, [ { "DivMod": { @@ -33209,7 +34545,7 @@ ] ], [ - 16651, + 17380, [ { "DivMod": { @@ -33235,7 +34571,7 @@ ] ], [ - 16660, + 17389, [ { "DivMod": { @@ -33261,7 +34597,7 @@ ] ], [ - 16670, + 17399, [ { "DivMod": { @@ -33287,7 +34623,7 @@ ] ], [ - 16681, + 17410, [ { "DivMod": { @@ -33313,7 +34649,7 @@ ] ], [ - 16702, + 17431, [ { "Uint512DivModByUint256": { @@ -33382,7 +34718,7 @@ ] ], [ - 16720, + 17449, [ { "WideMul128": { @@ -33507,7 +34843,7 @@ ] ], [ - 16749, + 17478, [ { "TestLessThan": { @@ -33532,7 +34868,7 @@ ] ], [ - 16761, + 17490, [ { "TestLessThan": { @@ -33557,7 +34893,7 @@ ] ], [ - 16776, + 17505, [ { "DivMod": { @@ -33583,7 +34919,7 @@ ] ], [ - 16786, + 17515, [ { "DivMod": { @@ -33609,7 +34945,7 @@ ] ], [ - 16797, + 17526, [ { "DivMod": { @@ -33635,7 +34971,7 @@ ] ], [ - 16806, + 17535, [ { "DivMod": { @@ -33661,7 +34997,7 @@ ] ], [ - 16816, + 17545, [ { "DivMod": { @@ -33687,7 +35023,7 @@ ] ], [ - 16827, + 17556, [ { "DivMod": { @@ -33713,7 +35049,7 @@ ] ], [ - 16836, + 17565, [ { "DivMod": { @@ -33739,7 +35075,7 @@ ] ], [ - 16846, + 17575, [ { "DivMod": { @@ -33765,7 +35101,7 @@ ] ], [ - 16857, + 17586, [ { "DivMod": { @@ -33791,7 +35127,7 @@ ] ], [ - 16866, + 17595, [ { "DivMod": { @@ -33817,7 +35153,7 @@ ] ], [ - 16876, + 17605, [ { "DivMod": { @@ -33843,7 +35179,7 @@ ] ], [ - 16887, + 17616, [ { "DivMod": { @@ -33869,7 +35205,7 @@ ] ], [ - 16896, + 17625, [ { "DivMod": { @@ -33895,7 +35231,7 @@ ] ], [ - 16906, + 17635, [ { "DivMod": { @@ -33921,7 +35257,7 @@ ] ], [ - 16917, + 17646, [ { "DivMod": { @@ -33947,7 +35283,7 @@ ] ], [ - 16929, + 17658, [ { "TestLessThan": { @@ -33969,7 +35305,7 @@ ] ], [ - 16954, + 17683, [ { "TestLessThan": { @@ -33991,7 +35327,7 @@ ] ], [ - 16974, + 17703, [ { "TestLessThan": { @@ -34013,7 +35349,7 @@ ] ], [ - 17010, + 17739, [ { "Uint512DivModByUint256": { @@ -34082,7 +35418,7 @@ ] ], [ - 17028, + 17757, [ { "WideMul128": { @@ -34207,7 +35543,7 @@ ] ], [ - 17057, + 17786, [ { "TestLessThan": { @@ -34232,7 +35568,7 @@ ] ], [ - 17069, + 17798, [ { "TestLessThan": { @@ -34257,7 +35593,7 @@ ] ], [ - 17084, + 17813, [ { "DivMod": { @@ -34283,7 +35619,7 @@ ] ], [ - 17094, + 17823, [ { "DivMod": { @@ -34309,7 +35645,7 @@ ] ], [ - 17105, + 17834, [ { "DivMod": { @@ -34335,7 +35671,7 @@ ] ], [ - 17114, + 17843, [ { "DivMod": { @@ -34361,7 +35697,7 @@ ] ], [ - 17124, + 17853, [ { "DivMod": { @@ -34387,7 +35723,7 @@ ] ], [ - 17135, + 17864, [ { "DivMod": { @@ -34413,7 +35749,7 @@ ] ], [ - 17144, + 17873, [ { "DivMod": { @@ -34439,7 +35775,7 @@ ] ], [ - 17154, + 17883, [ { "DivMod": { @@ -34465,7 +35801,7 @@ ] ], [ - 17165, + 17894, [ { "DivMod": { @@ -34491,7 +35827,7 @@ ] ], [ - 17174, + 17903, [ { "DivMod": { @@ -34517,7 +35853,7 @@ ] ], [ - 17184, + 17913, [ { "DivMod": { @@ -34543,7 +35879,7 @@ ] ], [ - 17195, + 17924, [ { "DivMod": { @@ -34569,7 +35905,7 @@ ] ], [ - 17204, + 17933, [ { "DivMod": { @@ -34595,7 +35931,7 @@ ] ], [ - 17214, + 17943, [ { "DivMod": { @@ -34621,7 +35957,7 @@ ] ], [ - 17225, + 17954, [ { "DivMod": { @@ -34647,7 +35983,7 @@ ] ], [ - 17245, + 17974, [ { "SystemCall": { @@ -34662,7 +35998,7 @@ ] ], [ - 17257, + 17986, [ { "SystemCall": { @@ -34683,7 +36019,7 @@ ] ], [ - 17268, + 17997, [ { "SystemCall": { @@ -34704,7 +36040,7 @@ ] ], [ - 17314, + 18043, [ { "AllocSegment": { @@ -34717,7 +36053,7 @@ ] ], [ - 17330, + 18059, [ { "DivMod": { @@ -34743,7 +36079,7 @@ ] ], [ - 17340, + 18069, [ { "DivMod": { @@ -34769,7 +36105,7 @@ ] ], [ - 17351, + 18080, [ { "DivMod": { @@ -34795,7 +36131,7 @@ ] ], [ - 17360, + 18089, [ { "DivMod": { @@ -34821,7 +36157,7 @@ ] ], [ - 17370, + 18099, [ { "DivMod": { @@ -34847,7 +36183,7 @@ ] ], [ - 17381, + 18110, [ { "DivMod": { @@ -34873,7 +36209,7 @@ ] ], [ - 17390, + 18119, [ { "AllocSegment": { @@ -34886,7 +36222,7 @@ ] ], [ - 17407, + 18136, [ { "AllocSegment": { @@ -34899,7 +36235,7 @@ ] ], [ - 17464, + 18193, [ { "SystemCall": { @@ -34914,7 +36250,7 @@ ] ], [ - 17471, + 18200, [ { "AllocConstantSize": { @@ -34930,7 +36266,7 @@ ] ], [ - 17475, + 18204, [ { "AllocSegment": { @@ -34943,7 +36279,7 @@ ] ], [ - 17510, + 18239, [ { "SystemCall": { @@ -34958,7 +36294,7 @@ ] ], [ - 17583, + 18312, [ { "DivMod": { @@ -34987,7 +36323,7 @@ ] ], [ - 17589, + 18318, [ { "TestLessThan": { @@ -35009,7 +36345,7 @@ ] ], [ - 17656, + 18385, [ { "WideMul128": { @@ -35038,7 +36374,7 @@ ] ], [ - 17658, + 18387, [ { "DivMod": { @@ -35064,7 +36400,7 @@ ] ], [ - 17668, + 18397, [ { "DivMod": { @@ -35090,7 +36426,7 @@ ] ], [ - 17679, + 18408, [ { "DivMod": { @@ -35116,7 +36452,7 @@ ] ], [ - 17688, + 18417, [ { "WideMul128": { @@ -35145,7 +36481,7 @@ ] ], [ - 17690, + 18419, [ { "DivMod": { @@ -35171,7 +36507,7 @@ ] ], [ - 17700, + 18429, [ { "DivMod": { @@ -35197,7 +36533,7 @@ ] ], [ - 17711, + 18440, [ { "DivMod": { @@ -35223,7 +36559,7 @@ ] ], [ - 17721, + 18450, [ { "TestLessThan": { @@ -35245,7 +36581,7 @@ ] ], [ - 17743, + 18472, [ { "WideMul128": { @@ -35274,7 +36610,7 @@ ] ], [ - 17745, + 18474, [ { "DivMod": { @@ -35300,7 +36636,7 @@ ] ], [ - 17755, + 18484, [ { "DivMod": { @@ -35326,7 +36662,7 @@ ] ], [ - 17766, + 18495, [ { "DivMod": { @@ -35352,7 +36688,7 @@ ] ], [ - 17776, + 18505, [ { "TestLessThan": { @@ -35374,7 +36710,7 @@ ] ], [ - 17799, + 18528, [ { "TestLessThan": { @@ -35396,7 +36732,7 @@ ] ], [ - 17821, + 18550, [ { "WideMul128": { @@ -35425,7 +36761,7 @@ ] ], [ - 17823, + 18552, [ { "DivMod": { @@ -35451,7 +36787,7 @@ ] ], [ - 17833, + 18562, [ { "DivMod": { @@ -35477,7 +36813,7 @@ ] ], [ - 17844, + 18573, [ { "DivMod": { @@ -35503,7 +36839,7 @@ ] ], [ - 17854, + 18583, [ { "TestLessThan": { @@ -35525,7 +36861,7 @@ ] ], [ - 17878, + 18607, [ { "TestLessThan": { @@ -35547,7 +36883,7 @@ ] ], [ - 17903, + 18632, [ { "TestLessThan": { @@ -35569,7 +36905,7 @@ ] ], [ - 17927, + 18656, [ { "TestLessThan": { @@ -35591,7 +36927,7 @@ ] ], [ - 18045, + 18774, [ { "AllocSegment": { @@ -35604,7 +36940,7 @@ ] ], [ - 18068, + 18797, [ { "TestLessThanOrEqual": { @@ -35629,7 +36965,7 @@ ] ], [ - 18143, + 18872, [ { "AllocSegment": { @@ -35642,7 +36978,7 @@ ] ], [ - 18198, + 18927, [ { "DivMod": { @@ -35671,7 +37007,7 @@ ] ], [ - 18204, + 18933, [ { "TestLessThan": { @@ -35693,7 +37029,7 @@ ] ], [ - 18217, + 18946, [ { "TestLessThan": { @@ -35715,7 +37051,7 @@ ] ], [ - 18227, + 18956, [ { "TestLessThan": { @@ -35737,7 +37073,7 @@ ] ], [ - 18275, + 19004, [ { "DivMod": { @@ -35766,7 +37102,7 @@ ] ], [ - 18281, + 19010, [ { "TestLessThan": { @@ -35788,7 +37124,7 @@ ] ], [ - 18297, + 19026, [ { "TestLessThan": { @@ -35810,7 +37146,7 @@ ] ], [ - 18307, + 19036, [ { "TestLessThan": { @@ -35832,7 +37168,7 @@ ] ], [ - 18330, + 19059, [ { "AllocSegment": { @@ -35845,7 +37181,7 @@ ] ], [ - 18344, + 19073, [ { "AllocSegment": { @@ -35858,7 +37194,7 @@ ] ], [ - 18363, + 19092, [ { "AllocSegment": { @@ -35871,7 +37207,7 @@ ] ], [ - 18377, + 19106, [ { "AllocSegment": { @@ -35888,14 +37224,14 @@ "EXTERNAL": [ { "selector": "0x1143aa89c8e3ebf8ed14df2a3606c1cd2dd513fac8040b0f8ab441f5c52fe4", - "offset": 3981, + "offset": 4442, "builtins": [ "range_check" ] }, { "selector": "0x3541591104188daef4379e06e92ecce09094a3b381da2e654eb041d00566d8", - "offset": 5511, + "offset": 5972, "builtins": [ "range_check", "range_check96" @@ -35903,35 +37239,35 @@ }, { "selector": "0x3c118a68e16e12e97ed25cb4901c12f4d3162818669cc44c391d8049924c14", - "offset": 1226, + "offset": 1687, "builtins": [ "range_check" ] }, { "selector": "0x5562b3e932b4d139366854d5a2e578382e6a3b6572ac9943d55e7efbe43d00", - "offset": 3383, + "offset": 3844, "builtins": [ "range_check" ] }, { "selector": "0x600c98a299d72ef1e09a2e1503206fbc76081233172c65f7e2438ef0069d8d", - "offset": 4120, + "offset": 4581, "builtins": [ "range_check" ] }, { "selector": "0x62c83572d28cb834a3de3c1e94977a4191469a4a8c26d1d7bc55305e640ed5", - "offset": 3564, + "offset": 4025, "builtins": [ "range_check" ] }, { "selector": "0x679c22735055a10db4f275395763a3752a1e3a3043c192299ab6b574fba8d6", - "offset": 4966, + "offset": 5427, "builtins": [ "range_check", "ec_op" @@ -35939,7 +37275,7 @@ }, { "selector": "0x7772be8b80a8a33dc6c1f9a6ab820c02e537c73e859de67f288c70f92571bb", - "offset": 4611, + "offset": 5072, "builtins": [ "pedersen", "range_check", @@ -35955,21 +37291,21 @@ }, { "selector": "0xe7510edcf6e9f1b70f7bd1f488767b50f0363422f3c563160ab77adf62467b", - "offset": 2107, + "offset": 2568, "builtins": [ "range_check" ] }, { "selector": "0xf818e4530ec36b83dfe702489b4df537308c3b798b0cc120e32c2056d68b7d", - "offset": 2958, + "offset": 3419, "builtins": [ "range_check" ] }, { "selector": "0x10d2fede95e3ec06a875a67219425c27c5bd734d57f1b221d729a2337b6b556", - "offset": 2506, + "offset": 2967, "builtins": [ "range_check", "segment_arena" @@ -35977,36 +37313,43 @@ }, { "selector": "0x12ead94ae9d3f9d2bdb6b847cf255f1f398193a1f88884a0ae8e18f24a037b6", - "offset": 5227, + "offset": 5688, "builtins": [ "range_check" ] }, { "selector": "0x14dae1999ae9ab799bc72def6dc6e90890cf8ac0d64525021b7e71d05cb13e8", - "offset": 712, + "offset": 1173, "builtins": [ "range_check" ] }, { "selector": "0x169f135eddda5ab51886052d777a57f2ea9c162d713691b5e04a6d4ed71d47f", - "offset": 2617, + "offset": 3078, "builtins": [ "range_check" ] }, { "selector": "0x1ae1a515cf2d214b29bdf63a79ee2d490efd4dd1acc99d383a8e549c3cecb5d", - "offset": 5077, + "offset": 5538, "builtins": [ "pedersen", "range_check" ] }, + { + "selector": "0x1e4089d1f1349077b1970f9937c904e27c4582b49a60b6078946dba95bc3c08", + "offset": 1002, + "builtins": [ + "range_check" + ] + }, { "selector": "0x23039bef544cff56442d9f61ae9b13cf9e36fcce009102c5b678aac93f37b36", - "offset": 1048, + "offset": 1509, "builtins": [ "range_check" ] @@ -36020,21 +37363,28 @@ }, { "selector": "0x2d7cf5d5a324a320f9f37804b1615a533fde487400b41af80f13f7ac5581325", - "offset": 2281, + "offset": 2742, "builtins": [ "range_check" ] }, { "selector": "0x30f842021fbf02caf80d09a113997c1e00a32870eee0c6136bed27acb348bea", - "offset": 4759, + "offset": 5220, + "builtins": [ + "range_check" + ] + }, + { + "selector": "0x31401f504973a5e8e1bb41e9c592519e3aa0b8cf6bbfb9c91b532aab8db54b0", + "offset": 712, "builtins": [ "range_check" ] }, { "selector": "0x317eb442b72a9fae758d4fb26830ed0d9f31c8e7da4dbff4e8c59ea6a158e7f", - "offset": 4387, + "offset": 4848, "builtins": [ "pedersen", "range_check" @@ -36042,28 +37392,28 @@ }, { "selector": "0x32564d7e0fe091d49b4c20f4632191e4ed6986bf993849879abfef9465def25", - "offset": 3749, + "offset": 4210, "builtins": [ "range_check" ] }, { "selector": "0x3604cea1cdb094a73a31144f14a3e5861613c008e1e879939ebc4827d10cd50", - "offset": 1515, + "offset": 1976, "builtins": [ "range_check" ] }, { "selector": "0x382be990ca34815134e64a9ac28f41a907c62e5ad10547f97174362ab94dc89", - "offset": 3062, + "offset": 3523, "builtins": [ "range_check" ] }, { "selector": "0x38be5d5f7bf135b52888ba3e440a457d11107aca3f6542e574b016bf3f074d8", - "offset": 3166, + "offset": 3627, "builtins": [ "range_check", "bitwise" @@ -36071,7 +37421,7 @@ }, { "selector": "0x3a6a8bae4c51d5959683ae246347ffdd96aa5b2bfa68cc8c3a6a7c2ed0be331", - "offset": 1806, + "offset": 2267, "builtins": [ "range_check" ] @@ -36085,7 +37435,7 @@ }, { "selector": "0x3d3da80997f8be5d16e9ae7ee6a4b5f7191d60765a1a6c219ab74269c85cf97", - "offset": 5383, + "offset": 5844, "builtins": [ "range_check", "range_check96", @@ -36095,14 +37445,14 @@ }, { "selector": "0x3d95049b565ec2d4197a55108ef03996381d31c84acf392a0a42b28163d69d1", - "offset": 3279, + "offset": 3740, "builtins": [ "range_check" ] }, { "selector": "0x3eb640b15f75fcc06d43182cdb94ed38c8e71755d5fb57c16dd673b466db1d4", - "offset": 3842, + "offset": 4303, "builtins": [ "range_check" ] @@ -36111,14 +37461,14 @@ "L1_HANDLER": [ { "selector": "0x205500a208d0d49d79197fea83cc3f5fde99ac2e1909ae0a5d9f394c0c52ed0", - "offset": 5795, + "offset": 6256, "builtins": [ "range_check" ] }, { "selector": "0x39edbbb129ad752107a94d40c3873cae369a46fd2fc578d075679aa67e85d12", - "offset": 5634, + "offset": 6095, "builtins": [ "range_check" ] @@ -36127,7 +37477,7 @@ "CONSTRUCTOR": [ { "selector": "0x28ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194", - "offset": 6045, + "offset": 6506, "builtins": [ "range_check" ] diff --git a/crates/blockifier/feature_contracts/cairo1/test_contract.cairo b/crates/blockifier/feature_contracts/cairo1/test_contract.cairo index 604c9f278bf..5ca7fa376be 100644 --- a/crates/blockifier/feature_contracts/cairo1/test_contract.cairo +++ b/crates/blockifier/feature_contracts/cairo1/test_contract.cairo @@ -71,6 +71,41 @@ mod TestContract { .span() } + + #[external(v0)] + fn test_call_contract_revert( + ref self: ContractState, + contract_address: ContractAddress, + entry_point_selector: felt252, + calldata: Array:: + ) { + match syscalls::call_contract_syscall( + contract_address, entry_point_selector, calldata.span()) + { + Result::Ok(_) => panic!("Expected revert"), + Result::Err(errors) => { + let mut error_span = errors.span(); + assert( + *error_span.pop_back().unwrap() == 'ENTRYPOINT_FAILED', + 'Unexpected error', + ); + }, + }; + // TODO(Yoni, 1/12/2024): test replace class once get_class_hash_at syscall is supported. + assert(self.my_storage_var.read() == 0, 'values should not change.'); + } + + + #[external(v0)] + fn test_revert_helper(ref self: ContractState, class_hash: ClassHash) { + let dummy_span = array![0].span(); + syscalls::emit_event_syscall(dummy_span, dummy_span).unwrap_syscall(); + syscalls::replace_class_syscall(class_hash).unwrap_syscall(); + syscalls::send_message_to_l1_syscall(17.try_into().unwrap(), dummy_span).unwrap_syscall(); + self.my_storage_var.write(17); + panic!("test_revert_helper"); + } + #[external(v0)] fn test_emit_events( self: @ContractState, events_number: u64, keys: Array::, data: Array:: diff --git a/crates/blockifier/resources/versioned_constants_13_0.json b/crates/blockifier/resources/versioned_constants_0_13_0.json similarity index 99% rename from crates/blockifier/resources/versioned_constants_13_0.json rename to crates/blockifier/resources/versioned_constants_0_13_0.json index ad5fb53de28..def515b556d 100644 --- a/crates/blockifier/resources/versioned_constants_13_0.json +++ b/crates/blockifier/resources/versioned_constants_0_13_0.json @@ -186,6 +186,8 @@ }, "error_block_number_out_of_range": "Block number out of range", "error_out_of_gas": "Out of gas", + "error_entry_point_failed": "ENTRYPOINT_FAILED", + "error_entry_point_not_found": "ENTRYPOINT_NOT_FOUND", "error_invalid_input_len": "Invalid input length", "error_invalid_argument": "Invalid argument", "validated": "VALID", diff --git a/crates/blockifier/resources/versioned_constants_13_1.json b/crates/blockifier/resources/versioned_constants_0_13_1.json similarity index 99% rename from crates/blockifier/resources/versioned_constants_13_1.json rename to crates/blockifier/resources/versioned_constants_0_13_1.json index 220fc50df36..cb1043c2c34 100644 --- a/crates/blockifier/resources/versioned_constants_13_1.json +++ b/crates/blockifier/resources/versioned_constants_0_13_1.json @@ -188,6 +188,8 @@ }, "error_block_number_out_of_range": "Block number out of range", "error_out_of_gas": "Out of gas", + "error_entry_point_failed": "ENTRYPOINT_FAILED", + "error_entry_point_not_found": "ENTRYPOINT_NOT_FOUND", "error_invalid_input_len": "Invalid input length", "error_invalid_argument": "Invalid argument", "validated": "VALID", diff --git a/crates/blockifier/resources/versioned_constants_13_1_1.json b/crates/blockifier/resources/versioned_constants_0_13_1_1.json similarity index 99% rename from crates/blockifier/resources/versioned_constants_13_1_1.json rename to crates/blockifier/resources/versioned_constants_0_13_1_1.json index 8e4fb58be36..3ac05908519 100644 --- a/crates/blockifier/resources/versioned_constants_13_1_1.json +++ b/crates/blockifier/resources/versioned_constants_0_13_1_1.json @@ -188,6 +188,8 @@ }, "error_block_number_out_of_range": "Block number out of range", "error_out_of_gas": "Out of gas", + "error_entry_point_failed": "ENTRYPOINT_FAILED", + "error_entry_point_not_found": "ENTRYPOINT_NOT_FOUND", "error_invalid_input_len": "Invalid input length", "error_invalid_argument": "Invalid argument", "validated": "VALID", diff --git a/crates/blockifier/resources/versioned_constants_13_2.json b/crates/blockifier/resources/versioned_constants_0_13_2.json similarity index 99% rename from crates/blockifier/resources/versioned_constants_13_2.json rename to crates/blockifier/resources/versioned_constants_0_13_2.json index bdf25c399da..bb2f2816550 100644 --- a/crates/blockifier/resources/versioned_constants_13_2.json +++ b/crates/blockifier/resources/versioned_constants_0_13_2.json @@ -73,6 +73,8 @@ "error_invalid_input_len": "Invalid input length", "error_invalid_argument": "Invalid argument", "error_out_of_gas": "Out of gas", + "error_entry_point_failed": "ENTRYPOINT_FAILED", + "error_entry_point_not_found": "ENTRYPOINT_NOT_FOUND", "execute_entry_point_selector": "0x15d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad", "fee_transfer_gas_cost": { "entry_point_gas_cost": 1, diff --git a/crates/blockifier/resources/versioned_constants_13_2_1.json b/crates/blockifier/resources/versioned_constants_0_13_2_1.json similarity index 99% rename from crates/blockifier/resources/versioned_constants_13_2_1.json rename to crates/blockifier/resources/versioned_constants_0_13_2_1.json index 1a84226aa51..9d901715016 100644 --- a/crates/blockifier/resources/versioned_constants_13_2_1.json +++ b/crates/blockifier/resources/versioned_constants_0_13_2_1.json @@ -73,6 +73,8 @@ "error_invalid_input_len": "Invalid input length", "error_invalid_argument": "Invalid argument", "error_out_of_gas": "Out of gas", + "error_entry_point_failed": "ENTRYPOINT_FAILED", + "error_entry_point_not_found": "ENTRYPOINT_NOT_FOUND", "execute_entry_point_selector": "0x15d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad", "fee_transfer_gas_cost": { "entry_point_gas_cost": 1, diff --git a/crates/blockifier/resources/versioned_constants.json b/crates/blockifier/resources/versioned_constants_0_13_3.json similarity index 97% rename from crates/blockifier/resources/versioned_constants.json rename to crates/blockifier/resources/versioned_constants_0_13_3.json index a944b28b882..f031928371a 100644 --- a/crates/blockifier/resources/versioned_constants.json +++ b/crates/blockifier/resources/versioned_constants_0_13_3.json @@ -74,6 +74,8 @@ "error_invalid_input_len": "Invalid input length", "error_invalid_argument": "Invalid argument", "error_out_of_gas": "Out of gas", + "error_entry_point_failed": "ENTRYPOINT_FAILED", + "error_entry_point_not_found": "ENTRYPOINT_NOT_FOUND", "execute_entry_point_selector": "0x15d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad", "get_block_hash_gas_cost": { "step_gas_cost": 104, @@ -206,7 +208,7 @@ "os_resources": { "execute_syscalls": { "CallContract": { - "n_steps": 860, + "n_steps": 866, "builtin_instance_counter": { "range_check_builtin": 15 }, @@ -227,7 +229,7 @@ "n_memory_holes": 0 }, "Deploy": { - "n_steps": 1130, + "n_steps": 1132, "builtin_instance_counter": { "pedersen_builtin": 7, "range_check_builtin": 18 @@ -306,7 +308,7 @@ "n_memory_holes": 0 }, "LibraryCall": { - "n_steps": 836, + "n_steps": 842, "builtin_instance_counter": { "range_check_builtin": 15 }, @@ -404,7 +406,7 @@ "n_memory_holes": 0 }, "Sha256ProcessBlock": { - "n_steps": 1855, + "n_steps": 1865, "builtin_instance_counter": { "range_check_builtin": 65, "bitwise_builtin": 1115 @@ -430,7 +432,7 @@ "Declare": { "deprecated_resources": { "constant": { - "n_steps": 3201, + "n_steps": 3203, "builtin_instance_counter": { "pedersen_builtin": 16, "range_check_builtin": 56, @@ -446,7 +448,7 @@ }, "resources": { "constant": { - "n_steps": 3344, + "n_steps": 3346, "builtin_instance_counter": { "pedersen_builtin": 4, "range_check_builtin": 64, @@ -464,7 +466,7 @@ "DeployAccount": { "deprecated_resources": { "constant": { - "n_steps": 4157, + "n_steps": 4161, "builtin_instance_counter": { "pedersen_builtin": 23, "range_check_builtin": 72 @@ -481,7 +483,7 @@ }, "resources": { "constant": { - "n_steps": 4317, + "n_steps": 4321, "builtin_instance_counter": { "pedersen_builtin": 11, "range_check_builtin": 80, @@ -501,7 +503,7 @@ "InvokeFunction": { "deprecated_resources": { "constant": { - "n_steps": 3914, + "n_steps": 3918, "builtin_instance_counter": { "pedersen_builtin": 14, "range_check_builtin": 69 @@ -518,7 +520,7 @@ }, "resources": { "constant": { - "n_steps": 4098, + "n_steps": 4102, "builtin_instance_counter": { "pedersen_builtin": 4, "range_check_builtin": 77, @@ -538,7 +540,7 @@ "L1Handler": { "deprecated_resources": { "constant": { - "n_steps": 1277, + "n_steps": 1279, "builtin_instance_counter": { "pedersen_builtin": 11, "range_check_builtin": 16 diff --git a/crates/blockifier/src/abi/abi_utils_test.rs b/crates/blockifier/src/abi/abi_utils_test.rs index 05ed2eb3c0a..7c3ad40eca4 100644 --- a/crates/blockifier/src/abi/abi_utils_test.rs +++ b/crates/blockifier/src/abi/abi_utils_test.rs @@ -6,7 +6,7 @@ use starknet_types_core::felt::Felt; use crate::abi::abi_utils::selector_from_name; use crate::abi::constants as abi_constants; use crate::abi::sierra_types::felt_to_u128; -use crate::transaction::constants as transaction_constants; +use crate::transaction::constants as tx_constants; #[test] fn test_selector_from_name() { @@ -27,7 +27,7 @@ fn test_selector_from_name() { "0x15d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad"; let expected_execute_selector = EntryPointSelector(felt!(expected_execute_selector)); assert_eq!( - selector_from_name(transaction_constants::EXECUTE_ENTRY_POINT_NAME), + selector_from_name(tx_constants::EXECUTE_ENTRY_POINT_NAME), expected_execute_selector ); diff --git a/crates/blockifier/src/blockifier/stateful_validator.rs b/crates/blockifier/src/blockifier/stateful_validator.rs index 06e92eb5be2..9d7a66929db 100644 --- a/crates/blockifier/src/blockifier/stateful_validator.rs +++ b/crates/blockifier/src/blockifier/stateful_validator.rs @@ -84,7 +84,7 @@ impl StatefulValidator { } fn execute(&mut self, tx: AccountTransaction) -> StatefulValidatorResult<()> { - self.tx_executor.execute(&Transaction::AccountTransaction(tx))?; + self.tx_executor.execute(&Transaction::Account(tx))?; Ok(()) } @@ -135,7 +135,7 @@ impl StatefulValidator { &execution_resources, CallInfo::summarize_many(validate_call_info.iter()), 0, - )?; + ); Ok((validate_call_info, tx_receipt)) } diff --git a/crates/blockifier/src/blockifier/stateful_validator_test.rs b/crates/blockifier/src/blockifier/stateful_validator_test.rs index 6e0ff43f524..66899156099 100644 --- a/crates/blockifier/src/blockifier/stateful_validator_test.rs +++ b/crates/blockifier/src/blockifier/stateful_validator_test.rs @@ -28,7 +28,7 @@ use crate::transaction::transaction_types::TransactionType; #[case::validate_deploy_version_3(TransactionType::DeployAccount, false, TransactionVersion::THREE)] #[case::constructor_version_1(TransactionType::DeployAccount, true, TransactionVersion::ONE)] #[case::constructor_version_3(TransactionType::DeployAccount, true, TransactionVersion::THREE)] -fn test_transaction_validator( +fn test_tx_validator( #[case] tx_type: TransactionType, #[case] validate_constructor: bool, #[case] tx_version: TransactionVersion, @@ -46,7 +46,7 @@ fn test_transaction_validator( let mut state = test_state(chain_info, account_balance, &[(faulty_account, 1)]); - let transaction_args = FaultyAccountTxCreatorArgs { + let tx_args = FaultyAccountTxCreatorArgs { tx_type, tx_version, sender_address, @@ -61,7 +61,7 @@ fn test_transaction_validator( // Positive flow. let tx = create_account_tx_for_validate_test_nonce_0(FaultyAccountTxCreatorArgs { scenario: VALID, - ..transaction_args + ..tx_args }); if let AccountTransaction::DeployAccount(deploy_tx) = &tx { fund_account(chain_info, deploy_tx.contract_address(), BALANCE, &mut state.state); @@ -75,7 +75,7 @@ fn test_transaction_validator( } #[rstest] -fn test_transaction_validator_skip_validate(max_l1_resource_bounds: ValidResourceBounds) { +fn test_tx_validator_skip_validate(max_l1_resource_bounds: ValidResourceBounds) { let block_context = BlockContext::create_for_testing(); let faulty_account = FeatureContract::FaultyAccount(CairoVersion::Cairo1); let state = test_state(&block_context.chain_info, BALANCE, &[(faulty_account, 1)]); diff --git a/crates/blockifier/src/blockifier/transaction_executor_test.rs b/crates/blockifier/src/blockifier/transaction_executor_test.rs index 0c293c96352..5cf8f132606 100644 --- a/crates/blockifier/src/blockifier/transaction_executor_test.rs +++ b/crates/blockifier/src/blockifier/transaction_executor_test.rs @@ -1,6 +1,7 @@ use assert_matches::assert_matches; use pretty_assertions::assert_eq; use rstest::rstest; +use starknet_api::execution_resources::GasAmount; use starknet_api::test_utils::NonceManager; use starknet_api::transaction::{Fee, TransactionVersion}; use starknet_api::{declare_tx_args, deploy_account_tx_args, felt, invoke_tx_args, nonce}; @@ -22,7 +23,6 @@ use crate::test_utils::declare::declare_tx; use crate::test_utils::deploy_account::deploy_account_tx; use crate::test_utils::initial_test_state::test_state; use crate::test_utils::{create_calldata, CairoVersion, BALANCE, DEFAULT_STRK_L1_GAS_PRICE}; -use crate::transaction::account_transaction::AccountTransaction; use crate::transaction::errors::TransactionExecutionError; use crate::transaction::test_utils::{ account_invoke_tx, @@ -64,7 +64,7 @@ fn tx_executor_test_body( } #[rstest] -#[case::transaction_version_0( +#[case::tx_version_0( TransactionVersion::ZERO, CairoVersion::Cairo0, BouncerWeights { @@ -74,7 +74,7 @@ fn tx_executor_test_body( ..Default::default() } )] -#[case::transaction_version_1( +#[case::tx_version_1( TransactionVersion::ONE, CairoVersion::Cairo0, BouncerWeights { @@ -84,7 +84,7 @@ fn tx_executor_test_body( ..Default::default() } )] -#[case::transaction_version_2( +#[case::tx_version_2( TransactionVersion::TWO, CairoVersion::Cairo1, BouncerWeights { @@ -94,7 +94,7 @@ fn tx_executor_test_body( ..Default::default() } )] -#[case::transaction_version_3( +#[case::tx_version_3( TransactionVersion::THREE, CairoVersion::Cairo1, BouncerWeights { @@ -107,7 +107,7 @@ fn tx_executor_test_body( fn test_declare( block_context: BlockContext, #[values(CairoVersion::Cairo0, CairoVersion::Cairo1)] account_cairo_version: CairoVersion, - #[case] transaction_version: TransactionVersion, + #[case] tx_version: TransactionVersion, #[case] cairo_version: CairoVersion, #[case] expected_bouncer_weights: BouncerWeights, ) { @@ -115,16 +115,17 @@ fn test_declare( let declared_contract = FeatureContract::Empty(cairo_version); let state = test_state(&block_context.chain_info, BALANCE, &[(account_contract, 1)]); - let tx = Transaction::AccountTransaction(declare_tx( + let tx = declare_tx( declare_tx_args! { sender_address: account_contract.get_instance_address(0), class_hash: declared_contract.get_class_hash(), compiled_class_hash: declared_contract.get_compiled_class_hash(), - version: transaction_version, - resource_bounds: l1_resource_bounds(0, DEFAULT_STRK_L1_GAS_PRICE), + version: tx_version, + resource_bounds: l1_resource_bounds(GasAmount(0), DEFAULT_STRK_L1_GAS_PRICE), }, calculate_class_info_for_testing(declared_contract.get_class()), - )); + ) + .into(); tx_executor_test_body(state, block_context, tx, expected_bouncer_weights); } @@ -137,14 +138,15 @@ fn test_deploy_account( let account_contract = FeatureContract::AccountWithoutValidations(cairo_version); let state = test_state(&block_context.chain_info, BALANCE, &[(account_contract, 0)]); - let tx = Transaction::AccountTransaction(AccountTransaction::DeployAccount(deploy_account_tx( + let tx = deploy_account_tx( deploy_account_tx_args! { class_hash: account_contract.get_class_hash(), - resource_bounds: l1_resource_bounds(0, DEFAULT_STRK_L1_GAS_PRICE), + resource_bounds: l1_resource_bounds(GasAmount(0), DEFAULT_STRK_L1_GAS_PRICE), version, }, &mut NonceManager::default(), - ))); + ) + .into(); let expected_bouncer_weights = BouncerWeights { state_diff_size: 3, message_segment_length: 0, @@ -210,11 +212,12 @@ fn test_invoke( let calldata = create_calldata(test_contract.get_instance_address(0), entry_point_name, &entry_point_args); - let tx = Transaction::AccountTransaction(account_invoke_tx(invoke_tx_args! { + let tx = account_invoke_tx(invoke_tx_args! { sender_address: account_contract.get_instance_address(0), calldata, version, - })); + }) + .into(); tx_executor_test_body(state, block_context, tx, expected_bouncer_weights); } @@ -223,7 +226,7 @@ fn test_l1_handler(block_context: BlockContext) { let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1); let state = test_state(&block_context.chain_info, BALANCE, &[(test_contract, 1)]); - let tx = Transaction::L1HandlerTransaction(L1HandlerTransaction::create_for_testing( + let tx = Transaction::L1Handler(L1HandlerTransaction::create_for_testing( Fee(1908000000000000), test_contract.get_instance_address(0), )); @@ -265,12 +268,15 @@ fn test_bouncing(#[case] initial_bouncer_weights: BouncerWeights, #[case] n_even tx_executor.bouncer.set_accumulated_weights(initial_bouncer_weights); tx_executor - .execute(&Transaction::AccountTransaction(emit_n_events_tx( - n_events, - account_address, - contract_address, - nonce_manager.next(account_address), - ))) + .execute( + &emit_n_events_tx( + n_events, + account_address, + contract_address, + nonce_manager.next(account_address), + ) + .into(), + ) .map_err(|error| panic!("{error:?}: {error}")) .unwrap(); } @@ -302,7 +308,7 @@ fn test_execute_txs_bouncing() { emit_n_events_tx(1, account_address, contract_address, nonce!(3_u32)), ] .into_iter() - .map(Transaction::AccountTransaction) + .map(Transaction::Account) .collect(); // Run. diff --git a/crates/blockifier/src/bouncer.rs b/crates/blockifier/src/bouncer.rs index b13ee81caf1..4e778f94c25 100644 --- a/crates/blockifier/src/bouncer.rs +++ b/crates/blockifier/src/bouncer.rs @@ -296,7 +296,7 @@ pub fn get_tx_weights( state_changes_keys: &StateChangesKeys, ) -> TransactionExecutionResult { let message_resources = &tx_resources.starknet_resources.messages; - let message_starknet_gas = usize_from_u128(message_resources.get_starknet_gas_cost().l1_gas) + let message_starknet_gas = usize_from_u128(message_resources.get_starknet_gas_cost().l1_gas.0) .expect("This conversion should not fail as the value is a converted usize."); let mut additional_os_resources = get_casm_hash_calculation_resources(state_reader, executed_class_hashes)?; diff --git a/crates/blockifier/src/concurrency/scheduler_test.rs b/crates/blockifier/src/concurrency/scheduler_test.rs index d2cf9b440e3..29499cba0c3 100644 --- a/crates/blockifier/src/concurrency/scheduler_test.rs +++ b/crates/blockifier/src/concurrency/scheduler_test.rs @@ -87,8 +87,8 @@ fn test_next_task( #[rstest] #[case::happy_flow(0, TransactionStatus::Executed, false)] #[case::happy_flow_with_halt(0, TransactionStatus::Executed, true)] -#[case::last_transaction_to_commit(DEFAULT_CHUNK_SIZE - 1, TransactionStatus::Executed, false)] -#[case::last_transaction_to_commit_with_halt(DEFAULT_CHUNK_SIZE - 1, TransactionStatus::Executed, true)] +#[case::last_tx_to_commit(DEFAULT_CHUNK_SIZE - 1, TransactionStatus::Executed, false)] +#[case::last_tx_to_commit_with_halt(DEFAULT_CHUNK_SIZE - 1, TransactionStatus::Executed, true)] #[case::wrong_status_ready(0, TransactionStatus::ReadyToExecute, false)] #[case::wrong_status_executing(0, TransactionStatus::Executing, false)] #[case::wrong_status_aborting(0, TransactionStatus::Aborting, false)] @@ -100,16 +100,16 @@ fn test_commit_flow( ) { let scheduler = default_scheduler!(chunk_size: DEFAULT_CHUNK_SIZE, commit_index: commit_index); scheduler.set_tx_status(commit_index, commit_index_tx_status); - let mut transaction_committer = scheduler.try_enter_commit_phase().unwrap(); + let mut tx_committer = scheduler.try_enter_commit_phase().unwrap(); // Lock is already acquired. assert!(scheduler.try_enter_commit_phase().is_none()); - if let Some(index) = transaction_committer.try_commit() { + if let Some(index) = tx_committer.try_commit() { assert_eq!(index, commit_index); } if should_halt { - transaction_committer.halt_scheduler(); + tx_committer.halt_scheduler(); } - drop(transaction_committer); + drop(tx_committer); if commit_index_tx_status == TransactionStatus::Executed { assert_eq!(*scheduler.lock_tx_status(commit_index), TransactionStatus::Committed); assert_eq!( diff --git a/crates/blockifier/src/concurrency/versioned_state_test.rs b/crates/blockifier/src/concurrency/versioned_state_test.rs index 1093535a693..8932df1907e 100644 --- a/crates/blockifier/src/concurrency/versioned_state_test.rs +++ b/crates/blockifier/src/concurrency/versioned_state_test.rs @@ -238,7 +238,7 @@ fn test_run_parallel_txs(max_l1_resource_bounds: ValidResourceBounds) { let deploy_account_tx_1 = deploy_account_tx( deploy_account_tx_args! { class_hash: account_without_validation.get_class_hash(), - resource_bounds: l1_resource_bounds(u64::from(!zero_bounds), DEFAULT_STRK_L1_GAS_PRICE), + resource_bounds: l1_resource_bounds(u128::from(!zero_bounds).into(), DEFAULT_STRK_L1_GAS_PRICE), }, &mut NonceManager::default(), ); diff --git a/crates/blockifier/src/concurrency/worker_logic.rs b/crates/blockifier/src/concurrency/worker_logic.rs index e30fa84a359..425b810985f 100644 --- a/crates/blockifier/src/concurrency/worker_logic.rs +++ b/crates/blockifier/src/concurrency/worker_logic.rs @@ -110,11 +110,11 @@ impl<'a, S: StateReader> WorkerExecutor<'a, S> { } fn commit_while_possible(&self) { - if let Some(mut transaction_committer) = self.scheduler.try_enter_commit_phase() { - while let Some(tx_index) = transaction_committer.try_commit() { + if let Some(mut tx_committer) = self.scheduler.try_enter_commit_phase() { + while let Some(tx_index) = tx_committer.try_commit() { let commit_succeeded = self.commit_tx(tx_index); if !commit_succeeded { - transaction_committer.halt_scheduler(); + tx_committer.halt_scheduler(); } } } diff --git a/crates/blockifier/src/concurrency/worker_logic_test.rs b/crates/blockifier/src/concurrency/worker_logic_test.rs index e4d1d7b76d3..27f55b0323d 100644 --- a/crates/blockifier/src/concurrency/worker_logic_test.rs +++ b/crates/blockifier/src/concurrency/worker_logic_test.rs @@ -111,7 +111,7 @@ pub fn test_commit_tx() { trivial_calldata_invoke_tx(account_address, test_contract_address, nonce!(10_u8)), ] .into_iter() - .map(Transaction::AccountTransaction) + .map(Transaction::Account) .collect::>(); let mut bouncer = Bouncer::new(block_context.bouncer_config.clone()); let cached_state = @@ -207,7 +207,7 @@ fn test_commit_tx_when_sender_is_sequencer() { let (sequencer_balance_key_low, sequencer_balance_key_high) = get_sequencer_balance_keys(&block_context); - let sequencer_tx = [Transaction::AccountTransaction(trivial_calldata_invoke_tx( + let sequencer_tx = [Transaction::Account(trivial_calldata_invoke_tx( account_address, test_contract_address, nonce!(0_u8), @@ -320,7 +320,7 @@ fn test_worker_execute(max_l1_resource_bounds: ValidResourceBounds) { let txs = [tx_success, tx_failure, tx_revert] .into_iter() - .map(Transaction::AccountTransaction) + .map(Transaction::Account) .collect::>(); let mut bouncer = Bouncer::new(block_context.bouncer_config.clone()); @@ -482,7 +482,7 @@ fn test_worker_validate(max_l1_resource_bounds: ValidResourceBounds) { let txs = [account_tx0, account_tx1] .into_iter() - .map(Transaction::AccountTransaction) + .map(Transaction::Account) .collect::>(); let mut bouncer = Bouncer::new(block_context.bouncer_config.clone()); @@ -593,10 +593,8 @@ fn test_deploy_before_declare( nonce: nonce!(0_u8) }); - let txs = [declare_tx, invoke_tx] - .into_iter() - .map(Transaction::AccountTransaction) - .collect::>(); + let txs = + [declare_tx, invoke_tx].into_iter().map(Transaction::Account).collect::>(); let mut bouncer = Bouncer::new(block_context.bouncer_config.clone()); let worker_executor = @@ -661,7 +659,7 @@ fn test_worker_commit_phase(max_l1_resource_bounds: ValidResourceBounds) { let txs = (0..3) .map(|_| { - Transaction::AccountTransaction(account_invoke_tx(invoke_tx_args! { + Transaction::Account(account_invoke_tx(invoke_tx_args! { sender_address, calldata: calldata.clone(), resource_bounds: max_l1_resource_bounds, @@ -751,7 +749,7 @@ fn test_worker_commit_phase_with_halt() { let txs = (0..2) .map(|_| { - Transaction::AccountTransaction(emit_n_events_tx( + Transaction::Account(emit_n_events_tx( n_events, sender_address, test_contract_address, diff --git a/crates/blockifier/src/context.rs b/crates/blockifier/src/context.rs index 8d8aec27a31..253ea3a4eec 100644 --- a/crates/blockifier/src/context.rs +++ b/crates/blockifier/src/context.rs @@ -4,11 +4,10 @@ use papyrus_config::dumping::{append_sub_config_name, ser_param, SerializeConfig use papyrus_config::{ParamPath, ParamPrivacyInput, SerializedParam}; use serde::{Deserialize, Serialize}; use starknet_api::core::{ChainId, ContractAddress}; -use starknet_api::transaction::ValidResourceBounds; +use starknet_api::transaction::{GasVectorComputationMode, ValidResourceBounds}; use crate::blockifier::block::BlockInfo; use crate::bouncer::BouncerConfig; -use crate::fee::resources::GasVectorComputationMode; use crate::transaction::objects::{ FeeType, HasRelatedFeeType, diff --git a/crates/blockifier/src/execution/call_info.rs b/crates/blockifier/src/execution/call_info.rs index f6a19d2f3ac..69fc1602112 100644 --- a/crates/blockifier/src/execution/call_info.rs +++ b/crates/blockifier/src/execution/call_info.rs @@ -9,6 +9,7 @@ use starknet_api::state::StorageKey; use starknet_api::transaction::{EventContent, L2ToL1Payload}; use starknet_types_core::felt::Felt; +use crate::execution::contract_class::TrackedResource; use crate::execution::entry_point::CallEntryPoint; use crate::state::cached_state::StorageEntry; use crate::utils::u128_from_usize; @@ -102,6 +103,7 @@ pub struct CallInfo { pub execution: CallExecution, pub resources: ExecutionResources, pub inner_calls: Vec, + pub tracked_resource: TrackedResource, // Additional information gathered during execution. pub storage_read_values: Vec, diff --git a/crates/blockifier/src/execution/contract_class.rs b/crates/blockifier/src/execution/contract_class.rs index 8a271ea8a76..bab8d188731 100644 --- a/crates/blockifier/src/execution/contract_class.rs +++ b/crates/blockifier/src/execution/contract_class.rs @@ -11,6 +11,8 @@ use cairo_lang_starknet_classes::casm_contract_class::{ }; use cairo_lang_starknet_classes::NestedIntList; use cairo_lang_utils::bigint::BigUintAsHex; +#[allow(unused_imports)] +use cairo_native::executor::AotNativeExecutor; use cairo_vm::serde::deserialize_program::{ ApTracking, FlowTrackingData, @@ -53,8 +55,9 @@ pub mod test; pub type ContractClassResult = Result; /// The resource used to run a contract function. +#[cfg_attr(feature = "transaction_serde", derive(serde::Deserialize))] #[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Serialize)] -pub enum TrackingResource { +pub enum TrackedResource { #[default] CairoSteps, // AKA VM mode. SierraGas, // AKA Sierra mode. @@ -118,11 +121,11 @@ impl ContractClass { } /// Returns whether this contract should run using Cairo steps or Sierra gas. - pub fn tracking_resource(&self, min_sierra_version: &CompilerVersion) -> TrackingResource { + pub fn tracked_resource(&self, min_sierra_version: &CompilerVersion) -> TrackedResource { match self { - ContractClass::V0(_) => TrackingResource::CairoSteps, + ContractClass::V0(_) => TrackedResource::CairoSteps, ContractClass::V1(contract_class) => { - contract_class.tracking_resource(min_sierra_version) + contract_class.tracked_resource(min_sierra_version) } } } @@ -177,8 +180,8 @@ impl ContractClassV0 { } } - pub fn tracking_resource(&self) -> TrackingResource { - TrackingResource::CairoSteps + pub fn tracked_resource(&self) -> TrackedResource { + TrackedResource::CairoSteps } pub fn try_from_json_string(raw_contract_class: &str) -> Result { @@ -260,11 +263,11 @@ impl ContractClassV1 { } /// Returns whether this contract should run using Cairo steps or Sierra gas. - pub fn tracking_resource(&self, min_sierra_version: &CompilerVersion) -> TrackingResource { + pub fn tracked_resource(&self, min_sierra_version: &CompilerVersion) -> TrackedResource { if *min_sierra_version <= self.compiler_version { - TrackingResource::SierraGas + TrackedResource::SierraGas } else { - TrackingResource::CairoSteps + TrackedResource::CairoSteps } } @@ -422,7 +425,7 @@ impl TryFrom for ContractClassV1 { type Error = ProgramError; fn try_from(class: CasmContractClass) -> Result { - try_from_casm_contrcat_class_internal( + try_from_casm_contract_class_internal( &class.bytecode, &class.hints, &class.entry_points_by_type, @@ -436,7 +439,7 @@ impl TryFrom<&CasmContractClass> for ContractClassV1 { type Error = ProgramError; fn try_from(class: &CasmContractClass) -> Result { - try_from_casm_contrcat_class_internal( + try_from_casm_contract_class_internal( &class.bytecode, &class.hints, &class.entry_points_by_type, @@ -459,7 +462,7 @@ pub fn deserialize_program<'de, D: Deserializer<'de>>( // V1 utilities. -fn try_from_casm_contrcat_class_internal( +fn try_from_casm_contract_class_internal( bytecode: &[BigUintAsHex], casm_class_hints: &[(usize, Vec)], casm_class_entry_points_by_type: &CasmContractEntryPoints, diff --git a/crates/blockifier/src/execution/deprecated_entry_point_execution.rs b/crates/blockifier/src/execution/deprecated_entry_point_execution.rs index c5021fe5fb9..3cc4ca8835a 100644 --- a/crates/blockifier/src/execution/deprecated_entry_point_execution.rs +++ b/crates/blockifier/src/execution/deprecated_entry_point_execution.rs @@ -13,7 +13,7 @@ use super::execution_utils::SEGMENT_ARENA_BUILTIN_SIZE; use crate::abi::abi_utils::selector_from_name; use crate::abi::constants::{CONSTRUCTOR_ENTRY_POINT_NAME, DEFAULT_ENTRY_POINT_SELECTOR}; use crate::execution::call_info::{CallExecution, CallInfo}; -use crate::execution::contract_class::ContractClassV0; +use crate::execution::contract_class::{ContractClassV0, TrackedResource}; use crate::execution::deprecated_syscalls::hint_processor::DeprecatedSyscallHintProcessor; use crate::execution::entry_point::{ CallEntryPoint, @@ -265,8 +265,8 @@ pub fn finalize_execution( } *syscall_handler.resources += &vm_resources_without_inner_calls; // Take into account the syscall resources of the current call. - *syscall_handler.resources += &versioned_constants - .get_additional_os_syscall_resources(&syscall_handler.syscall_counter)?; + *syscall_handler.resources += + &versioned_constants.get_additional_os_syscall_resources(&syscall_handler.syscall_counter); let full_call_resources = &*syscall_handler.resources - &previous_resources; Ok(CallInfo { @@ -280,6 +280,7 @@ pub fn finalize_execution( }, resources: full_call_resources.filter_unused_builtins(), inner_calls: syscall_handler.inner_calls, + tracked_resource: TrackedResource::CairoSteps, storage_read_values: syscall_handler.read_values, accessed_storage_keys: syscall_handler.accessed_keys, }) diff --git a/crates/blockifier/src/execution/deprecated_syscalls/hint_processor.rs b/crates/blockifier/src/execution/deprecated_syscalls/hint_processor.rs index 78754ef67ee..fe9cc94b05f 100644 --- a/crates/blockifier/src/execution/deprecated_syscalls/hint_processor.rs +++ b/crates/blockifier/src/execution/deprecated_syscalls/hint_processor.rs @@ -502,8 +502,12 @@ pub fn execute_inner_call( vm: &mut VirtualMachine, syscall_handler: &mut DeprecatedSyscallHintProcessor<'_>, ) -> DeprecatedSyscallResult { - let call_info = - call.execute(syscall_handler.state, syscall_handler.resources, syscall_handler.context)?; + // Use `non_reverting_execute` since we don't support reverts here. + let call_info = call.non_reverting_execute( + syscall_handler.state, + syscall_handler.resources, + syscall_handler.context, + )?; let retdata = &call_info.execution.retdata.0; let retdata: Vec = retdata.iter().map(|&x| MaybeRelocatable::from(x)).collect(); diff --git a/crates/blockifier/src/execution/entry_point.rs b/crates/blockifier/src/execution/entry_point.rs index 7db977e561d..b82e18f9d11 100644 --- a/crates/blockifier/src/execution/entry_point.rs +++ b/crates/blockifier/src/execution/entry_point.rs @@ -1,5 +1,6 @@ use std::cell::RefCell; use std::cmp::min; +use std::collections::HashMap; use std::sync::Arc; use cairo_vm::vm::runners::cairo_runner::{ExecutionResources, ResourceTracker, RunResources}; @@ -7,6 +8,7 @@ use num_traits::{Inv, Zero}; use serde::Serialize; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector}; use starknet_api::deprecated_contract_class::EntryPointType; +use starknet_api::state::StorageKey; use starknet_api::transaction::{Calldata, TransactionVersion}; use starknet_types_core::felt::Felt; @@ -15,13 +17,14 @@ use crate::abi::constants; use crate::context::{BlockContext, TransactionContext}; use crate::execution::call_info::CallInfo; use crate::execution::common_hints::ExecutionMode; +use crate::execution::contract_class::TrackedResource; use crate::execution::errors::{ ConstructorEntryPointExecutionError, EntryPointExecutionError, PreExecutionError, }; -use crate::execution::execution_utils::execute_entry_point_call; -use crate::state::state_api::State; +use crate::execution::execution_utils::execute_entry_point_call_wrapper; +use crate::state::state_api::{State, StateResult}; use crate::transaction::objects::{HasRelatedFeeType, TransactionInfo}; use crate::transaction::transaction_types::TransactionType; use crate::utils::{u128_from_usize, usize_from_u128}; @@ -37,6 +40,43 @@ pub const FAULTY_CLASS_HASH: &str = pub type EntryPointExecutionResult = Result; pub type ConstructorEntryPointExecutionResult = Result; +/// Holds the the information required to revert the execution of an entry point. +#[derive(Debug)] +pub struct EntryPointRevertInfo { + // The contract address that the revert info applies to. + pub contract_address: ContractAddress, + /// The original class hash of the contract that was called. + pub original_class_hash: ClassHash, + /// The original storage values. + pub original_values: HashMap, + // The number of emitted events before the call. + n_emitted_events: usize, + // The number of sent messages to L1 before the call. + n_sent_messages_to_l1: usize, +} +impl EntryPointRevertInfo { + pub fn new( + contract_address: ContractAddress, + original_class_hash: ClassHash, + n_emitted_events: usize, + n_sent_messages_to_l1: usize, + ) -> Self { + Self { + contract_address, + original_class_hash, + original_values: HashMap::new(), + n_emitted_events, + n_sent_messages_to_l1, + } + } +} + +/// The ExecutionRevertInfo stores a vector of entry point revert infos. +/// We don't merge infos related same contract as doing it on every nesting level would +/// result in O(N^2) complexity. +#[derive(Default, Debug)] +pub struct ExecutionRevertInfo(pub Vec); + /// Represents a the type of the call (used for debugging). #[cfg_attr(feature = "transaction_serde", derive(serde::Deserialize))] #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Serialize)] @@ -102,7 +142,36 @@ impl CallEntryPoint { self.class_hash = Some(class_hash); let contract_class = state.get_compiled_contract_class(class_hash)?; - execute_entry_point_call(self, contract_class, state, resources, context) + context.revert_infos.0.push(EntryPointRevertInfo::new( + self.storage_address, + storage_class_hash, + context.n_emitted_events, + context.n_sent_messages_to_l1, + )); + + execute_entry_point_call_wrapper(self, contract_class, state, resources, context) + } + + /// Similar to `execute`, but returns an error if the outer call is reverted. + pub fn non_reverting_execute( + self, + state: &mut dyn State, + resources: &mut ExecutionResources, + context: &mut EntryPointExecutionContext, + ) -> EntryPointExecutionResult { + let execution_result = self.execute(state, resources, context); + if let Ok(call_info) = &execution_result { + // If the execution of the outer call failed, revert the transction. + if call_info.execution.failed { + let retdata = &call_info.execution.retdata.0; + + return Err(EntryPointExecutionError::ExecutionFailed { + error_data: retdata.clone(), + }); + } + } + + execution_result } } @@ -130,6 +199,11 @@ pub struct EntryPointExecutionContext { // The execution mode affects the behavior of the hint processor. pub execution_mode: ExecutionMode, + // The call stack of tracked resources from the first entry point to the current. + pub tracked_resource_stack: Vec, + + // Information for reverting the state (inludes the revert info of the callers). + pub revert_infos: ExecutionRevertInfo, } impl EntryPointExecutionContext { @@ -146,6 +220,8 @@ impl EntryPointExecutionContext { tx_context: tx_context.clone(), current_recursion_depth: Default::default(), execution_mode: mode, + tracked_resource_stack: vec![], + revert_infos: ExecutionRevertInfo(vec![]), } } @@ -282,6 +358,24 @@ impl EntryPointExecutionContext { pub fn gas_costs(&self) -> &GasCosts { &self.versioned_constants().os_constants.gas_costs } + + /// Reverts the state back to the way it was when self.revert_infos.0['revert_idx'] was created. + pub fn revert(&mut self, revert_idx: usize, state: &mut dyn State) -> StateResult<()> { + for contract_revert_info in self.revert_infos.0.drain(revert_idx..).rev() { + for (key, value) in contract_revert_info.original_values.iter() { + state.set_storage_at(contract_revert_info.contract_address, *key, *value)?; + } + state.set_class_hash_at( + contract_revert_info.contract_address, + contract_revert_info.original_class_hash, + )?; + + self.n_emitted_events = contract_revert_info.n_emitted_events; + self.n_sent_messages_to_l1 = contract_revert_info.n_sent_messages_to_l1; + } + + Ok(()) + } } pub fn execute_constructor_entry_point( @@ -315,7 +409,7 @@ pub fn execute_constructor_entry_point( initial_gas: remaining_gas, }; - constructor_call.execute(state, resources, context).map_err(|error| { + constructor_call.non_reverting_execute(state, resources, context).map_err(|error| { ConstructorEntryPointExecutionError::new(error, &ctor_context, Some(constructor_selector)) }) } diff --git a/crates/blockifier/src/execution/entry_point_execution.rs b/crates/blockifier/src/execution/entry_point_execution.rs index 1d21241a6b0..1464e806147 100644 --- a/crates/blockifier/src/execution/entry_point_execution.rs +++ b/crates/blockifier/src/execution/entry_point_execution.rs @@ -14,7 +14,7 @@ use starknet_api::felt; use starknet_types_core::felt::Felt; use crate::execution::call_info::{CallExecution, CallInfo, Retdata}; -use crate::execution::contract_class::{ContractClassV1, EntryPointV1}; +use crate::execution::contract_class::{ContractClassV1, EntryPointV1, TrackedResource}; use crate::execution::entry_point::{ CallEntryPoint, EntryPointExecutionContext, @@ -63,6 +63,8 @@ pub fn execute_entry_point_call( "Class hash must not be None when executing an entry point.".into(), ))?; + let tracked_resource = + *context.tracked_resource_stack.last().expect("Unexpected empty tracked resource."); let VmExecutionContext { mut runner, mut syscall_handler, @@ -103,8 +105,9 @@ pub fn execute_entry_point_call( previous_resources, n_total_args, program_extra_data_length, + tracked_resource, )?; - if call_info.execution.failed { + if call_info.execution.failed && !context.versioned_constants().enable_reverts { return Err(EntryPointExecutionError::ExecutionFailed { error_data: call_info.execution.retdata.0, }); @@ -369,10 +372,11 @@ fn maybe_fill_holes( pub fn finalize_execution( mut runner: CairoRunner, - syscall_handler: SyscallHintProcessor<'_>, + mut syscall_handler: SyscallHintProcessor<'_>, previous_resources: ExecutionResources, n_total_args: usize, program_extra_data_length: usize, + tracked_resource: TrackedResource, ) -> Result { // Close memory holes in segments (OS code touches those memory cells, we simulate it). let program_start_ptr = runner @@ -406,8 +410,10 @@ pub fn finalize_execution( } *syscall_handler.resources += &vm_resources_without_inner_calls; // Take into account the syscall resources of the current call. - *syscall_handler.resources += &versioned_constants - .get_additional_os_syscall_resources(&syscall_handler.syscall_counter)?; + *syscall_handler.resources += + &versioned_constants.get_additional_os_syscall_resources(&syscall_handler.syscall_counter); + + syscall_handler.finalize(); let full_call_resources = &*syscall_handler.resources - &previous_resources; Ok(CallInfo { @@ -421,6 +427,7 @@ pub fn finalize_execution( }, resources: full_call_resources.filter_unused_builtins(), inner_calls: syscall_handler.inner_calls, + tracked_resource, storage_read_values: syscall_handler.read_values, accessed_storage_keys: syscall_handler.accessed_keys, }) diff --git a/crates/blockifier/src/execution/execution_utils.rs b/crates/blockifier/src/execution/execution_utils.rs index 306d973f8be..9dc58340853 100644 --- a/crates/blockifier/src/execution/execution_utils.rs +++ b/crates/blockifier/src/execution/execution_utils.rs @@ -25,7 +25,7 @@ use starknet_types_core::felt::Felt; use super::entry_point::ConstructorEntryPointExecutionResult; use super::errors::ConstructorEntryPointExecutionError; use crate::execution::call_info::{CallInfo, Retdata}; -use crate::execution::contract_class::ContractClass; +use crate::execution::contract_class::{ContractClass, TrackedResource}; use crate::execution::entry_point::{ execute_constructor_entry_point, CallEntryPoint, @@ -43,6 +43,31 @@ pub type Args = Vec; pub const SEGMENT_ARENA_BUILTIN_SIZE: usize = 3; +/// A wrapper for execute_entry_point_call that performs pre and post-processing. +pub fn execute_entry_point_call_wrapper( + call: CallEntryPoint, + contract_class: ContractClass, + state: &mut dyn State, + resources: &mut ExecutionResources, + context: &mut EntryPointExecutionContext, +) -> EntryPointExecutionResult { + let tracked_resource = contract_class + .tracked_resource(&context.versioned_constants().min_compiler_version_for_sierra_gas); + // Note: no return statements (explicit or implicit) should be added between the push and the + // pop commands. + + // Once we ran with CairoSteps, we will continue to run using it for all nested calls. + if context.tracked_resource_stack.last().is_some_and(|x| *x == TrackedResource::CairoSteps) { + context.tracked_resource_stack.push(TrackedResource::CairoSteps); + } else { + context.tracked_resource_stack.push(tracked_resource); + } + + let res = execute_entry_point_call(call, contract_class, state, resources, context); + context.tracked_resource_stack.pop(); + res +} + /// Executes a specific call to a contract entry point and returns its output. pub fn execute_entry_point_call( call: CallEntryPoint, diff --git a/crates/blockifier/src/execution/stack_trace.rs b/crates/blockifier/src/execution/stack_trace.rs index af8f9ad1a52..06414576ab0 100644 --- a/crates/blockifier/src/execution/stack_trace.rs +++ b/crates/blockifier/src/execution/stack_trace.rs @@ -148,7 +148,7 @@ impl ErrorStack { } /// Extracts the error trace from a `TransactionExecutionError`. This is a top level function. -pub fn gen_transaction_execution_error_trace(error: &TransactionExecutionError) -> ErrorStack { +pub fn gen_tx_execution_error_trace(error: &TransactionExecutionError) -> ErrorStack { match error { TransactionExecutionError::ExecutionError { error, diff --git a/crates/blockifier/src/execution/stack_trace_test.rs b/crates/blockifier/src/execution/stack_trace_test.rs index 6befda1689d..bcca1bcfe01 100644 --- a/crates/blockifier/src/execution/stack_trace_test.rs +++ b/crates/blockifier/src/execution/stack_trace_test.rs @@ -39,8 +39,6 @@ use crate::transaction::test_utils::{ use crate::transaction::transaction_types::TransactionType; use crate::transaction::transactions::ExecutableTransaction; -const INNER_CALL_CONTRACT_IN_CALL_CHAIN_OFFSET: usize = 117; - #[rstest] fn test_stack_trace_with_inner_error_msg(block_context: BlockContext) { let cairo_version = CairoVersion::Cairo0; @@ -212,19 +210,16 @@ An ASSERT_EQ instruction failed: 1 != 0. " ); - let expected_trace_cairo1 = format!( + let expected_trace_cairo1 = "Transaction execution has failed: -0: Error in the called contract (contract address: {account_address_felt:#064x}, class hash: \ - {account_contract_hash:#064x}, selector: {execute_selector_felt:#064x}): -Error at pc=0:767: -1: Error in the called contract (contract address: {test_contract_address_felt:#064x}, class hash: \ - {test_contract_hash:#064x}, selector: {external_entry_point_selector_felt:#064x}): -Error at pc=0:612: -2: Error in the called contract (contract address: {test_contract_address_2_felt:#064x}, class \ - hash: {test_contract_hash:#064x}, selector: {inner_entry_point_selector_felt:#064x}): -Execution failed. Failure reason: 0x6661696c ('fail'). +0: Error in the called contract (contract address: \ + 0x00000000000000000000000000000000000000000000000000000000c0020000, class hash: \ + 0x0000000000000000000000000000000000000000000000000000000080020000, selector: \ + 0x015d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad): +Execution failed. Failure reason: (0x6661696c ('fail'), 0x454e545259504f494e545f4641494c4544 \ + ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED')). " - ); + .into(); let expected_trace = match cairo_version { CairoVersion::Cairo0 => expected_trace_cairo0, @@ -334,21 +329,13 @@ Unknown location (pc=0:{expected_pc1}) ) } CairoVersion::Cairo1 => { - let pc_location = entry_point_offset.0 + INNER_CALL_CONTRACT_IN_CALL_CHAIN_OFFSET; format!( "Transaction execution has failed: 0: Error in the called contract (contract address: {account_address_felt:#064x}, class hash: \ {account_contract_hash:#064x}, selector: {execute_selector_felt:#064x}): -Error at pc=0:767: -1: Error in the called contract (contract address: {contract_address_felt:#064x}, class hash: \ - {test_contract_hash:#064x}, selector: {invoke_call_chain_selector_felt:#064x}): -Error at pc=0:9631: -Cairo traceback (most recent call last): -Unknown location (pc=0:{pc_location}) - -2: Error in the called contract (contract address: {contract_address_felt:#064x}, class hash: \ - {test_contract_hash:#064x}, selector: {invoke_call_chain_selector_felt:#064x}): -Execution failed. Failure reason: {expected_error}. +Execution failed. Failure reason: ({expected_error}, 0x454e545259504f494e545f4641494c4544 \ + ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 \ + ('ENTRYPOINT_FAILED')). " ) } @@ -490,27 +477,16 @@ Unknown location (pc=0:{expected_pc3}) ) } CairoVersion::Cairo1 => { - let pc_location = entry_point_offset.0 + INNER_CALL_CONTRACT_IN_CALL_CHAIN_OFFSET; - let (expected_pc0, expected_pc1, _, _) = expected_pcs; format!( "Transaction execution has failed: -0: Error in the called contract (contract address: {account_address_felt:#064x}, class hash: \ - {account_contract_hash:#064x}, selector: {execute_selector_felt:#064x}): -Error at pc=0:767: -1: Error in the called contract (contract address: {address_felt:#064x}, class hash: \ - {test_contract_hash:#064x}, selector: {invoke_call_chain_selector_felt:#064x}): -Error at pc=0:{expected_pc0}: -Cairo traceback (most recent call last): -Unknown location (pc=0:{pc_location}) - -2: Error in the called contract (contract address: {address_felt:#064x}, class hash: \ - {test_contract_hash:#064x}, selector: {invoke_call_chain_selector_felt:#064x}): -Error at pc=0:{expected_pc1}: -Cairo traceback (most recent call last): -Unknown location (pc=0:{pc_location}) - -3: {last_call_preamble}: -Execution failed. Failure reason: {expected_error}. +0: Error in the called contract (contract address: \ + 0x00000000000000000000000000000000000000000000000000000000c0020000, class hash: \ + 0x0000000000000000000000000000000000000000000000000000000080020000, selector: \ + 0x015d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad): +Execution failed. Failure reason: ({expected_error}, 0x454e545259504f494e545f4641494c4544 \ + ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 \ + ('ENTRYPOINT_FAILED'), 0x454e545259504f494e545f4641494c4544 \ + ('ENTRYPOINT_FAILED')). " ) } @@ -607,14 +583,9 @@ An ASSERT_EQ instruction failed: 1 != 0. ", class_hash.0 ), - CairoVersion::Cairo1 => format!( - "Transaction validation has failed: -0: Error in the called contract (contract address: {contract_address:#064x}, class hash: {:#064x}, \ - selector: {selector:#064x}): -Execution failed. Failure reason: 0x496e76616c6964207363656e6172696f ('Invalid scenario'). -", - class_hash.0 - ), + CairoVersion::Cairo1 => "The `validate` entry point panicked with \ + 0x496e76616c6964207363656e6172696f ('Invalid scenario')." + .into(), }; // Clean pc locations from the trace. diff --git a/crates/blockifier/src/execution/syscalls/hint_processor.rs b/crates/blockifier/src/execution/syscalls/hint_processor.rs index 3753febac95..9a6764cd4ec 100644 --- a/crates/blockifier/src/execution/syscalls/hint_processor.rs +++ b/crates/blockifier/src/execution/syscalls/hint_processor.rs @@ -1,5 +1,5 @@ use std::any::Any; -use std::collections::{HashMap, HashSet}; +use std::collections::{hash_map, HashMap, HashSet}; use cairo_lang_casm::hints::{Hint, StarknetHint}; use cairo_lang_casm::operand::{BinOpOperand, DerefOrImmediate, Operation, Register, ResOperand}; @@ -194,6 +194,9 @@ pub const OUT_OF_GAS_ERROR: &str = // "Block number out of range"; pub const BLOCK_NUMBER_OUT_OF_RANGE_ERROR: &str = "0x00000000000000426c6f636b206e756d626572206f7574206f662072616e6765"; +// "ENTRYPOINT_FAILED"; +pub const ENTRYPOINT_FAILED_ERROR: &str = + "0x000000000000000000000000000000454e545259504f494e545f4641494c4544"; // "Invalid input length"; pub const INVALID_INPUT_LENGTH_ERROR: &str = "0x000000000000000000000000496e76616c696420696e707574206c656e677468"; @@ -225,6 +228,10 @@ pub struct SyscallHintProcessor<'a> { pub read_values: Vec, pub accessed_keys: HashSet, + // The original storage value of the executed contract. + // Should be moved back `context.revert_info` before executing an inner call. + pub original_values: HashMap, + // Secp hint processors. pub secp256k1_hint_processor: SecpHintProcessor, pub secp256r1_hint_processor: SecpHintProcessor, @@ -247,6 +254,14 @@ impl<'a> SyscallHintProcessor<'a> { hints: &'a HashMap, read_only_segments: ReadOnlySegments, ) -> Self { + let original_values = std::mem::take( + &mut context + .revert_infos + .0 + .last_mut() + .expect("Missing contract revert info.") + .original_values, + ); SyscallHintProcessor { state, resources, @@ -260,6 +275,7 @@ impl<'a> SyscallHintProcessor<'a> { syscall_ptr: initial_syscall_ptr, read_values: vec![], accessed_keys: HashSet::new(), + original_values, hints, execution_info_ptr: None, secp256k1_hint_processor: SecpHintProcessor::default(), @@ -689,11 +705,29 @@ impl<'a> SyscallHintProcessor<'a> { key: StorageKey, value: Felt, ) -> SyscallResult { + let contract_address = self.storage_address(); + + match self.original_values.entry(key) { + hash_map::Entry::Vacant(entry) => { + entry.insert(self.state.get_storage_at(contract_address, key)?); + } + hash_map::Entry::Occupied(_) => {} + } + self.accessed_keys.insert(key); - self.state.set_storage_at(self.storage_address(), key, value)?; + self.state.set_storage_at(contract_address, key, value)?; Ok(StorageWriteResponse {}) } + + pub fn finalize(&mut self) { + self.context + .revert_infos + .0 + .last_mut() + .expect("Missing contract revert info.") + .original_values = std::mem::take(&mut self.original_values); + } } /// Retrieves a [Relocatable] from the VM given a [ResOperand]. @@ -791,22 +825,38 @@ pub fn execute_inner_call( syscall_handler: &mut SyscallHintProcessor<'_>, remaining_gas: &mut u64, ) -> SyscallResult { + let revert_idx = syscall_handler.context.revert_infos.0.len(); + let call_info = call.execute(syscall_handler.state, syscall_handler.resources, syscall_handler.context)?; - let raw_retdata = &call_info.execution.retdata.0; - - if call_info.execution.failed { - // TODO(spapini): Append an error word according to starknet spec if needed. - // Something like "EXECUTION_ERROR". - return Err(SyscallExecutionError::SyscallError { error_data: raw_retdata.clone() }); - } - let retdata_segment = create_retdata_segment(vm, syscall_handler, raw_retdata)?; + let mut raw_retdata = call_info.execution.retdata.0.clone(); update_remaining_gas(remaining_gas, &call_info); + let failed = call_info.execution.failed; syscall_handler.inner_calls.push(call_info); + if failed { + syscall_handler.context.revert(revert_idx, syscall_handler.state)?; + + // Delete events and l2_to_l1_messages from the reverted call. + let reverted_call = &mut syscall_handler.inner_calls.last_mut().unwrap(); + let mut stack: Vec<&mut CallInfo> = vec![reverted_call]; + while let Some(call_info) = stack.pop() { + call_info.execution.events.clear(); + call_info.execution.l2_to_l1_messages.clear(); + // Add inner calls that did not fail to the stack. + // The events and l2_to_l1_messages of the failed calls were already cleared. + stack.extend( + call_info.inner_calls.iter_mut().filter(|call_info| !call_info.execution.failed), + ); + } - Ok(retdata_segment) + raw_retdata + .push(Felt::from_hex(ENTRYPOINT_FAILED_ERROR).map_err(SyscallExecutionError::from)?); + return Err(SyscallExecutionError::SyscallError { error_data: raw_retdata }); + } + + create_retdata_segment(vm, syscall_handler, &raw_retdata) } pub fn create_retdata_segment( @@ -843,12 +893,14 @@ pub fn execute_library_call( initial_gas: *remaining_gas, }; - execute_inner_call(entry_point, vm, syscall_handler, remaining_gas).map_err(|error| { - error.as_lib_call_execution_error( + execute_inner_call(entry_point, vm, syscall_handler, remaining_gas).map_err(|error| match error + { + SyscallExecutionError::SyscallError { .. } => error, + _ => error.as_lib_call_execution_error( class_hash, syscall_handler.storage_address(), entry_point_selector, - ) + ), }) } diff --git a/crates/blockifier/src/execution/syscalls/mod.rs b/crates/blockifier/src/execution/syscalls/mod.rs index 9091f92ea18..8258da2f75c 100644 --- a/crates/blockifier/src/execution/syscalls/mod.rs +++ b/crates/blockifier/src/execution/syscalls/mod.rs @@ -198,9 +198,11 @@ pub fn call_contract( call_type: CallType::Call, initial_gas: *remaining_gas, }; + let retdata_segment = execute_inner_call(entry_point, vm, syscall_handler, remaining_gas) - .map_err(|error| { - error.as_call_contract_execution_error(class_hash, storage_address, selector) + .map_err(|error| match error { + SyscallExecutionError::SyscallError { .. } => error, + _ => error.as_call_contract_execution_error(class_hash, storage_address, selector), })?; Ok(CallContractResponse { segment: retdata_segment }) diff --git a/crates/blockifier/src/execution/syscalls/syscall_tests/call_contract.rs b/crates/blockifier/src/execution/syscalls/syscall_tests/call_contract.rs index f73b04027bf..78f8236fb2f 100644 --- a/crates/blockifier/src/execution/syscalls/syscall_tests/call_contract.rs +++ b/crates/blockifier/src/execution/syscalls/syscall_tests/call_contract.rs @@ -1,3 +1,5 @@ +use core::panic; + use pretty_assertions::assert_eq; use starknet_api::felt; use test_case::test_case; @@ -7,11 +9,49 @@ use crate::abi::abi_utils::selector_from_name; use crate::context::ChainInfo; use crate::execution::call_info::{CallExecution, Retdata}; use crate::execution::entry_point::CallEntryPoint; +use crate::execution::execution_utils::format_panic_data; use crate::retdata; +use crate::state::state_api::StateReader; use crate::test_utils::contracts::FeatureContract; use crate::test_utils::initial_test_state::test_state; use crate::test_utils::{create_calldata, trivial_external_entry_point_new, CairoVersion, BALANCE}; +#[test] +fn test_call_contract_that_panics() { + let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1); + let empty_contract = FeatureContract::Empty(CairoVersion::Cairo1); + let chain_info = &ChainInfo::create_for_testing(); + let mut state = test_state(chain_info, BALANCE, &[(test_contract, 1), (empty_contract, 0)]); + + let new_class_hash = empty_contract.get_class_hash(); + let outer_entry_point_selector = selector_from_name("test_call_contract_revert"); + let calldata = create_calldata( + FeatureContract::TestContract(CairoVersion::Cairo1).get_instance_address(0), + "test_revert_helper", + &[new_class_hash.0], + ); + let entry_point_call = CallEntryPoint { + entry_point_selector: outer_entry_point_selector, + calldata, + ..trivial_external_entry_point_new(test_contract) + }; + + let res = entry_point_call.execute_directly(&mut state).unwrap(); + assert!(!res.execution.failed); + let [inner_call] = &res.inner_calls[..] else { + panic!("Expected one inner call, got {:?}", res.inner_calls); + }; + // The inner call should have failed. + assert!(inner_call.execution.failed); + assert_eq!(format_panic_data(&inner_call.execution.retdata.0), "\"test_revert_helper\""); + assert!(inner_call.execution.events.is_empty()); + assert!(inner_call.execution.l2_to_l1_messages.is_empty()); + assert_eq!( + state.get_class_hash_at(inner_call.call.storage_address).unwrap(), + test_contract.get_class_hash() + ); +} + #[test_case( FeatureContract::TestContract(CairoVersion::Cairo1), FeatureContract::TestContract(CairoVersion::Cairo1), diff --git a/crates/blockifier/src/execution/syscalls/syscall_tests/get_block_hash.rs b/crates/blockifier/src/execution/syscalls/syscall_tests/get_block_hash.rs index cb3bdc85db0..64acd453607 100644 --- a/crates/blockifier/src/execution/syscalls/syscall_tests/get_block_hash.rs +++ b/crates/blockifier/src/execution/syscalls/syscall_tests/get_block_hash.rs @@ -11,6 +11,7 @@ use crate::abi::constants; use crate::context::ChainInfo; use crate::execution::call_info::{CallExecution, Retdata}; use crate::execution::entry_point::CallEntryPoint; +use crate::execution::execution_utils::format_panic_data; use crate::state::cached_state::CachedState; use crate::state::state_api::State; use crate::test_utils::contracts::FeatureContract; @@ -92,6 +93,10 @@ fn negative_flow_block_number_out_of_range(test_contract: FeatureContract) { ..trivial_external_entry_point_new(test_contract) }; - let error = entry_point_call.execute_directly(&mut state).unwrap_err().to_string(); - assert!(error.contains("Block number out of range")); + let call_info = entry_point_call.execute_directly(&mut state).unwrap(); + assert!(call_info.execution.failed); + assert_eq!( + format_panic_data(&call_info.execution.retdata.0), + "0x426c6f636b206e756d626572206f7574206f662072616e6765 ('Block number out of range')" + ); } diff --git a/crates/blockifier/src/execution/syscalls/syscall_tests/library_call.rs b/crates/blockifier/src/execution/syscalls/syscall_tests/library_call.rs index 3592eae4180..b5eb86a10a7 100644 --- a/crates/blockifier/src/execution/syscalls/syscall_tests/library_call.rs +++ b/crates/blockifier/src/execution/syscalls/syscall_tests/library_call.rs @@ -13,6 +13,7 @@ use crate::abi::abi_utils::selector_from_name; use crate::context::ChainInfo; use crate::execution::call_info::{CallExecution, CallInfo, Retdata}; use crate::execution::entry_point::{CallEntryPoint, CallType}; +use crate::execution::execution_utils::format_panic_data; use crate::execution::syscalls::syscall_tests::constants::{ REQUIRED_GAS_LIBRARY_CALL_TEST, REQUIRED_GAS_STORAGE_READ_WRITE_TEST, @@ -27,6 +28,7 @@ use crate::test_utils::{ CairoVersion, BALANCE, }; +use crate::versioned_constants::VersionedConstants; #[test_case(FeatureContract::TestContract(CairoVersion::Cairo1), REQUIRED_GAS_LIBRARY_CALL_TEST; "VM")] fn test_library_call(test_contract: FeatureContract, expected_gas: u64) { @@ -78,8 +80,12 @@ fn test_library_call_assert_fails(test_contract: FeatureContract) { ..trivial_external_entry_point_new(test_contract) }; - let err = entry_point_call.execute_directly(&mut state).unwrap_err(); - assert!(err.to_string().contains("x != y")); + let call_info = entry_point_call.execute_directly(&mut state).unwrap(); + assert!(call_info.execution.failed); + assert_eq!( + format_panic_data(&call_info.execution.retdata.0), + "(0x7820213d2079 ('x != y'), 0x454e545259504f494e545f4641494c4544 ('ENTRYPOINT_FAILED'))" + ); } #[test_case(FeatureContract::TestContract(CairoVersion::Cairo1), 478110; "VM")] @@ -142,6 +148,12 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { n_memory_holes: 0, builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 7)]), }; + + // The default VersionedConstants is used in the execute_directly call bellow. + let tracked_resource = test_contract.get_class().tracked_resource( + &VersionedConstants::create_for_testing().min_compiler_version_for_sierra_gas, + ); + let nested_storage_call_info = CallInfo { call: nested_storage_entry_point, execution: CallExecution { @@ -150,6 +162,7 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { ..CallExecution::default() }, resources: storage_entry_point_resources.clone(), + tracked_resource, storage_read_values: vec![felt!(value + 1)], accessed_storage_keys: HashSet::from([StorageKey(patricia_key!(key + 1))]), ..Default::default() @@ -161,6 +174,7 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { n_memory_holes: 0, builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 15)]), }; + let library_call_info = CallInfo { call: library_entry_point, execution: CallExecution { @@ -170,6 +184,7 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { }, resources: library_call_resources, inner_calls: vec![nested_storage_call_info], + tracked_resource, ..Default::default() }; @@ -183,6 +198,7 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { resources: storage_entry_point_resources, storage_read_values: vec![felt!(value)], accessed_storage_keys: HashSet::from([StorageKey(patricia_key!(key))]), + tracked_resource, ..Default::default() }; @@ -201,6 +217,7 @@ fn test_nested_library_call(test_contract: FeatureContract, expected_gas: u64) { }, resources: main_call_resources, inner_calls: vec![library_call_info, storage_call_info], + tracked_resource, ..Default::default() }; diff --git a/crates/blockifier/src/execution/syscalls/syscall_tests/out_of_gas.rs b/crates/blockifier/src/execution/syscalls/syscall_tests/out_of_gas.rs index 9f7a9e7c1a7..0b87c51b991 100644 --- a/crates/blockifier/src/execution/syscalls/syscall_tests/out_of_gas.rs +++ b/crates/blockifier/src/execution/syscalls/syscall_tests/out_of_gas.rs @@ -5,6 +5,7 @@ use test_case::test_case; use crate::abi::abi_utils::selector_from_name; use crate::context::ChainInfo; use crate::execution::entry_point::CallEntryPoint; +use crate::execution::execution_utils::format_panic_data; use crate::execution::syscalls::syscall_tests::constants::REQUIRED_GAS_STORAGE_READ_WRITE_TEST; use crate::test_utils::contracts::FeatureContract; use crate::test_utils::initial_test_state::test_state; @@ -24,6 +25,10 @@ fn test_out_of_gas(test_contract: FeatureContract) { initial_gas: REQUIRED_GAS_STORAGE_READ_WRITE_TEST - 1, ..trivial_external_entry_point_new(test_contract) }; - let error = entry_point_call.execute_directly(&mut state).unwrap_err().to_string(); - assert!(error.contains("Out of gas")); + let call_info = entry_point_call.execute_directly(&mut state).unwrap(); + assert!(call_info.execution.failed); + assert_eq!( + format_panic_data(&call_info.execution.retdata.0), + "0x4f7574206f6620676173 ('Out of gas')" + ); } diff --git a/crates/blockifier/src/fee/fee_checks.rs b/crates/blockifier/src/fee/fee_checks.rs index 0a14d27b56a..a47d4e8d1b6 100644 --- a/crates/blockifier/src/fee/fee_checks.rs +++ b/crates/blockifier/src/fee/fee_checks.rs @@ -1,3 +1,4 @@ +use starknet_api::execution_resources::GasAmount; use starknet_api::transaction::Resource::{self, L1DataGas, L1Gas, L2Gas}; use starknet_api::transaction::{AllResourceBounds, Fee, ResourceBounds, ValidResourceBounds}; use starknet_types_core::felt::Felt; @@ -16,7 +17,7 @@ pub enum FeeCheckError { #[error( "Insufficient max {resource}: max amount: {max_amount}, actual used: {actual_amount}." )] - MaxGasAmountExceeded { resource: Resource, max_amount: u128, actual_amount: u128 }, + MaxGasAmountExceeded { resource: Resource, max_amount: GasAmount, actual_amount: GasAmount }, #[error("Insufficient max fee: max fee: {}, actual fee: {}.", max_fee.0, actual_fee.0)] MaxFeeExceeded { max_fee: Fee, actual_fee: Fee }, #[error( diff --git a/crates/blockifier/src/fee/fee_test.rs b/crates/blockifier/src/fee/fee_test.rs index 16e2e498262..7223ca03aed 100644 --- a/crates/blockifier/src/fee/fee_test.rs +++ b/crates/blockifier/src/fee/fee_test.rs @@ -4,18 +4,20 @@ use assert_matches::assert_matches; use cairo_vm::types::builtin_name::BuiltinName; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use rstest::rstest; +use starknet_api::execution_resources::GasAmount; use starknet_api::invoke_tx_args; -use starknet_api::transaction::{Fee, Resource, ValidResourceBounds}; +use starknet_api::transaction::{Fee, GasVectorComputationMode, Resource, ValidResourceBounds}; use crate::blockifier::block::GasPrices; use crate::context::BlockContext; use crate::fee::fee_checks::{FeeCheckError, FeeCheckReportFields, PostExecutionReport}; -use crate::fee::fee_utils::get_vm_resources_cost; +use crate::fee::fee_utils::{get_fee_by_gas_vector, get_vm_resources_cost}; use crate::fee::receipt::TransactionReceipt; -use crate::fee::resources::{GasVector, GasVectorComputationMode}; +use crate::fee::resources::GasVector; use crate::test_utils::contracts::FeatureContract; use crate::test_utils::initial_test_state::test_state; use crate::test_utils::{ + gas_vector_from_vm_usage, CairoVersion, BALANCE, DEFAULT_ETH_L1_DATA_GAS_PRICE, @@ -25,6 +27,7 @@ use crate::test_utils::{ DEFAULT_STRK_L1_GAS_PRICE, MAX_L1_GAS_AMOUNT, }; +use crate::transaction::objects::FeeType; use crate::transaction::test_utils::{account_invoke_tx, all_resource_bounds, l1_resource_bounds}; use crate::utils::u128_from_usize; use crate::versioned_constants::VersionedConstants; @@ -43,89 +46,117 @@ fn get_vm_resource_usage() -> ExecutionResources { } } -#[test] -fn test_simple_get_vm_resource_usage() { +#[rstest] +fn test_simple_get_vm_resource_usage( + #[values(GasVectorComputationMode::NoL2Gas, GasVectorComputationMode::All)] + gas_vector_computation_mode: GasVectorComputationMode, +) { let versioned_constants = VersionedConstants::create_for_account_testing(); let mut vm_resource_usage = get_vm_resource_usage(); let n_reverted_steps = 15; // Positive flow. // Verify calculation - in our case, n_steps is the heaviest resource. - let l1_gas_by_vm_usage = (versioned_constants.vm_resource_fee_cost().n_steps - * (u128_from_usize(vm_resource_usage.n_steps + n_reverted_steps))) - .ceil() - .to_integer(); + let vm_usage_in_l1_gas = GasAmount( + (versioned_constants.vm_resource_fee_cost().n_steps + * (u128_from_usize(vm_resource_usage.n_steps + n_reverted_steps))) + .ceil() + .to_integer(), + ); + let expected_gas_vector = gas_vector_from_vm_usage( + vm_usage_in_l1_gas, + &gas_vector_computation_mode, + &versioned_constants, + ); assert_eq!( - GasVector::from_l1_gas(l1_gas_by_vm_usage), + expected_gas_vector, get_vm_resources_cost( &versioned_constants, &vm_resource_usage, n_reverted_steps, - &GasVectorComputationMode::NoL2Gas + &gas_vector_computation_mode ) - .unwrap() ); // Another positive flow, this time the heaviest resource is range_check_builtin. let n_reverted_steps = 0; vm_resource_usage.n_steps = vm_resource_usage.builtin_instance_counter.get(&BuiltinName::range_check).unwrap() - 1; - let l1_gas_by_vm_usage = - vm_resource_usage.builtin_instance_counter.get(&BuiltinName::range_check).unwrap(); + let vm_usage_in_l1_gas = u128_from_usize( + *vm_resource_usage.builtin_instance_counter.get(&BuiltinName::range_check).unwrap(), + ) + .into(); + let expected_gas_vector = gas_vector_from_vm_usage( + vm_usage_in_l1_gas, + &gas_vector_computation_mode, + &versioned_constants, + ); assert_eq!( - GasVector::from_l1_gas(u128_from_usize(*l1_gas_by_vm_usage)), + expected_gas_vector, get_vm_resources_cost( &versioned_constants, &vm_resource_usage, n_reverted_steps, - &GasVectorComputationMode::NoL2Gas + &gas_vector_computation_mode ) - .unwrap() ); } -#[test] -fn test_float_get_vm_resource_usage() { +#[rstest] +fn test_float_get_vm_resource_usage( + #[values(GasVectorComputationMode::NoL2Gas, GasVectorComputationMode::All)] + gas_vector_computation_mode: GasVectorComputationMode, +) { let versioned_constants = VersionedConstants::create_for_testing(); let mut vm_resource_usage = get_vm_resource_usage(); // Positive flow. // Verify calculation - in our case, n_steps is the heaviest resource. let n_reverted_steps = 300; - let l1_gas_by_vm_usage = (versioned_constants.vm_resource_fee_cost().n_steps - * u128_from_usize(vm_resource_usage.n_steps + n_reverted_steps)) - .ceil() - .to_integer(); + let vm_usage_in_l1_gas = GasAmount( + (versioned_constants.vm_resource_fee_cost().n_steps + * u128_from_usize(vm_resource_usage.n_steps + n_reverted_steps)) + .ceil() + .to_integer(), + ); + let expected_gas_vector = gas_vector_from_vm_usage( + vm_usage_in_l1_gas, + &gas_vector_computation_mode, + &versioned_constants, + ); assert_eq!( - GasVector::from_l1_gas(l1_gas_by_vm_usage), + expected_gas_vector, get_vm_resources_cost( &versioned_constants, &vm_resource_usage, n_reverted_steps, - &GasVectorComputationMode::NoL2Gas + &gas_vector_computation_mode ) - .unwrap() ); // Another positive flow, this time the heaviest resource is ecdsa_builtin. vm_resource_usage.n_steps = 200; - let l1_gas_by_vm_usage = + let vm_usage_in_l1_gas = GasAmount( ((*versioned_constants.vm_resource_fee_cost().builtins.get(&BuiltinName::ecdsa).unwrap()) * u128_from_usize( *vm_resource_usage.builtin_instance_counter.get(&BuiltinName::ecdsa).unwrap(), )) .ceil() - .to_integer(); - + .to_integer(), + ); + let expected_gas_vector = gas_vector_from_vm_usage( + vm_usage_in_l1_gas, + &gas_vector_computation_mode, + &versioned_constants, + ); assert_eq!( - GasVector::from_l1_gas(l1_gas_by_vm_usage), + expected_gas_vector, get_vm_resources_cost( &versioned_constants, &vm_resource_usage, n_reverted_steps, - &GasVectorComputationMode::NoL2Gas + &gas_vector_computation_mode ) - .unwrap() ); } @@ -143,9 +174,10 @@ fn test_discounted_gas_overdraft( #[case] data_gas_price: u128, #[case] l1_gas_used: usize, #[case] l1_data_gas_used: usize, - #[case] gas_bound: u64, + #[case] gas_bound: u128, #[case] expect_failure: bool, ) { + let gas_bound = GasAmount(gas_bound); let mut block_context = BlockContext::create_for_account_testing(); block_context.block_info.gas_prices = GasPrices::new( DEFAULT_ETH_L1_GAS_PRICE.try_into().unwrap(), @@ -173,8 +205,8 @@ fn test_discounted_gas_overdraft( let tx_receipt = TransactionReceipt { fee: Fee(7), gas: GasVector { - l1_gas: u128_from_usize(l1_gas_used), - l1_data_gas: u128_from_usize(l1_data_gas_used), + l1_gas: u128_from_usize(l1_gas_used).into(), + l1_data_gas: u128_from_usize(l1_data_gas_used).into(), ..Default::default() }, ..Default::default() @@ -190,11 +222,14 @@ fn test_discounted_gas_overdraft( if expect_failure { let error = report.error().unwrap(); - let expected_actual_amount = u128_from_usize(l1_gas_used) - + (u128_from_usize(l1_data_gas_used) * data_gas_price) / gas_price; + let expected_actual_amount = (u128_from_usize(l1_gas_used) + + (u128_from_usize(l1_data_gas_used) * data_gas_price) / gas_price) + .into(); assert_matches!( error, FeeCheckError::MaxGasAmountExceeded { resource, max_amount, actual_amount } - if max_amount == u128::from(gas_bound) && actual_amount == expected_actual_amount && resource == Resource::L1Gas + if max_amount == gas_bound + && actual_amount == expected_actual_amount + && resource == Resource::L1Gas ) } else { assert_matches!(report.error(), None); @@ -258,3 +293,36 @@ fn test_post_execution_gas_overdraft_all_resource_bounds( } } } + +#[rstest] +#[case::happy_flow_l1_gas_only(10, 0, 0, 10, 2*10)] +#[case::happy_flow_no_l2_gas(10, 20, 0, 10 + 3*20, 2*10 + 4*20)] +#[case::saturating_l1_gas(u128::MAX, 1, 0, u128::MAX, u128::MAX)] +#[case::saturating_l1_data_gas(1, u128::MAX, 0, u128::MAX, u128::MAX)] +fn test_get_fee_by_gas_vector_regression( + #[case] l1_gas: u128, + #[case] l1_data_gas: u128, + #[case] l2_gas: u128, + #[case] expected_fee_eth: u128, + #[case] expected_fee_strk: u128, +) { + let mut block_info = BlockContext::create_for_account_testing().block_info; + block_info.gas_prices = GasPrices::new( + 1.try_into().unwrap(), + 2.try_into().unwrap(), + 3.try_into().unwrap(), + 4.try_into().unwrap(), + 5.try_into().unwrap(), + 6.try_into().unwrap(), + ); + let gas_vector = + GasVector { l1_gas: l1_gas.into(), l1_data_gas: l1_data_gas.into(), l2_gas: l2_gas.into() }; + assert_eq!( + get_fee_by_gas_vector(&block_info, gas_vector, &FeeType::Eth), + Fee(expected_fee_eth) + ); + assert_eq!( + get_fee_by_gas_vector(&block_info, gas_vector, &FeeType::Strk), + Fee(expected_fee_strk) + ); +} diff --git a/crates/blockifier/src/fee/fee_utils.rs b/crates/blockifier/src/fee/fee_utils.rs index eb433d55362..eca01cecbf6 100644 --- a/crates/blockifier/src/fee/fee_utils.rs +++ b/crates/blockifier/src/fee/fee_utils.rs @@ -4,15 +4,17 @@ use cairo_vm::types::builtin_name::BuiltinName; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use num_bigint::BigUint; use starknet_api::core::ContractAddress; +use starknet_api::execution_resources::GasAmount; use starknet_api::state::StorageKey; -use starknet_api::transaction::{Fee, Resource}; +use starknet_api::transaction::ValidResourceBounds::{AllResources, L1Gas}; +use starknet_api::transaction::{AllResourceBounds, Fee, GasVectorComputationMode, Resource}; use starknet_types_core::felt::Felt; use crate::abi::abi_utils::get_fee_token_var_address; use crate::abi::sierra_types::next_storage_key; use crate::blockifier::block::BlockInfo; use crate::context::{BlockContext, TransactionContext}; -use crate::fee::resources::{GasVector, GasVectorComputationMode, TransactionFeeResult}; +use crate::fee::resources::{GasVector, TransactionFeeResult}; use crate::state::state_api::StateReader; use crate::transaction::errors::TransactionFeeError; use crate::transaction::objects::{ExecutionResourcesTraits, FeeType, TransactionInfo}; @@ -32,7 +34,7 @@ pub fn get_vm_resources_cost( vm_resource_usage: &ExecutionResources, n_reverted_steps: usize, computation_mode: &GasVectorComputationMode, -) -> TransactionFeeResult { +) -> GasVector { // TODO(Yoni, 1/7/2024): rename vm -> cairo. let vm_resource_fee_costs = versioned_constants.vm_resource_fee_cost(); let builtin_usage_for_fee = vm_resource_usage.prover_builtins(); @@ -49,7 +51,8 @@ pub fn get_vm_resources_cost( // Convert Cairo resource usage to L1 gas usage. // Do so by taking the maximum of the usage of each builtin + step usage. - let vm_l1_gas_usage = vm_resource_fee_costs + let vm_l1_gas_usage = GasAmount( + vm_resource_fee_costs .builtins .iter() // Builtin costs and usage. @@ -62,13 +65,14 @@ pub fn get_vm_resources_cost( vm_resource_usage.total_n_steps() + n_reverted_steps, )]) .map(|(cost, usage)| (cost * u128_from_usize(usage)).ceil().to_integer()) - .fold(0, u128::max); + .fold(0, u128::max), + ); match computation_mode { - GasVectorComputationMode::NoL2Gas => Ok(GasVector::from_l1_gas(vm_l1_gas_usage)), - GasVectorComputationMode::All => Ok(GasVector::from_l2_gas( + GasVectorComputationMode::NoL2Gas => GasVector::from_l1_gas(vm_l1_gas_usage), + GasVectorComputationMode::All => GasVector::from_l2_gas( versioned_constants.convert_l1_to_l2_gas_amount_round_up(vm_l1_gas_usage), - )), + ), } } @@ -111,11 +115,24 @@ pub fn verify_can_pay_committed_bounds( let tx_info = &tx_context.tx_info; let committed_fee = match tx_info { TransactionInfo::Current(context) => { - let l1_bounds = context.l1_resource_bounds(); - let max_amount: u128 = l1_bounds.max_amount.into(); - // Sender will not be charged by `max_price_per_unit`, but this check should not depend - // on the current gas price. - Fee(max_amount * l1_bounds.max_price_per_unit) + match &context.resource_bounds { + L1Gas(l1_gas) => + // Sender will not be charged by `max_price_per_unit`, but this check should not + // depend on the current gas price. + { + Fee(u128::from(l1_gas.max_amount) * l1_gas.max_price_per_unit) + } + // TODO!(Aner): add tests + AllResources(AllResourceBounds { l1_gas, l2_gas, l1_data_gas }) => { + // Committed fee is sum of products (resource_max_amount * resource_max_price) + // of the different resources. + // The Sender will not be charged by`max_price_per_unit`, but this check should + // not depend on the current gas price + Fee(u128::from(l1_gas.max_amount) * l1_gas.max_price_per_unit + + u128::from(l1_data_gas.max_amount) * l1_data_gas.max_price_per_unit + + u128::from(l2_gas.max_amount) * l2_gas.max_price_per_unit) + } + } } TransactionInfo::Deprecated(context) => context.max_fee, }; @@ -125,15 +142,25 @@ pub fn verify_can_pay_committed_bounds( Ok(()) } else { Err(match tx_info { - TransactionInfo::Current(context) => { - let l1_bounds = context.l1_resource_bounds(); - TransactionFeeError::GasBoundsExceedBalance { + TransactionInfo::Current(context) => match &context.resource_bounds { + L1Gas(l1_gas) => TransactionFeeError::GasBoundsExceedBalance { resource: Resource::L1Gas, - max_amount: l1_bounds.max_amount, - max_price: l1_bounds.max_price_per_unit, + max_amount: l1_gas.max_amount, + max_price: l1_gas.max_price_per_unit, balance: balance_to_big_uint(&balance_low, &balance_high), + }, + AllResources(AllResourceBounds { l1_gas, l2_gas, l1_data_gas }) => { + TransactionFeeError::ResourcesBoundsExceedBalance { + balance: balance_to_big_uint(&balance_low, &balance_high), + l1_max_amount: l1_gas.max_amount, + l1_max_price: l1_gas.max_price_per_unit, + l1_data_max_amount: l1_data_gas.max_amount, + l1_data_max_price: l1_data_gas.max_price_per_unit, + l2_max_amount: l2_gas.max_amount, + l2_max_price: l2_gas.max_price_per_unit, + } } - } + }, TransactionInfo::Deprecated(context) => TransactionFeeError::MaxFeeExceedsBalance { max_fee: context.max_fee, balance: balance_to_big_uint(&balance_low, &balance_high), diff --git a/crates/blockifier/src/fee/gas_usage.rs b/crates/blockifier/src/fee/gas_usage.rs index 1baad6fd064..49d3896e78e 100644 --- a/crates/blockifier/src/fee/gas_usage.rs +++ b/crates/blockifier/src/fee/gas_usage.rs @@ -1,13 +1,13 @@ use cairo_vm::vm::runners::cairo_runner::ExecutionResources; +use starknet_api::transaction::GasVectorComputationMode; use super::fee_utils::get_vm_resources_cost; use crate::abi::constants; use crate::context::BlockContext; use crate::fee::eth_gas_constants; -use crate::fee::resources::{GasVector, GasVectorComputationMode}; +use crate::fee::resources::GasVector; use crate::state::cached_state::StateChangesCount; use crate::transaction::account_transaction::AccountTransaction; -use crate::transaction::objects::TransactionPreValidationResult; use crate::utils::u128_from_usize; #[cfg(test)] @@ -42,10 +42,11 @@ pub fn get_da_gas_cost(state_changes_count: &StateChangesCount, use_kzg_da: bool let (l1_gas, blob_gas) = if use_kzg_da { ( - 0, + 0_u8.into(), u128_from_usize( onchain_data_segment_length * eth_gas_constants::DATA_GAS_PER_FIELD_ELEMENT, - ), + ) + .into(), ) } else { // TODO(Yoni, 1/5/2024): count the exact amount of nonzero bytes for each DA entry. @@ -70,7 +71,7 @@ pub fn get_da_gas_cost(state_changes_count: &StateChangesCount, use_kzg_da: bool naive_cost - discount }; - (u128_from_usize(gas), 0) + (u128_from_usize(gas).into(), 0_u8.into()) }; GasVector { l1_gas, l1_data_gas: blob_gas, ..Default::default() } @@ -134,22 +135,25 @@ pub fn get_log_message_to_l1_emissions_cost(l2_to_l1_payload_lengths: &[usize]) } fn get_event_emission_cost(n_topics: usize, data_length: usize) -> GasVector { - GasVector::from_l1_gas(u128_from_usize( - eth_gas_constants::GAS_PER_LOG - + (n_topics + constants::N_DEFAULT_TOPICS) * eth_gas_constants::GAS_PER_LOG_TOPIC - + data_length * eth_gas_constants::GAS_PER_LOG_DATA_WORD, - )) + GasVector::from_l1_gas( + u128_from_usize( + eth_gas_constants::GAS_PER_LOG + + (n_topics + constants::N_DEFAULT_TOPICS) * eth_gas_constants::GAS_PER_LOG_TOPIC + + data_length * eth_gas_constants::GAS_PER_LOG_DATA_WORD, + ) + .into(), + ) } -/// Return an estimated lower bound for the L1 gas on an account transaction. +/// Returns an estimated lower bound for the gas required by the given account transaction. pub fn estimate_minimal_gas_vector( block_context: &BlockContext, tx: &AccountTransaction, gas_usage_vector_computation_mode: &GasVectorComputationMode, -) -> TransactionPreValidationResult { +) -> GasVector { // TODO(Dori, 1/8/2023): Give names to the constant VM step estimates and regression-test them. let BlockContext { block_info, versioned_constants, .. } = block_context; - let state_changes_by_account_transaction = match tx { + let state_changes_by_account_tx = match tx { // We consider the following state changes: sender balance update (storage update) + nonce // increment (contract modification) (we exclude the sequencer balance update and the ERC20 // contract modification since it occurs for every tx). @@ -174,18 +178,17 @@ pub fn estimate_minimal_gas_vector( }, }; - let data_segment_length = - get_onchain_data_segment_length(&state_changes_by_account_transaction); + let data_segment_length = get_onchain_data_segment_length(&state_changes_by_account_tx); let os_steps_for_type = versioned_constants.os_resources_for_tx_type(&tx.tx_type(), tx.calldata_length()).n_steps + versioned_constants.os_kzg_da_resources(data_segment_length).n_steps; let resources = ExecutionResources { n_steps: os_steps_for_type, ..Default::default() }; - Ok(get_da_gas_cost(&state_changes_by_account_transaction, block_info.use_kzg_da) + get_da_gas_cost(&state_changes_by_account_tx, block_info.use_kzg_da) + get_vm_resources_cost( versioned_constants, &resources, 0, gas_usage_vector_computation_mode, - )?) + ) } diff --git a/crates/blockifier/src/fee/gas_usage_test.rs b/crates/blockifier/src/fee/gas_usage_test.rs index d7f40643a4e..bccfe881b69 100644 --- a/crates/blockifier/src/fee/gas_usage_test.rs +++ b/crates/blockifier/src/fee/gas_usage_test.rs @@ -2,8 +2,9 @@ use std::num::NonZeroU128; use pretty_assertions::assert_eq; use rstest::{fixture, rstest}; +use starknet_api::execution_resources::GasAmount; use starknet_api::invoke_tx_args; -use starknet_api::transaction::{EventContent, EventData, EventKey, Fee}; +use starknet_api::transaction::{EventContent, EventData, EventKey, Fee, GasVectorComputationMode}; use starknet_types_core::felt::Felt; use crate::abi::constants; @@ -12,8 +13,7 @@ use crate::execution::call_info::{CallExecution, CallInfo, OrderedEvent}; use crate::fee::eth_gas_constants; use crate::fee::fee_utils::get_fee_by_gas_vector; use crate::fee::gas_usage::{get_da_gas_cost, get_message_segment_length}; -use crate::fee::resources::GasVectorComputationMode::NoL2Gas; -use crate::fee::resources::{GasVector, GasVectorComputationMode, StarknetResources}; +use crate::fee::resources::{GasVector, StarknetResources, StateResources}; use crate::state::cached_state::StateChangesCount; use crate::test_utils::{DEFAULT_ETH_L1_DATA_GAS_PRICE, DEFAULT_ETH_L1_GAS_PRICE}; use crate::transaction::objects::FeeType; @@ -28,13 +28,14 @@ fn versioned_constants() -> &'static VersionedConstants { #[rstest] fn test_get_event_gas_cost( versioned_constants: &VersionedConstants, - // TODO!(Aner): add `All` computation mode. - #[values(NoL2Gas)] gas_vector_computation_mode: GasVectorComputationMode, #[values(false, true)] use_kzg_da: bool, + #[values(GasVectorComputationMode::NoL2Gas, GasVectorComputationMode::All)] + gas_vector_computation_mode: GasVectorComputationMode, ) { - let l2_resource_gas_costs = &versioned_constants.deprecated_l2_resource_gas_costs; + let archival_gas_costs = + versioned_constants.get_archival_data_gas_costs(&gas_vector_computation_mode); let (event_key_factor, data_word_cost) = - (l2_resource_gas_costs.event_key_factor, l2_resource_gas_costs.gas_per_data_felt); + (archival_gas_costs.event_key_factor, archival_gas_costs.gas_per_data_felt); let call_infos: Vec = vec![CallInfo::default(), CallInfo::default(), CallInfo::default()] .into_iter() @@ -42,7 +43,7 @@ fn test_get_event_gas_cost( .collect(); let execution_summary = CallInfo::summarize_many(call_infos.iter()); let starknet_resources = - StarknetResources::new(0, 0, 0, StateChangesCount::default(), None, execution_summary); + StarknetResources::new(0, 0, 0, StateResources::default(), None, execution_summary); assert_eq!( GasVector::default(), starknet_resources.to_gas_vector( @@ -89,18 +90,21 @@ fn test_get_event_gas_cost( .map(|call_info| call_info.with_some_class_hash()) .collect(); let execution_summary = CallInfo::summarize_many(call_infos.iter()); - let expected = GasVector::from_l1_gas( - // 8 keys and 11 data words overall. - (data_word_cost * (event_key_factor * 8_u128 + 11_u128)).to_integer(), - ); + // 8 keys and 11 data words overall. + let expected_gas = + GasAmount((data_word_cost * (event_key_factor * 8_u128 + 11_u128)).to_integer()); + let expected_gas_vector = match gas_vector_computation_mode { + GasVectorComputationMode::NoL2Gas => GasVector::from_l1_gas(expected_gas), + GasVectorComputationMode::All => GasVector::from_l2_gas(expected_gas), + }; let starknet_resources = - StarknetResources::new(0, 0, 0, StateChangesCount::default(), None, execution_summary); + StarknetResources::new(0, 0, 0, StateResources::default(), None, execution_summary); let gas_vector = starknet_resources.to_gas_vector( versioned_constants, use_kzg_da, &gas_vector_computation_mode, ); - assert_eq!(expected, gas_vector); + assert_eq!(expected_gas_vector, gas_vector); assert_ne!(GasVector::default(), gas_vector) } @@ -144,7 +148,7 @@ fn test_get_da_gas_cost_basic(#[case] state_changes_count: StateChangesCount) { let computed_gas_vector = get_da_gas_cost(&state_changes_count, true); assert_eq!( - GasVector::from_l1_data_gas(u128_from_usize(manual_blob_gas_usage)), + GasVector::from_l1_data_gas(u128_from_usize(manual_blob_gas_usage).into()), computed_gas_vector ); } @@ -153,7 +157,7 @@ fn test_get_da_gas_cost_basic(#[case] state_changes_count: StateChangesCount) { fn test_onchain_data_discount() { let use_kzg_da = false; // Check that there's no negative cost. - assert_eq!(get_da_gas_cost(&StateChangesCount::default(), use_kzg_da).l1_gas, 0); + assert_eq!(get_da_gas_cost(&StateChangesCount::default(), use_kzg_da).l1_gas, GasAmount(0)); // Check discount: modified_contract_felt and fee balance discount. let state_changes_count = StateChangesCount { @@ -184,7 +188,7 @@ fn test_onchain_data_discount() { assert_eq!( get_da_gas_cost(&state_changes_count, use_kzg_da).l1_gas, - expected_cost.try_into().unwrap() + u64::try_from(expected_cost).unwrap().into() ); // Test 10% discount. @@ -193,7 +197,7 @@ fn test_onchain_data_discount() { let cost_without_discount = (state_changes_count.n_storage_updates * 2) * (512 + 100); let actual_cost = get_da_gas_cost(&state_changes_count, use_kzg_da).l1_gas; - let cost_ratio = ResourceCost::new(actual_cost, u128_from_usize(cost_without_discount)); + let cost_ratio = ResourceCost::new(actual_cost.0, u128_from_usize(cost_without_discount)); assert!(cost_ratio <= ResourceCost::new(9, 10)); assert!(cost_ratio >= ResourceCost::new(88, 100)); } @@ -225,21 +229,25 @@ fn test_get_message_segment_length( fn test_discounted_gas_from_gas_vector_computation() { let tx_context = BlockContext::create_for_testing().to_tx_context(&account_invoke_tx(invoke_tx_args! {})); - let gas_usage = GasVector { l1_gas: 100, l1_data_gas: 2, ..Default::default() }; + let gas_usage = + GasVector { l1_gas: GasAmount(100), l1_data_gas: GasAmount(2), ..Default::default() }; let actual_result = gas_usage.to_discounted_l1_gas(&tx_context); let result_div_ceil = gas_usage.l1_gas + u128_div_ceil( - gas_usage.l1_data_gas * DEFAULT_ETH_L1_DATA_GAS_PRICE, + gas_usage.l1_data_gas.0 * DEFAULT_ETH_L1_DATA_GAS_PRICE, NonZeroU128::new(DEFAULT_ETH_L1_GAS_PRICE).unwrap(), - ); - let result_div_floor = gas_usage.l1_gas - + (gas_usage.l1_data_gas * DEFAULT_ETH_L1_DATA_GAS_PRICE) / DEFAULT_ETH_L1_GAS_PRICE; + ) + .into(); + let result_div_floor = GasAmount( + gas_usage.l1_gas.0 + + (gas_usage.l1_data_gas.0 * DEFAULT_ETH_L1_DATA_GAS_PRICE) / DEFAULT_ETH_L1_GAS_PRICE, + ); assert_eq!(actual_result, result_div_ceil); - assert_eq!(actual_result, result_div_floor + 1); + assert_eq!(actual_result, result_div_floor + GasAmount(1)); assert!( get_fee_by_gas_vector(&tx_context.block_context.block_info, gas_usage, &FeeType::Eth) - <= Fee(actual_result * DEFAULT_ETH_L1_GAS_PRICE) + <= Fee(actual_result.0 * DEFAULT_ETH_L1_GAS_PRICE) ); } diff --git a/crates/blockifier/src/fee/receipt.rs b/crates/blockifier/src/fee/receipt.rs index 21bc6129559..b0b4707c4e6 100644 --- a/crates/blockifier/src/fee/receipt.rs +++ b/crates/blockifier/src/fee/receipt.rs @@ -8,11 +8,12 @@ use crate::fee::resources::{ ComputationResources, GasVector, StarknetResources, + StateResources, TransactionResources, }; use crate::state::cached_state::StateChanges; use crate::transaction::account_transaction::AccountTransaction; -use crate::transaction::objects::{HasRelatedFeeType, TransactionExecutionResult}; +use crate::transaction::objects::HasRelatedFeeType; use crate::transaction::transaction_types::TransactionType; #[cfg(test)] @@ -46,9 +47,7 @@ pub struct TransactionReceipt { } impl TransactionReceipt { - fn from_params( - tx_receipt_params: TransactionReceiptParameters<'_>, - ) -> TransactionExecutionResult { + fn from_params(tx_receipt_params: TransactionReceiptParameters<'_>) -> Self { let TransactionReceiptParameters { tx_context, calldata_length, @@ -67,7 +66,7 @@ impl TransactionReceipt { calldata_length, signature_length, code_size, - state_changes.count_for_fee_charge(sender_address, tx_context.fee_token_address()), + StateResources::new(state_changes, sender_address, tx_context.fee_token_address()), l1_handler_payload_size, execution_summary_without_fee_transfer, ); @@ -77,7 +76,7 @@ impl TransactionReceipt { tx_type, &starknet_resources, tx_context.block_context.block_info.use_kzg_da, - )?) + )) .filter_unused_builtins(); let tx_resources = TransactionResources { @@ -92,7 +91,7 @@ impl TransactionReceipt { &tx_context.block_context.versioned_constants, tx_context.block_context.block_info.use_kzg_da, &tx_context.get_gas_vector_computation_mode(), - )?; + ); // L1 handler transactions are not charged an L2 fee but it is compared to the L1 fee. let fee = if tx_context.tx_info.enforce_fee() || tx_type == TransactionType::L1Handler { @@ -102,19 +101,20 @@ impl TransactionReceipt { }; let da_gas = tx_resources .starknet_resources - .get_state_changes_cost(tx_context.block_context.block_info.use_kzg_da); + .state + .to_gas_vector(tx_context.block_context.block_info.use_kzg_da); - Ok(Self { resources: tx_resources, gas, da_gas, fee }) + Self { resources: tx_resources, gas, da_gas, fee } } - /// Computes actual cost of an L1 handler transaction. + /// Computes the receipt of an L1 handler transaction. pub fn from_l1_handler<'a>( tx_context: &'a TransactionContext, l1_handler_payload_size: usize, execution_summary_without_fee_transfer: ExecutionSummary, state_changes: &'a StateChanges, execution_resources: &'a ExecutionResources, - ) -> TransactionExecutionResult { + ) -> Self { Self::from_params(TransactionReceiptParameters { tx_context, calldata_length: l1_handler_payload_size, @@ -130,7 +130,7 @@ impl TransactionReceipt { }) } - /// Computes actual cost of an account transaction. + /// Computes the receipt of an account transaction. pub fn from_account_tx<'a>( account_tx: &'a AccountTransaction, tx_context: &'a TransactionContext, @@ -138,7 +138,7 @@ impl TransactionReceipt { execution_resources: &'a ExecutionResources, execution_summary_without_fee_transfer: ExecutionSummary, reverted_steps: usize, - ) -> TransactionExecutionResult { + ) -> Self { Self::from_params(TransactionReceiptParameters { tx_context, calldata_length: account_tx.calldata_length(), diff --git a/crates/blockifier/src/fee/receipt_test.rs b/crates/blockifier/src/fee/receipt_test.rs index 0b88b4be4ae..4cbe47a6779 100644 --- a/crates/blockifier/src/fee/receipt_test.rs +++ b/crates/blockifier/src/fee/receipt_test.rs @@ -1,5 +1,6 @@ use rstest::{fixture, rstest}; -use starknet_api::transaction::{L2ToL1Payload, ValidResourceBounds}; +use starknet_api::execution_resources::GasAmount; +use starknet_api::transaction::{GasVectorComputationMode, L2ToL1Payload}; use starknet_api::{invoke_tx_args, nonce}; use starknet_types_core::felt::Felt; @@ -17,7 +18,7 @@ use crate::fee::gas_usage::{ get_log_message_to_l1_emissions_cost, get_message_segment_length, }; -use crate::fee::resources::{GasVector, GasVectorComputationMode, StarknetResources}; +use crate::fee::resources::{GasVector, StarknetResources, StateResources}; use crate::state::cached_state::StateChangesCount; use crate::test_utils::contracts::FeatureContract; use crate::test_utils::initial_test_state::test_state; @@ -27,7 +28,7 @@ use crate::transaction::objects::HasRelatedFeeType; use crate::transaction::test_utils::{ account_invoke_tx, calculate_class_info_for_testing, - max_l1_resource_bounds, + create_resource_bounds, }; use crate::transaction::transactions::ExecutableTransaction; use crate::utils::{u128_from_usize, usize_from_u128}; @@ -51,14 +52,18 @@ fn versioned_constants() -> &'static VersionedConstants { // TODO(Aner, 29/01/24) Refactor with assert on GasVector objects. // TODO(Aner, 29/01/24) Refactor to replace match with if when formatting is nicer #[rstest] -fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool) { +fn test_calculate_tx_gas_usage_basic<'a>( + #[values(false, true)] use_kzg_da: bool, + #[values(GasVectorComputationMode::NoL2Gas, GasVectorComputationMode::All)] + gas_vector_computation_mode: GasVectorComputationMode, +) { // An empty transaction (a theoretical case for sanity check). let versioned_constants = VersionedConstants::create_for_account_testing(); let empty_tx_starknet_resources = StarknetResources::default(); let empty_tx_gas_usage_vector = empty_tx_starknet_resources.to_gas_vector( &versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas, + &gas_vector_computation_mode, ); assert_eq!(empty_tx_gas_usage_vector, GasVector::default()); @@ -70,23 +75,30 @@ fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool 0, 0, class_info.code_size(), - StateChangesCount::default(), + StateResources::default(), None, ExecutionSummary::default(), ); - let code_gas_cost = versioned_constants.archival_data_gas_costs.gas_per_code_byte - * versioned_constants.get_l1_to_l2_gas_price_ratio() - * u128_from_usize( - (class_info.bytecode_length() + class_info.sierra_program_length()) - * eth_gas_constants::WORD_WIDTH - + class_info.abi_length(), - ); - let manual_gas_vector = - GasVector { l1_gas: code_gas_cost.to_integer(), ..Default::default() }; + let gas_per_code_byte = versioned_constants + .get_archival_data_gas_costs(&gas_vector_computation_mode) + .gas_per_code_byte; + let code_gas_cost = GasAmount( + (gas_per_code_byte + * u128_from_usize( + (class_info.bytecode_length() + class_info.sierra_program_length()) + * eth_gas_constants::WORD_WIDTH + + class_info.abi_length(), + )) + .to_integer(), + ); + let manual_gas_vector = match gas_vector_computation_mode { + GasVectorComputationMode::NoL2Gas => GasVector::from_l1_gas(code_gas_cost), + GasVectorComputationMode::All => GasVector::from_l2_gas(code_gas_cost), + }; let declare_gas_usage_vector = declare_tx_starknet_resources.to_gas_vector( &versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas, + &gas_vector_computation_mode, ); assert_eq!(manual_gas_vector, declare_gas_usage_vector); } @@ -107,22 +119,30 @@ fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool calldata_length, signature_length, 0, - deploy_account_state_changes_count, + StateResources::new_for_testing(deploy_account_state_changes_count), None, ExecutionSummary::default(), ); - let calldata_and_signature_gas_cost = - versioned_constants.archival_data_gas_costs.gas_per_data_felt - * versioned_constants.get_l1_to_l2_gas_price_ratio() - * u128_from_usize(calldata_length + signature_length); - let manual_starknet_gas_usage = calldata_and_signature_gas_cost.to_integer(); - let manual_gas_vector = GasVector { l1_gas: manual_starknet_gas_usage, ..Default::default() } - + deploy_account_tx_starknet_resources.get_state_changes_cost(use_kzg_da); + let gas_per_data_felt = versioned_constants + .get_archival_data_gas_costs(&gas_vector_computation_mode) + .gas_per_data_felt; + let calldata_and_signature_gas_cost = (gas_per_data_felt + * u128_from_usize(calldata_length + signature_length)) + .to_integer() + .into(); + let manual_starknet_gas_usage_vector = match gas_vector_computation_mode { + GasVectorComputationMode::NoL2Gas => { + GasVector::from_l1_gas(calldata_and_signature_gas_cost) + } + GasVectorComputationMode::All => GasVector::from_l2_gas(calldata_and_signature_gas_cost), + }; + let manual_gas_vector = manual_starknet_gas_usage_vector + + deploy_account_tx_starknet_resources.state.to_gas_vector(use_kzg_da); let deploy_account_gas_usage_vector = deploy_account_tx_starknet_resources.to_gas_vector( &versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas, + &gas_vector_computation_mode, ); assert_eq!(manual_gas_vector, deploy_account_gas_usage_vector); @@ -133,33 +153,43 @@ fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool l1_handler_payload_size, signature_length, 0, - StateChangesCount::default(), + StateResources::default(), Some(l1_handler_payload_size), ExecutionSummary::default(), ); let l1_handler_gas_usage_vector = l1_handler_tx_starknet_resources.to_gas_vector( &versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas, + &gas_vector_computation_mode, ); // Manual calculation. let message_segment_length = get_message_segment_length(&[], Some(l1_handler_payload_size)); - let calldata_and_signature_gas_cost = - versioned_constants.archival_data_gas_costs.gas_per_data_felt - * versioned_constants.get_l1_to_l2_gas_price_ratio() - * u128_from_usize(l1_handler_payload_size + signature_length); - let manual_starknet_gas_usage = message_segment_length * eth_gas_constants::GAS_PER_MEMORY_WORD + let calldata_and_signature_gas_cost = (gas_per_data_felt + * u128_from_usize(l1_handler_payload_size + signature_length)) + .to_integer() + .into(); + let calldata_and_signature_gas_cost_vector = match gas_vector_computation_mode { + GasVectorComputationMode::NoL2Gas => { + GasVector::from_l1_gas(calldata_and_signature_gas_cost) + } + GasVectorComputationMode::All => GasVector::from_l2_gas(calldata_and_signature_gas_cost), + }; + let manual_starknet_l1_gas_usage = message_segment_length + * eth_gas_constants::GAS_PER_MEMORY_WORD + eth_gas_constants::GAS_PER_COUNTER_DECREASE + usize_from_u128( - get_consumed_message_to_l2_emissions_cost(Some(l1_handler_payload_size)).l1_gas, + get_consumed_message_to_l2_emissions_cost(Some(l1_handler_payload_size)).l1_gas.0, ) - .unwrap() - + usize_from_u128(calldata_and_signature_gas_cost.to_integer()).unwrap(); + .unwrap(); + let manual_starknet_l1_gas_usage_vector = + GasVector::from_l1_gas(u128_from_usize(manual_starknet_l1_gas_usage).into()); let manual_sharp_gas_usage = message_segment_length * eth_gas_constants::SHARP_GAS_PER_MEMORY_WORD; let manual_gas_computation = - GasVector::from_l1_gas(u128_from_usize(manual_starknet_gas_usage + manual_sharp_gas_usage)); + GasVector::from_l1_gas(u128_from_usize(manual_sharp_gas_usage).into()) + + manual_starknet_l1_gas_usage_vector + + calldata_and_signature_gas_cost_vector; assert_eq!(l1_handler_gas_usage_vector, manual_gas_computation); // Any transaction with L2-to-L1 messages. @@ -199,7 +229,7 @@ fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool 0, 0, 0, - l2_to_l1_state_changes_count, + StateResources::new_for_testing(l2_to_l1_state_changes_count), None, execution_summary.clone(), ); @@ -207,24 +237,25 @@ fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool let l2_to_l1_messages_gas_usage_vector = l2_to_l1_starknet_resources.to_gas_vector( &versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas, + &gas_vector_computation_mode, ); // Manual calculation. + // No L2 gas is used, so gas amount does not depend on gas vector computation mode. let message_segment_length = get_message_segment_length(&l2_to_l1_payload_lengths, None); let n_l2_to_l1_messages = l2_to_l1_payload_lengths.len(); let manual_starknet_gas_usage = message_segment_length * eth_gas_constants::GAS_PER_MEMORY_WORD + n_l2_to_l1_messages * eth_gas_constants::GAS_PER_ZERO_TO_NONZERO_STORAGE_SET - + usize_from_u128(get_log_message_to_l1_emissions_cost(&l2_to_l1_payload_lengths).l1_gas) + + usize_from_u128(get_log_message_to_l1_emissions_cost(&l2_to_l1_payload_lengths).l1_gas.0) .unwrap(); let manual_sharp_gas_usage = message_segment_length * eth_gas_constants::SHARP_GAS_PER_MEMORY_WORD - + usize_from_u128(l2_to_l1_starknet_resources.get_state_changes_cost(use_kzg_da).l1_gas) + + usize_from_u128(l2_to_l1_starknet_resources.state.to_gas_vector(use_kzg_da).l1_gas.0) .unwrap(); let manual_sharp_blob_gas_usage = - l2_to_l1_starknet_resources.get_state_changes_cost(use_kzg_da).l1_data_gas; + l2_to_l1_starknet_resources.state.to_gas_vector(use_kzg_da).l1_data_gas; let manual_gas_computation = GasVector { - l1_gas: u128_from_usize(manual_starknet_gas_usage + manual_sharp_gas_usage), + l1_gas: u128_from_usize(manual_starknet_gas_usage + manual_sharp_gas_usage).into(), l1_data_gas: manual_sharp_blob_gas_usage, ..Default::default() }; @@ -245,7 +276,7 @@ fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool 0, 0, 0, - storage_writes_state_changes_count, + StateResources::new_for_testing(storage_writes_state_changes_count), None, ExecutionSummary::default(), ); @@ -253,12 +284,12 @@ fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool let storage_writings_gas_usage_vector = storage_writes_starknet_resources.to_gas_vector( &versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas, + &gas_vector_computation_mode, ); // Manual calculation. - let manual_gas_computation = - storage_writes_starknet_resources.get_state_changes_cost(use_kzg_da); + // No L2 gas is used, so gas amount does not depend on gas vector computation mode. + let manual_gas_computation = storage_writes_starknet_resources.state.to_gas_vector(use_kzg_da); assert_eq!(manual_gas_computation, storage_writings_gas_usage_vector); @@ -274,7 +305,7 @@ fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool l1_handler_payload_size, signature_length, 0, - combined_state_changes_count, + StateResources::new_for_testing(combined_state_changes_count), Some(l1_handler_payload_size), execution_summary.clone(), ); @@ -282,7 +313,7 @@ fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool let gas_usage_vector = combined_cases_starknet_resources.to_gas_vector( &versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas, + &gas_vector_computation_mode, ); // Manual calculation. @@ -299,12 +330,10 @@ fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool + storage_writings_gas_usage_vector.l1_gas // l2_to_l1_messages_gas_usage and storage_writings_gas_usage got a discount each, while // the combined calculation got it once. - + u128_from_usize(fee_balance_discount), + + u128_from_usize(fee_balance_discount).into(), // Expected blob gas usage is from data availability only. - l1_data_gas: combined_cases_starknet_resources - .get_state_changes_cost(use_kzg_da) - .l1_data_gas, - ..Default::default() + l1_data_gas: combined_cases_starknet_resources.state.to_gas_vector(use_kzg_da).l1_data_gas, + l2_gas: l1_handler_gas_usage_vector.l2_gas, }; assert_eq!(expected_gas_vector, gas_usage_vector); @@ -312,14 +341,15 @@ fn test_calculate_tx_gas_usage_basic<'a>(#[values(false, true)] use_kzg_da: bool // Test that we exclude the fee token contract modification and adds the account’s balance change // in the state changes. -// TODO(Aner, 21/01/24) modify for 4844 (taking blob_gas into account). // TODO(Nimrod, 1/5/2024): Test regression w.r.t. all resources (including VM). (Only starknet // resources are taken into account). #[rstest] fn test_calculate_tx_gas_usage( - max_l1_resource_bounds: ValidResourceBounds, #[values(false, true)] use_kzg_da: bool, + #[values(GasVectorComputationMode::NoL2Gas, GasVectorComputationMode::All)] + gas_vector_computation_mode: GasVectorComputationMode, ) { + let max_resource_bounds = create_resource_bounds(&gas_vector_computation_mode); let account_cairo_version = CairoVersion::Cairo0; let test_contract_cairo_version = CairoVersion::Cairo0; let block_context = &BlockContext::create_for_account_testing_with_kzg(use_kzg_da); @@ -333,7 +363,7 @@ fn test_calculate_tx_gas_usage( let account_tx = account_invoke_tx(invoke_tx_args! { sender_address: account_contract_address, calldata: create_trivial_calldata(test_contract.get_instance_address(0)), - resource_bounds: max_l1_resource_bounds, + resource_bounds: max_resource_bounds, }); let calldata_length = account_tx.calldata_length(); let signature_length = account_tx.signature_length(); @@ -352,7 +382,7 @@ fn test_calculate_tx_gas_usage( calldata_length, signature_length, 0, - state_changes_count, + StateResources::new_for_testing(state_changes_count), None, ExecutionSummary::default(), ); @@ -361,12 +391,12 @@ fn test_calculate_tx_gas_usage( starknet_resources.to_gas_vector( versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas + &gas_vector_computation_mode ), tx_execution_info.receipt.resources.starknet_resources.to_gas_vector( versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas + &gas_vector_computation_mode ) ); @@ -383,7 +413,7 @@ fn test_calculate_tx_gas_usage( ); let account_tx = account_invoke_tx(invoke_tx_args! { - resource_bounds: max_l1_resource_bounds, + resource_bounds: max_resource_bounds, sender_address: account_contract_address, calldata: execute_calldata, nonce: nonce!(1_u8), @@ -402,26 +432,29 @@ fn test_calculate_tx_gas_usage( n_modified_contracts, n_compiled_class_hash_updates: 0, }; - + let execution_call_info = + &tx_execution_info.execute_call_info.expect("Execution call info should exist."); + let execution_summary = CallInfo::summarize_many(vec![execution_call_info].into_iter()); let starknet_resources = StarknetResources::new( calldata_length, signature_length, 0, - state_changes_count, + StateResources::new_for_testing(state_changes_count), None, - ExecutionSummary::default(), + // The transfer entrypoint emits an event - pass the call info to count its resources. + execution_summary, ); assert_eq!( starknet_resources.to_gas_vector( versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas + &gas_vector_computation_mode ), tx_execution_info.receipt.resources.starknet_resources.to_gas_vector( versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas + &gas_vector_computation_mode ) ); } diff --git a/crates/blockifier/src/fee/resources.rs b/crates/blockifier/src/fee/resources.rs index f7dceecea42..f6d99bb6073 100644 --- a/crates/blockifier/src/fee/resources.rs +++ b/crates/blockifier/src/fee/resources.rs @@ -1,6 +1,8 @@ use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use serde::Serialize; -use starknet_api::transaction::Fee; +use starknet_api::core::ContractAddress; +use starknet_api::execution_resources::GasAmount; +use starknet_api::transaction::{Fee, GasVectorComputationMode}; use crate::context::TransactionContext; use crate::execution::call_info::{EventSummary, ExecutionSummary}; @@ -13,7 +15,7 @@ use crate::fee::gas_usage::{ get_message_segment_length, get_onchain_data_segment_length, }; -use crate::state::cached_state::StateChangesCount; +use crate::state::cached_state::{StateChanges, StateChangesCount}; use crate::transaction::errors::TransactionFeeError; use crate::transaction::objects::HasRelatedFeeType; use crate::utils::{u128_div_ceil, u128_from_usize}; @@ -36,9 +38,9 @@ impl TransactionResources { versioned_constants: &VersionedConstants, use_kzg_da: bool, computation_mode: &GasVectorComputationMode, - ) -> TransactionFeeResult { - Ok(self.starknet_resources.to_gas_vector(versioned_constants, use_kzg_da, computation_mode) - + self.computation.to_gas_vector(versioned_constants, computation_mode)?) + ) -> GasVector { + self.starknet_resources.to_gas_vector(versioned_constants, use_kzg_da, computation_mode) + + self.computation.to_gas_vector(versioned_constants, computation_mode) } } @@ -56,7 +58,7 @@ impl ComputationResources { &self, versioned_constants: &VersionedConstants, computation_mode: &GasVectorComputationMode, - ) -> TransactionFeeResult { + ) -> GasVector { get_vm_resources_cost( versioned_constants, &self.vm_resources, @@ -77,7 +79,7 @@ impl ComputationResources { pub struct StarknetResources { pub archival_data: ArchivalDataResources, pub messages: MessageResources, - pub state_changes_for_fee: StateChangesCount, + pub state: StateResources, } impl StarknetResources { @@ -85,7 +87,7 @@ impl StarknetResources { calldata_length: usize, signature_length: usize, code_size: usize, - state_changes_count: StateChangesCount, + state_resources: StateResources, l1_handler_payload_size: Option, execution_summary_without_fee_transfer: ExecutionSummary, ) -> Self { @@ -101,7 +103,7 @@ impl StarknetResources { execution_summary_without_fee_transfer.l2_to_l1_payload_lengths, l1_handler_payload_size, ), - state_changes_for_fee: state_changes_count, + state: state_resources, } } @@ -114,12 +116,36 @@ impl StarknetResources { mode: &GasVectorComputationMode, ) -> GasVector { self.archival_data.to_gas_vector(versioned_constants, mode) - + self.get_state_changes_cost(use_kzg_da) + + self.state.to_gas_vector(use_kzg_da) + self.messages.to_gas_vector() } +} + +#[cfg_attr(feature = "transaction_serde", derive(serde::Serialize, serde::Deserialize))] +#[derive(Clone, Debug, Default, PartialEq)] +pub struct StateResources { + state_changes_for_fee: StateChangesCount, +} + +impl StateResources { + pub fn new( + state_changes: &StateChanges, + sender_address: Option, + fee_token_address: ContractAddress, + ) -> Self { + Self { + state_changes_for_fee: state_changes + .count_for_fee_charge(sender_address, fee_token_address), + } + } + + #[cfg(any(test, feature = "testing"))] + pub fn new_for_testing(state_changes_for_fee: StateChangesCount) -> Self { + Self { state_changes_for_fee } + } /// Returns the gas cost of the transaction's state changes. - pub fn get_state_changes_cost(&self, use_kzg_da: bool) -> GasVector { + pub fn to_gas_vector(&self, use_kzg_da: bool) -> GasVector { // TODO(Nimrod, 29/3/2024): delete `get_da_gas_cost` and move it's logic here. get_da_gas_cost(&self.state_changes_for_fee, use_kzg_da) } @@ -174,24 +200,26 @@ impl ArchivalDataResources { fn get_calldata_and_signature_gas_cost( &self, archival_gas_costs: &ArchivalDataGasCosts, - ) -> u128 { + ) -> GasAmount { // TODO(Avi, 20/2/2024): Calculate the number of bytes instead of the number of felts. let total_data_size = u128_from_usize(self.calldata_length + self.signature_length); - (archival_gas_costs.gas_per_data_felt * total_data_size).to_integer() + GasAmount((archival_gas_costs.gas_per_data_felt * total_data_size).to_integer()) } /// Returns the cost of declared class codes in L1/L2 gas units, depending on the mode. - fn get_code_gas_cost(&self, archival_gas_costs: &ArchivalDataGasCosts) -> u128 { - (archival_gas_costs.gas_per_code_byte * u128_from_usize(self.code_size)).to_integer() + fn get_code_gas_cost(&self, archival_gas_costs: &ArchivalDataGasCosts) -> GasAmount { + (archival_gas_costs.gas_per_code_byte * u128_from_usize(self.code_size)).to_integer().into() } /// Returns the cost of the transaction's emmited events in L1/L2 gas units, depending on the /// mode. - fn get_events_gas_cost(&self, archival_gas_costs: &ArchivalDataGasCosts) -> u128 { - (archival_gas_costs.gas_per_data_felt - * (archival_gas_costs.event_key_factor * self.event_summary.total_event_keys - + self.event_summary.total_event_data_size)) - .to_integer() + fn get_events_gas_cost(&self, archival_gas_costs: &ArchivalDataGasCosts) -> GasAmount { + GasAmount( + (archival_gas_costs.gas_per_data_felt + * (archival_gas_costs.event_key_factor * self.event_summary.total_event_keys + + self.event_summary.total_event_data_size)) + .to_integer(), + ) } } @@ -217,9 +245,12 @@ impl MessageResources { /// both Starknet and SHARP contracts. pub fn to_gas_vector(&self) -> GasVector { let starknet_gas_usage = self.get_starknet_gas_cost(); - let sharp_gas_usage = GasVector::from_l1_gas(u128_from_usize( - self.message_segment_length * eth_gas_constants::SHARP_GAS_PER_MEMORY_WORD, - )); + let sharp_gas_usage = GasVector::from_l1_gas( + u128_from_usize( + self.message_segment_length * eth_gas_constants::SHARP_GAS_PER_MEMORY_WORD, + ) + .into(), + ); starknet_gas_usage + sharp_gas_usage } @@ -241,7 +272,8 @@ impl MessageResources { // message but we ignore it since refunded gas cannot be used for the current // transaction execution). + n_l1_to_l2_messages * eth_gas_constants::GAS_PER_COUNTER_DECREASE, - ), + ) + .into(), ) + get_consumed_message_to_l2_emissions_cost(self.l1_handler_payload_size) + get_log_message_to_l1_emissions_cost(&self.l2_to_l1_payload_lengths) } @@ -249,30 +281,39 @@ impl MessageResources { #[cfg_attr(feature = "transaction_serde", derive(serde::Deserialize))] #[derive( - derive_more::Add, derive_more::Sum, Clone, Copy, Debug, Default, Eq, PartialEq, Serialize, + derive_more::Add, + derive_more::Sum, + derive_more::AddAssign, + Clone, + Copy, + Debug, + Default, + Eq, + PartialEq, + Serialize, )] pub struct GasVector { - pub l1_gas: u128, - pub l1_data_gas: u128, - pub l2_gas: u128, + pub l1_gas: GasAmount, + pub l1_data_gas: GasAmount, + pub l2_gas: GasAmount, } impl GasVector { - pub fn from_l1_gas(l1_gas: u128) -> Self { + pub fn from_l1_gas(l1_gas: GasAmount) -> Self { Self { l1_gas, ..Default::default() } } - pub fn from_l1_data_gas(l1_data_gas: u128) -> Self { + pub fn from_l1_data_gas(l1_data_gas: GasAmount) -> Self { Self { l1_data_gas, ..Default::default() } } - pub fn from_l2_gas(l2_gas: u128) -> Self { + pub fn from_l2_gas(l2_gas: GasAmount) -> Self { Self { l2_gas, ..Default::default() } } /// Computes the cost (in fee token units) of the gas vector (saturating on overflow). pub fn saturated_cost(&self, gas_price: u128, blob_gas_price: u128) -> Fee { - let l1_gas_cost = self.l1_gas.checked_mul(gas_price).unwrap_or_else(|| { + let l1_gas_cost = self.l1_gas.0.checked_mul(gas_price).unwrap_or_else(|| { log::warn!( "L1 gas cost overflowed: multiplication of {} by {} resulted in overflow.", self.l1_gas, @@ -280,14 +321,15 @@ impl GasVector { ); u128::MAX }); - let l1_data_gas_cost = self.l1_data_gas.checked_mul(blob_gas_price).unwrap_or_else(|| { - log::warn!( - "L1 blob gas cost overflowed: multiplication of {} by {} resulted in overflow.", - self.l1_data_gas, - blob_gas_price - ); - u128::MAX - }); + let l1_data_gas_cost = + self.l1_data_gas.0.checked_mul(blob_gas_price).unwrap_or_else(|| { + log::warn!( + "L1 blob gas cost overflowed: multiplication of {} by {} resulted in overflow.", + self.l1_data_gas, + blob_gas_price + ); + u128::MAX + }); let total = l1_gas_cost.checked_add(l1_data_gas_cost).unwrap_or_else(|| { log::warn!( "Total gas cost overflowed: addition of {} and {} resulted in overflow.", @@ -306,17 +348,14 @@ impl GasVector { /// X non-data-related gas consumption and Y bytes of data, in non-blob mode, would /// cost (X + 16*Y) units of gas. Applying the discount ratio to the data-related /// summand, we get total_gas = (X + Y * DGP / GP). - pub fn to_discounted_l1_gas(&self, tx_context: &TransactionContext) -> u128 { + /// If this function is called with kzg_flag==false, then l1_data_gas==0, and this dicount + /// function does nothing. + pub fn to_discounted_l1_gas(&self, tx_context: &TransactionContext) -> GasAmount { let gas_prices = &tx_context.block_context.block_info.gas_prices; let fee_type = tx_context.tx_info.fee_type(); let gas_price = gas_prices.get_l1_gas_price_by_fee_type(&fee_type); let data_gas_price = gas_prices.get_l1_data_gas_price_by_fee_type(&fee_type); - self.l1_gas + u128_div_ceil(self.l1_data_gas * u128::from(data_gas_price), gas_price) + self.l1_gas + + u128_div_ceil(self.l1_data_gas.0 * u128::from(data_gas_price), gas_price).into() } } - -#[derive(Debug, PartialEq)] -pub enum GasVectorComputationMode { - All, - NoL2Gas, -} diff --git a/crates/blockifier/src/test_utils.rs b/crates/blockifier/src/test_utils.rs index 1510ad775bb..684d0f89682 100644 --- a/crates/blockifier/src/test_utils.rs +++ b/crates/blockifier/src/test_utils.rs @@ -14,8 +14,14 @@ use std::path::PathBuf; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use starknet_api::core::{ClassHash, ContractAddress, PatriciaKey}; +use starknet_api::execution_resources::GasAmount; use starknet_api::state::StorageKey; -use starknet_api::transaction::{Calldata, ContractAddressSalt, TransactionVersion}; +use starknet_api::transaction::{ + Calldata, + ContractAddressSalt, + GasVectorComputationMode, + TransactionVersion, +}; use starknet_api::{contract_address, felt, patricia_key}; use starknet_types_core::felt::Felt; @@ -24,8 +30,7 @@ use crate::execution::call_info::ExecutionSummary; use crate::execution::deprecated_syscalls::hint_processor::SyscallCounter; use crate::execution::entry_point::CallEntryPoint; use crate::execution::syscalls::SyscallSelector; -use crate::fee::resources::StarknetResources; -use crate::state::cached_state::StateChangesCount; +use crate::fee::resources::{GasVector, StarknetResources, StateResources}; use crate::test_utils::contracts::FeatureContract; use crate::transaction::transaction_types::TransactionType; use crate::utils::{const_max, u128_from_usize}; @@ -90,10 +95,10 @@ pub fn test_erc20_sequencer_balance_key() -> StorageKey { // The max_fee / resource bounds used for txs in this test. pub const MAX_L1_GAS_AMOUNT: u64 = 1000000; #[allow(clippy::as_conversions)] -pub const MAX_L1_GAS_AMOUNT_U128: u128 = MAX_L1_GAS_AMOUNT as u128; +pub const MAX_L1_GAS_AMOUNT_U128: GasAmount = GasAmount(MAX_L1_GAS_AMOUNT as u128); pub const MAX_L1_GAS_PRICE: u128 = DEFAULT_STRK_L1_GAS_PRICE; -pub const MAX_RESOURCE_COMMITMENT: u128 = MAX_L1_GAS_AMOUNT_U128 * MAX_L1_GAS_PRICE; -pub const MAX_FEE: u128 = MAX_L1_GAS_AMOUNT_U128 * DEFAULT_ETH_L1_GAS_PRICE; +pub const MAX_RESOURCE_COMMITMENT: u128 = MAX_L1_GAS_AMOUNT_U128.0 * MAX_L1_GAS_PRICE; +pub const MAX_FEE: u128 = MAX_L1_GAS_AMOUNT_U128.0 * DEFAULT_ETH_L1_GAS_PRICE; // The amount of test-token allocated to the account in this test, set to a multiple of the max // amount deprecated / non-deprecated transactions commit to paying. @@ -217,7 +222,7 @@ macro_rules! check_entry_point_execution_error_for_custom_hint { } #[macro_export] -macro_rules! check_transaction_execution_error_inner { +macro_rules! check_tx_execution_error_inner { ($error:expr, $expected_hint:expr, $validate_constructor:expr $(,)?) => { if $validate_constructor { match $error { @@ -246,9 +251,9 @@ macro_rules! check_transaction_execution_error_inner { } #[macro_export] -macro_rules! check_transaction_execution_error_for_custom_hint { +macro_rules! check_tx_execution_error_for_custom_hint { ($error:expr, $expected_hint:expr, $validate_constructor:expr $(,)?) => { - $crate::check_transaction_execution_error_inner!( + $crate::check_tx_execution_error_inner!( $error, Some($expected_hint), $validate_constructor, @@ -259,11 +264,11 @@ macro_rules! check_transaction_execution_error_for_custom_hint { /// Checks that a given error is an assertion error with the expected message. /// Formatted for test_validate_accounts_tx. #[macro_export] -macro_rules! check_transaction_execution_error_for_invalid_scenario { +macro_rules! check_tx_execution_error_for_invalid_scenario { ($cairo_version:expr, $error:expr, $validate_constructor:expr $(,)?) => { match $cairo_version { CairoVersion::Cairo0 => { - $crate::check_transaction_execution_error_inner!( + $crate::check_tx_execution_error_inner!( $error, None::<&str>, $validate_constructor, @@ -287,7 +292,7 @@ macro_rules! check_transaction_execution_error_for_invalid_scenario { pub fn get_syscall_resources(syscall_selector: SyscallSelector) -> ExecutionResources { let versioned_constants = VersionedConstants::create_for_testing(); let syscall_counter: SyscallCounter = HashMap::from([(syscall_selector, 1)]); - versioned_constants.get_additional_os_syscall_resources(&syscall_counter).unwrap() + versioned_constants.get_additional_os_syscall_resources(&syscall_counter) } pub fn get_tx_resources(tx_type: TransactionType) -> ExecutionResources { @@ -296,12 +301,12 @@ pub fn get_tx_resources(tx_type: TransactionType) -> ExecutionResources { 1, 0, 0, - StateChangesCount::default(), + StateResources::default(), None, ExecutionSummary::default(), ); - versioned_constants.get_additional_os_tx_resources(tx_type, &starknet_resources, false).unwrap() + versioned_constants.get_additional_os_tx_resources(tx_type, &starknet_resources, false) } /// Creates the calldata for the Cairo function "test_deploy" in the featured contract TestContract. @@ -384,3 +389,16 @@ pub fn update_json_value(base: &mut serde_json::Value, update: serde_json::Value _ => panic!("Both base and update should be of type serde_json::Value::Object."), } } + +pub fn gas_vector_from_vm_usage( + vm_usage_in_l1_gas: GasAmount, + computation_mode: &GasVectorComputationMode, + versioned_constants: &VersionedConstants, +) -> GasVector { + match computation_mode { + GasVectorComputationMode::NoL2Gas => GasVector::from_l1_gas(vm_usage_in_l1_gas), + GasVectorComputationMode::All => GasVector::from_l2_gas( + versioned_constants.convert_l1_to_l2_gas_amount_round_up(vm_usage_in_l1_gas), + ), + } +} diff --git a/crates/blockifier/src/test_utils/struct_impls.rs b/crates/blockifier/src/test_utils/struct_impls.rs index 4e13057440b..e3731030264 100644 --- a/crates/blockifier/src/test_utils/struct_impls.rs +++ b/crates/blockifier/src/test_utils/struct_impls.rs @@ -5,7 +5,13 @@ use serde_json::Value; use starknet_api::block::{BlockHash, BlockNumber, BlockTimestamp}; use starknet_api::core::{ChainId, ClassHash, ContractAddress, Nonce, PatriciaKey}; use starknet_api::hash::StarkHash; -use starknet_api::transaction::{Calldata, Fee, TransactionHash, TransactionVersion}; +use starknet_api::transaction::{ + Calldata, + Fee, + GasVectorComputationMode, + TransactionHash, + TransactionVersion, +}; use starknet_api::{calldata, contract_address, felt, patricia_key}; use starknet_types_core::felt::Felt; @@ -23,7 +29,7 @@ use crate::execution::entry_point::{ EntryPointExecutionResult, }; use crate::fee::fee_utils::get_fee_by_gas_vector; -use crate::fee::resources::{GasVectorComputationMode, TransactionFeeResult, TransactionResources}; +use crate::fee::resources::TransactionResources; use crate::state::state_api::State; use crate::test_utils::{ get_raw_contract_class, @@ -113,17 +119,13 @@ impl VersionedConstants { } impl TransactionResources { - pub fn calculate_tx_fee( - &self, - block_context: &BlockContext, - fee_type: &FeeType, - ) -> TransactionFeeResult { + pub fn calculate_tx_fee(&self, block_context: &BlockContext, fee_type: &FeeType) -> Fee { let gas_vector = self.to_gas_vector( &block_context.versioned_constants, block_context.block_info.use_kzg_da, &GasVectorComputationMode::NoL2Gas, - )?; - Ok(get_fee_by_gas_vector(&block_context.block_info, gas_vector, fee_type)) + ); + get_fee_by_gas_vector(&block_context.block_info, gas_vector, fee_type) } } @@ -131,7 +133,7 @@ impl GasCosts { pub fn create_for_testing_from_subset(subset_of_os_constants: &str) -> Self { let subset_of_os_constants: Value = serde_json::from_str(subset_of_os_constants).unwrap(); let mut os_constants: Value = - serde_json::from_str::(VERSIONED_CONSTANTS_LATEST_JSON) + serde_json::from_str::(VERSIONED_CONSTANTS_LATEST_JSON.as_str()) .unwrap() .get("os_constants") .unwrap() diff --git a/crates/blockifier/src/test_utils/transfers_generator.rs b/crates/blockifier/src/test_utils/transfers_generator.rs index 2018d2eb585..5e8c9ab7e9b 100644 --- a/crates/blockifier/src/test_utils/transfers_generator.rs +++ b/crates/blockifier/src/test_utils/transfers_generator.rs @@ -39,7 +39,7 @@ pub struct TransfersGeneratorConfig { pub n_txs: usize, pub randomization_seed: u64, pub cairo_version: CairoVersion, - pub transaction_version: TransactionVersion, + pub tx_version: TransactionVersion, pub recipient_generator_type: RecipientGeneratorType, pub concurrency_config: ConcurrencyConfig, } @@ -53,7 +53,7 @@ impl Default for TransfersGeneratorConfig { n_txs: N_TXS, randomization_seed: RANDOMIZATION_SEED, cairo_version: CAIRO_VERSION, - transaction_version: TRANSACTION_VERSION, + tx_version: TRANSACTION_VERSION, recipient_generator_type: RECIPIENT_GENERATOR_TYPE, concurrency_config: ConcurrencyConfig { enabled: CONCURRENCY_MODE, @@ -153,7 +153,7 @@ impl TransfersGenerator { self.sender_index = (self.sender_index + 1) % self.account_addresses.len(); let account_tx = self.generate_transfer(sender_address, recipient_address); - txs.push(Transaction::AccountTransaction(account_tx)); + txs.push(Transaction::Account(account_tx)); } let results = self.executor.execute_txs(&txs); assert_eq!(results.len(), self.config.n_txs); @@ -172,12 +172,12 @@ impl TransfersGenerator { let nonce = self.nonce_manager.next(sender_address); let entry_point_selector = selector_from_name(TRANSFER_ENTRY_POINT_NAME); - let contract_address = if self.config.transaction_version == TransactionVersion::ONE { + let contract_address = if self.config.tx_version == TransactionVersion::ONE { *self.chain_info.fee_token_addresses.eth_fee_token_address.0.key() - } else if self.config.transaction_version == TransactionVersion::THREE { + } else if self.config.tx_version == TransactionVersion::THREE { *self.chain_info.fee_token_addresses.strk_fee_token_address.0.key() } else { - panic!("Unsupported transaction version: {:?}", self.config.transaction_version) + panic!("Unsupported transaction version: {:?}", self.config.tx_version) }; let execute_calldata = calldata![ @@ -193,7 +193,7 @@ impl TransfersGenerator { max_fee: Fee(self.config.max_fee), sender_address, calldata: execute_calldata, - version: self.config.transaction_version, + version: self.config.tx_version, nonce, }); AccountTransaction::Invoke(tx) diff --git a/crates/blockifier/src/transaction/account_transaction.rs b/crates/blockifier/src/transaction/account_transaction.rs index 2409614ebfc..5aaba2b6bd7 100644 --- a/crates/blockifier/src/transaction/account_transaction.rs +++ b/crates/blockifier/src/transaction/account_transaction.rs @@ -2,14 +2,17 @@ use std::sync::Arc; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; use starknet_api::calldata; -use starknet_api::core::{ContractAddress, EntryPointSelector, Nonce}; +use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; +use starknet_api::data_availability::DataAvailabilityMode; use starknet_api::deprecated_contract_class::EntryPointType; use starknet_api::transaction::Resource::{L1DataGas, L1Gas, L2Gas}; use starknet_api::transaction::{ + AccountDeploymentData, AllResourceBounds, Calldata, Fee, - ResourceBounds, + PaymasterData, + Tip, TransactionHash, TransactionSignature, TransactionVersion, @@ -74,7 +77,7 @@ mod flavors_test; mod post_execution_test; /// Represents a paid Starknet transaction. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, derive_more::From)] pub enum AccountTransaction { Declare(DeclareTransaction), DeployAccount(DeployAccountTransaction), @@ -156,7 +159,15 @@ impl HasRelatedFeeType for AccountTransaction { } impl AccountTransaction { - implement_account_tx_inner_getters!((signature, TransactionSignature), (nonce, Nonce)); + implement_account_tx_inner_getters!( + (signature, TransactionSignature), + (nonce, Nonce), + (resource_bounds, ValidResourceBounds), + (tip, Tip), + (nonce_data_availability_mode, DataAvailabilityMode), + (fee_data_availability_mode, DataAvailabilityMode), + (paymaster_data, PaymasterData) + ); pub fn sender_address(&self) -> ContractAddress { match self { @@ -166,6 +177,22 @@ impl AccountTransaction { } } + pub fn class_hash(&self) -> Option { + match self { + Self::Declare(tx) => Some(tx.tx.class_hash()), + Self::DeployAccount(tx) => Some(tx.tx.class_hash()), + Self::Invoke(_) => None, + } + } + + pub fn account_deployment_data(&self) -> Option { + match self { + Self::Declare(tx) => Some(tx.tx.account_deployment_data().clone()), + Self::DeployAccount(_) => None, + Self::Invoke(tx) => Some(tx.tx.account_deployment_data().clone()), + } + } + // TODO(nir, 01/11/2023): Consider instantiating CommonAccountFields in AccountTransaction. pub fn tx_type(&self) -> TransactionType { match self { @@ -280,90 +307,79 @@ impl AccountTransaction { &self, tx_context: &TransactionContext, ) -> TransactionPreValidationResult<()> { - let minimal_l1_gas_amount_vector = estimate_minimal_gas_vector( + // TODO(Aner): seprate to cases based on context.resource_bounds type + let minimal_gas_amount_vector = estimate_minimal_gas_vector( &tx_context.block_context, self, &tx_context.get_gas_vector_computation_mode(), - )?; - // TODO(Aner, 30/01/24): modify once data gas limit is enforced. - let minimal_l1_gas_amount = minimal_l1_gas_amount_vector.to_discounted_l1_gas(tx_context); - + ); let TransactionContext { block_context, tx_info } = tx_context; let block_info = &block_context.block_info; let fee_type = &tx_info.fee_type(); match tx_info { - TransactionInfo::Current(context) => match &context.resource_bounds { - ValidResourceBounds::L1Gas(ResourceBounds { - max_amount: max_l1_gas_amount, - max_price_per_unit: max_l1_gas_price, - }) => { - let max_l1_gas_amount_as_u128: u128 = (*max_l1_gas_amount).into(); - if max_l1_gas_amount_as_u128 < minimal_l1_gas_amount { + TransactionInfo::Current(context) => { + let resources_amount_tuple = match &context.resource_bounds { + ValidResourceBounds::L1Gas(l1_gas_resource_bounds) => vec![( + L1Gas, + l1_gas_resource_bounds, + minimal_gas_amount_vector.to_discounted_l1_gas(tx_context), + u128::from(block_info.gas_prices.get_l1_gas_price_by_fee_type(fee_type)), + )], + ValidResourceBounds::AllResources(AllResourceBounds { + l1_gas: l1_gas_resource_bounds, + l2_gas: l2_gas_resource_bounds, + l1_data_gas: l1_data_gas_resource_bounds, + }) => { + let GasPricesForFeeType { l1_gas_price, l1_data_gas_price, l2_gas_price } = + block_info.gas_prices.get_gas_prices_by_fee_type(fee_type); + vec![ + ( + L1Gas, + l1_gas_resource_bounds, + minimal_gas_amount_vector.l1_gas, + l1_gas_price.into(), + ), + ( + L1DataGas, + l1_data_gas_resource_bounds, + minimal_gas_amount_vector.l1_data_gas, + l1_data_gas_price.into(), + ), + ( + L2Gas, + l2_gas_resource_bounds, + minimal_gas_amount_vector.l2_gas, + l2_gas_price.into(), + ), + ] + } + }; + for (resource, resource_bounds, minimal_gas_amount, actual_gas_price) in + resources_amount_tuple + { + // TODO(Aner): refactor to indicate both amount and price are too low. + // TODO(Aner): refactor to return all amounts that are too low. + if minimal_gas_amount > resource_bounds.max_amount.into() { return Err(TransactionFeeError::MaxGasAmountTooLow { - resource: L1Gas, - max_gas_amount: *max_l1_gas_amount, - // TODO(Ori, 1/2/2024): Write an indicative expect message - // explaining why the conversion - // works. - minimal_gas_amount: (minimal_l1_gas_amount - .try_into() - .expect("Failed to convert u128 to u64.")), + resource, + max_gas_amount: resource_bounds.max_amount.into(), + minimal_gas_amount, })?; } - - let actual_l1_gas_price = - block_info.gas_prices.get_l1_gas_price_by_fee_type(fee_type); - if *max_l1_gas_price < actual_l1_gas_price.into() { + // TODO(Aner): refactor to return all prices that are too low. + if resource_bounds.max_price_per_unit < actual_gas_price { return Err(TransactionFeeError::MaxGasPriceTooLow { - resource: L1Gas, - max_gas_price: *max_l1_gas_price, - actual_gas_price: actual_l1_gas_price.into(), + resource, + max_gas_price: resource_bounds.max_price_per_unit, + actual_gas_price, })?; } } - ValidResourceBounds::AllResources(AllResourceBounds { - l1_gas, - l2_gas, - l1_data_gas, - }) => { - let max_l1_gas_amount_as_u128: u128 = l1_gas.max_amount.into(); - if max_l1_gas_amount_as_u128 < minimal_l1_gas_amount { - return Err(TransactionFeeError::MaxGasAmountTooLow { - resource: L1Gas, - max_gas_amount: l1_gas.max_amount, - // TODO(Ori, 1/2/2024): Write an indicative expect message - // explaining why the conversion - // works. - minimal_gas_amount: (minimal_l1_gas_amount - .try_into() - .expect("Failed to convert u128 to u64.")), - })?; - } - // TODO(Aner): Add checks for minimal_l1_data_gas and minimal_l2_gas. - - let GasPricesForFeeType { l1_gas_price, l1_data_gas_price, l2_gas_price } = - block_info.gas_prices.get_gas_prices_by_fee_type(fee_type); - // TODO!(Aner): Add tests for l1_data_gas_price and l2_gas_price. - for (resource, max_gas_price, actual_gas_price) in [ - (L1Gas, l1_gas.max_price_per_unit, l1_gas_price.into()), - (L1DataGas, l1_data_gas.max_price_per_unit, l1_data_gas_price.into()), - (L2Gas, l2_gas.max_price_per_unit, l2_gas_price.into()), - ] { - // TODO(Aner): refactor to return all prices that are too low. - if max_gas_price < actual_gas_price { - return Err(TransactionFeeError::MaxGasPriceTooLow { - resource, - max_gas_price, - actual_gas_price, - })?; - } - } - } - }, + } TransactionInfo::Deprecated(context) => { let max_fee = context.max_fee; let min_fee = - get_fee_by_gas_vector(block_info, minimal_l1_gas_amount_vector, fee_type); + get_fee_by_gas_vector(block_info, minimal_gas_amount_vector, fee_type); if max_fee < min_fee { return Err(TransactionFeeError::MaxFeeTooLow { min_fee, max_fee })?; } @@ -418,15 +434,12 @@ impl AccountTransaction { fn assert_actual_fee_in_bounds(tx_context: &Arc, actual_fee: Fee) { match &tx_context.tx_info { TransactionInfo::Current(context) => { - let ResourceBounds { - max_amount: max_l1_gas_amount, - max_price_per_unit: max_l1_gas_price, - } = context.l1_resource_bounds(); - if actual_fee > Fee(u128::from(max_l1_gas_amount) * max_l1_gas_price) { + let max_fee = context.resource_bounds.max_possible_fee(); + if actual_fee > max_fee { panic!( - "Actual fee {:#?} exceeded bounds; max amount is {:#?}, max price is - {:#?}.", - actual_fee, max_l1_gas_amount, max_l1_gas_price + "Actual fee {:#?} exceeded bounds; max possible fee is {:#?} (computed \ + from {:#?}).", + actual_fee, max_fee, context.resource_bounds ); } } @@ -442,7 +455,6 @@ impl AccountTransaction { } fn handle_fee( - &self, state: &mut TransactionalState<'_, S>, tx_context: Arc, actual_fee: Fee, @@ -454,7 +466,6 @@ impl AccountTransaction { return Ok(None); } - // TODO(Amos, 8/04/2024): Add test for this assert. Self::assert_actual_fee_in_bounds(&tx_context, actual_fee); let fee_transfer_call_info = if concurrency_mode && !tx_context.is_sequencer_the_sender() { @@ -601,7 +612,7 @@ impl AccountTransaction { &resources, CallInfo::summarize_many(validate_call_info.iter().chain(execute_call_info.iter())), 0, - )?; + ); let post_execution_report = PostExecutionReport::new(state, &tx_context, &tx_receipt, charge_fee)?; @@ -668,7 +679,7 @@ impl AccountTransaction { &resources, CallInfo::summarize_many(validate_call_info.iter()), execution_steps_consumed, - )?; + ); match execution_result { Ok(execute_call_info) => { @@ -686,7 +697,7 @@ impl AccountTransaction { validate_call_info.iter().chain(execute_call_info.iter()), ), 0, - )?; + ); // Post-execution checks. let post_execution_report = PostExecutionReport::new( &mut execution_state, @@ -813,7 +824,7 @@ impl ExecutableTransaction for AccountTransaction { execution_flags.validate, execution_flags.charge_fee, )?; - let fee_transfer_call_info = self.handle_fee( + let fee_transfer_call_info = Self::handle_fee( state, tx_context, final_fee, @@ -909,6 +920,7 @@ impl ValidatableTransaction for AccountTransaction { initial_gas: *remaining_gas, }; + // Note that we allow a revert here and we handle it bellow to get a better error message. let validate_call_info = validate_call.execute(state, resources, &mut context).map_err(|error| { TransactionExecutionError::ValidateTransactionError { @@ -927,7 +939,6 @@ impl ValidatableTransaction for AccountTransaction { let expected_retdata = retdata![Felt::from_hex(constants::VALIDATE_RETDATA)?]; if validate_call_info.execution.failed { - // TODO(ilya): Add a test for this case. return Err(TransactionExecutionError::PanicInValidate { panic_reason: validate_call_info.execution.retdata, }); diff --git a/crates/blockifier/src/transaction/account_transactions_test.rs b/crates/blockifier/src/transaction/account_transactions_test.rs index f1a89307fb3..54e03860609 100644 --- a/crates/blockifier/src/transaction/account_transactions_test.rs +++ b/crates/blockifier/src/transaction/account_transactions_test.rs @@ -6,16 +6,20 @@ use cairo_vm::vm::runners::cairo_runner::ResourceTracker; use pretty_assertions::assert_eq; use rstest::rstest; use starknet_api::core::{calculate_contract_address, ClassHash, ContractAddress, PatriciaKey}; +use starknet_api::execution_resources::GasAmount; use starknet_api::hash::StarkHash; use starknet_api::state::StorageKey; use starknet_api::test_utils::invoke::InvokeTxArgs; use starknet_api::test_utils::NonceManager; use starknet_api::transaction::{ + AllResourceBounds, Calldata, ContractAddressSalt, DeclareTransactionV2, Fee, + GasVectorComputationMode, Resource, + ResourceBounds, TransactionHash, TransactionVersion, ValidResourceBounds, @@ -39,14 +43,13 @@ use crate::abi::abi_utils::{ get_storage_var_address, selector_from_name, }; -use crate::check_transaction_execution_error_for_invalid_scenario; +use crate::check_tx_execution_error_for_invalid_scenario; use crate::context::BlockContext; use crate::execution::contract_class::{ContractClass, ContractClassV1}; use crate::execution::entry_point::EntryPointExecutionContext; use crate::execution::syscalls::SyscallSelector; use crate::fee::fee_utils::{get_fee_by_gas_vector, get_sequencer_balance_keys}; use crate::fee::gas_usage::estimate_minimal_gas_vector; -use crate::fee::resources::{GasVector, GasVectorComputationMode}; use crate::state::cached_state::{StateChangesCount, TransactionalState}; use crate::state::state_api::{State, StateReader}; use crate::test_utils::contracts::FeatureContract; @@ -58,7 +61,6 @@ use crate::test_utils::{ create_trivial_calldata, get_syscall_resources, get_tx_resources, - u64_from_usize, CairoVersion, BALANCE, DEFAULT_STRK_L1_GAS_PRICE, @@ -84,6 +86,7 @@ use crate::transaction::test_utils::{ }; use crate::transaction::transaction_types::TransactionType; use crate::transaction::transactions::{DeclareTransaction, ExecutableTransaction, ExecutionFlags}; +use crate::utils::u128_from_usize; #[rstest] fn test_circuit(block_context: BlockContext, max_l1_resource_bounds: ValidResourceBounds) { @@ -116,7 +119,6 @@ fn test_circuit(block_context: BlockContext, max_l1_resource_bounds: ValidResour .unwrap(); assert!(tx_execution_info.revert_error.is_none()); - assert_eq!(tx_execution_info.receipt.gas, GasVector::from_l1_gas(6866)); } #[rstest] @@ -155,7 +157,6 @@ fn test_rc96_holes(block_context: BlockContext, max_l1_resource_bounds: ValidRes [&BuiltinName::range_check96], 24 ); - assert_eq!(tx_execution_info.receipt.gas, GasVector::from_l1_gas(6782)); } #[rstest] @@ -170,7 +171,7 @@ fn test_fee_enforcement( deploy_account_tx_args! { class_hash: account.get_class_hash(), max_fee: Fee(u128::from(!zero_bounds)), - resource_bounds: l1_resource_bounds(u64::from(!zero_bounds), DEFAULT_STRK_L1_GAS_PRICE), + resource_bounds: l1_resource_bounds(u8::from(!zero_bounds).into(), DEFAULT_STRK_L1_GAS_PRICE), version, }, &mut NonceManager::default(), @@ -182,6 +183,57 @@ fn test_fee_enforcement( assert_eq!(result.is_err(), enforce_fee); } +#[rstest] +#[case::positive_case_deprecated_tx(true, true)] +#[case::positive_case_new_tx(true, false)] +#[should_panic(expected = "exceeded bounds; max fee is")] +#[case::negative_case_deprecated_tx(false, true)] +#[should_panic(expected = "exceeded bounds; max possible fee is")] +#[case::negative_case_new_tx(false, false)] +fn test_assert_actual_fee_in_bounds( + block_context: BlockContext, + #[case] positive_flow: bool, + #[case] deprecated_tx: bool, +) { + let actual_fee_offset = if positive_flow { 0 } else { 1 }; + if deprecated_tx { + let max_fee = 100; + let tx = account_invoke_tx(invoke_tx_args! { + max_fee: Fee(max_fee), + version: TransactionVersion::ONE, + }); + let context = Arc::new(block_context.to_tx_context(&tx)); + AccountTransaction::assert_actual_fee_in_bounds(&context, Fee(max_fee + actual_fee_offset)); + } else { + // All resources. + let l1_gas = ResourceBounds { max_amount: 2, max_price_per_unit: 3 }; + let l2_gas = ResourceBounds { max_amount: 4, max_price_per_unit: 5 }; + let l1_data_gas = ResourceBounds { max_amount: 6, max_price_per_unit: 7 }; + let all_resource_bounds = + ValidResourceBounds::AllResources(AllResourceBounds { l1_gas, l2_gas, l1_data_gas }); + let all_resource_fee = u128::from(l1_gas.max_amount) * l1_gas.max_price_per_unit + + u128::from(l2_gas.max_amount) * l2_gas.max_price_per_unit + + u128::from(l1_data_gas.max_amount) * l1_data_gas.max_price_per_unit + + actual_fee_offset; + + // L1 resources. + let l1_resource_bounds = ValidResourceBounds::L1Gas(l1_gas); + let l1_resource_fee = + u128::from(l1_gas.max_amount) * l1_gas.max_price_per_unit + actual_fee_offset; + + for (bounds, actual_fee) in + [(all_resource_bounds, all_resource_fee), (l1_resource_bounds, l1_resource_fee)] + { + let tx = account_invoke_tx(invoke_tx_args! { + resource_bounds: bounds, + version: TransactionVersion::THREE, + }); + let context = Arc::new(block_context.to_tx_context(&tx)); + AccountTransaction::assert_actual_fee_in_bounds(&context, Fee(actual_fee)); + } + } +} + #[rstest] #[case(TransactionVersion::ZERO)] #[case(TransactionVersion::ONE)] @@ -194,7 +246,7 @@ fn test_enforce_fee_false_works(block_context: BlockContext, #[case] version: Tr &block_context, invoke_tx_args! { max_fee: Fee(0), - resource_bounds: l1_resource_bounds(0, DEFAULT_STRK_L1_GAS_PRICE), + resource_bounds: l1_resource_bounds(GasAmount(0), DEFAULT_STRK_L1_GAS_PRICE), sender_address: account_address, calldata: create_trivial_calldata(contract_address), version, @@ -422,8 +474,7 @@ fn test_max_fee_limit_validate( &block_context, &account_tx, &GasVectorComputationMode::NoL2Gas, - ) - .unwrap(); + ); let estimated_min_l1_gas = estimated_min_gas_usage_vector.l1_gas; let estimated_min_fee = get_fee_by_gas_vector(block_info, estimated_min_gas_usage_vector, &account_tx.fee_type()); @@ -436,7 +487,7 @@ fn test_max_fee_limit_validate( // TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion // works. resource_bounds: l1_resource_bounds( - estimated_min_l1_gas.try_into().expect("Failed to convert u128 to u64."), + estimated_min_l1_gas, block_info.gas_prices.get_l1_gas_price_by_fee_type(&account_tx.fee_type()).into() ), ..tx_args @@ -615,7 +666,7 @@ fn test_fail_deploy_account( let error = deploy_account_tx.execute(state, &block_context, true, true).unwrap_err(); // Check the error is as expected. Assure the error message is not nonce or fee related. - check_transaction_execution_error_for_invalid_scenario!(cairo_version, error, false); + check_tx_execution_error_for_invalid_scenario!(cairo_version, error, false); // Assert nonce and balance are unchanged, and that no contract was deployed at the address. assert_eq!(state.get_nonce_at(deploy_address).unwrap(), nonce!(0_u8)); @@ -914,13 +965,13 @@ fn test_max_fee_to_max_steps_conversion( ) { let TestInitData { mut state, account_address, contract_address, mut nonce_manager } = create_test_init_data(&block_context.chain_info, CairoVersion::Cairo0); - let actual_gas_used: u64 = u64_from_usize( + let actual_gas_used: GasAmount = u128_from_usize( get_syscall_resources(SyscallSelector::CallContract).n_steps + get_tx_resources(TransactionType::InvokeFunction).n_steps + 1751, - ); - let actual_gas_used_as_u128: u128 = actual_gas_used.into(); - let actual_fee = actual_gas_used_as_u128 * 100000000000; + ) + .into(); + let actual_fee = actual_gas_used.0 * 100000000000; let actual_strk_gas_price = block_context.block_info.gas_prices.get_l1_gas_price_by_fee_type(&FeeType::Strk); let execute_calldata = create_calldata( @@ -943,15 +994,11 @@ fn test_max_fee_to_max_steps_conversion( let max_steps_limit1 = execution_context1.vm_run_resources.get_n_steps(); let tx_execution_info1 = account_tx1.execute(&mut state, &block_context, true, true).unwrap(); let n_steps1 = tx_execution_info1.receipt.resources.computation.vm_resources.n_steps; - let gas_used_vector1 = tx_execution_info1 - .receipt - .resources - .to_gas_vector( - &block_context.versioned_constants, - block_context.block_info.use_kzg_da, - &GasVectorComputationMode::NoL2Gas, - ) - .unwrap(); + let gas_used_vector1 = tx_execution_info1.receipt.resources.to_gas_vector( + &block_context.versioned_constants, + block_context.block_info.use_kzg_da, + &GasVectorComputationMode::NoL2Gas, + ); // Second invocation of `with_arg` gets twice the pre-calculated actual fee as max_fee. let account_tx2 = account_invoke_tx(invoke_tx_args! { @@ -959,7 +1006,8 @@ fn test_max_fee_to_max_steps_conversion( sender_address: account_address, calldata: execute_calldata, version, - resource_bounds: l1_resource_bounds(2 * actual_gas_used, actual_strk_gas_price.into()), + resource_bounds: + l1_resource_bounds((2 * actual_gas_used.0).into(), actual_strk_gas_price.into()), nonce: nonce_manager.next(account_address), }); let tx_context2 = Arc::new(block_context.to_tx_context(&account_tx2)); @@ -967,25 +1015,18 @@ fn test_max_fee_to_max_steps_conversion( let max_steps_limit2 = execution_context2.vm_run_resources.get_n_steps(); let tx_execution_info2 = account_tx2.execute(&mut state, &block_context, true, true).unwrap(); let n_steps2 = tx_execution_info2.receipt.resources.computation.vm_resources.n_steps; - let gas_used_vector2 = tx_execution_info2 - .receipt - .resources - .to_gas_vector( - &block_context.versioned_constants, - block_context.block_info.use_kzg_da, - &GasVectorComputationMode::NoL2Gas, - ) - .unwrap(); + let gas_used_vector2 = tx_execution_info2.receipt.resources.to_gas_vector( + &block_context.versioned_constants, + block_context.block_info.use_kzg_da, + &GasVectorComputationMode::NoL2Gas, + ); // Test that steps limit doubles as max_fee doubles, but actual consumed steps and fee remains. assert_eq!(max_steps_limit2.unwrap(), 2 * max_steps_limit1.unwrap()); assert_eq!(tx_execution_info1.receipt.fee.0, tx_execution_info2.receipt.fee.0); // TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion works. // TODO(Aner, 21/01/24): verify test compliant with 4844 (or modify accordingly). - assert_eq!( - actual_gas_used, - u64::try_from(gas_used_vector2.l1_gas).expect("Failed to convert u128 to u64.") - ); + assert_eq!(actual_gas_used, gas_used_vector2.l1_gas); assert_eq!(actual_fee, tx_execution_info2.receipt.fee.0); assert_eq!(n_steps1, n_steps2); assert_eq!(gas_used_vector1, gas_used_vector2); @@ -1022,7 +1063,7 @@ fn test_insufficient_max_fee_reverts( let gas_price = u128::from( block_context.block_info.gas_prices.get_l1_gas_price_by_fee_type(&FeeType::Strk), ); - let gas_ammount = u64::try_from(actual_fee_depth1.0 / gas_price).unwrap(); + let gas_ammount = GasAmount(actual_fee_depth1.0 / gas_price); // Invoke the `recurse` function with depth of 2 and the actual fee of depth 1 as max_fee. // This call should fail due to insufficient max fee (steps bound based on max_fee is not so @@ -1462,3 +1503,55 @@ fn test_revert_in_execute( assert!(tx_execution_info.is_reverted()); assert!(tx_execution_info.revert_error.unwrap().contains("Failed to deserialize param #1")); } + +#[rstest] +#[case(true)] +#[case(false)] +fn test_call_contract_that_panics( + mut block_context: BlockContext, + max_l1_resource_bounds: ValidResourceBounds, + #[case] enable_reverts: bool, +) { + // Override enable reverts. + block_context.versioned_constants.enable_reverts = enable_reverts; + let test_contract = FeatureContract::TestContract(CairoVersion::Cairo1); + let account = FeatureContract::AccountWithoutValidations(CairoVersion::Cairo1); + let chain_info = &block_context.chain_info; + let state = &mut test_state(chain_info, BALANCE, &[(test_contract, 1), (account, 1)]); + let test_contract_address = test_contract.get_instance_address(0); + let account_address = account.get_instance_address(0); + let mut nonce_manager = NonceManager::default(); + + let new_class_hash = test_contract.get_class_hash(); + + let calldata = [ + *FeatureContract::TestContract(CairoVersion::Cairo1).get_instance_address(0).0.key(), + selector_from_name("test_revert_helper").0, + felt!(1_u8), + new_class_hash.0, + ]; + + // Invoke a function that changes the state and reverts. + let tx_args = invoke_tx_args! { + sender_address: account_address, + calldata: create_calldata( + test_contract_address, + "test_call_contract_revert", + &calldata + ), + nonce: nonce_manager.next(account_address) + }; + let tx_execution_info = run_invoke_tx( + state, + &block_context, + invoke_tx_args! { + resource_bounds: max_l1_resource_bounds, + ..tx_args + }, + ) + .unwrap(); + + // If reverts are enabled, `test_call_contract_revert` should catch it and ignore it. + // Otherwise, the transaction should revert. + assert_eq!(tx_execution_info.is_reverted(), !enable_reverts); +} diff --git a/crates/blockifier/src/transaction/error_format_test.rs b/crates/blockifier/src/transaction/error_format_test.rs index 97ff8346c92..d51ad27d593 100644 --- a/crates/blockifier/src/transaction/error_format_test.rs +++ b/crates/blockifier/src/transaction/error_format_test.rs @@ -17,7 +17,7 @@ fn test_contract_class_version_mismatch() { } #[test] -fn test_declare_transaction_error_format() { +fn test_declare_tx_error_format() { let error = TransactionExecutionError::DeclareTransactionError { class_hash: ClassHash(StarkHash::THREE), }; diff --git a/crates/blockifier/src/transaction/errors.rs b/crates/blockifier/src/transaction/errors.rs index bdfe491646d..ae0c85cf540 100644 --- a/crates/blockifier/src/transaction/errors.rs +++ b/crates/blockifier/src/transaction/errors.rs @@ -1,6 +1,7 @@ use cairo_vm::types::errors::program_errors::ProgramError; use num_bigint::BigUint; use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector, Nonce}; +use starknet_api::execution_resources::GasAmount; use starknet_api::transaction::{Fee, Resource, TransactionVersion}; use starknet_api::StarknetApiError; use starknet_types_core::felt::FromStrError; @@ -9,7 +10,7 @@ use thiserror::Error; use crate::execution::call_info::Retdata; use crate::execution::errors::{ConstructorEntryPointExecutionError, EntryPointExecutionError}; use crate::execution::execution_utils::format_panic_data; -use crate::execution::stack_trace::gen_transaction_execution_error_trace; +use crate::execution::stack_trace::gen_tx_execution_error_trace; use crate::fee::fee_checks::FeeCheckError; use crate::state::errors::StateError; @@ -25,7 +26,22 @@ pub enum TransactionFeeError { #[error("Actual fee ({}) exceeded paid fee on L1 ({}).", actual_fee.0, paid_fee.0)] InsufficientFee { paid_fee: Fee, actual_fee: Fee }, #[error( - "Resource {resource} bounds (max amount: {max_amount}, max price: {max_price}) exceed \ + "Resources bounds (l1 gas max amount: {l1_max_amount}, l1 gas max price: {l1_max_price}, \ + l1 data max amount: {l1_data_max_amount}, l1 data max price: {l1_data_max_price}, l2 gas \ + max amount: {l2_max_amount}, l2 gas max price: {l2_max_price}) exceed balance \ + ({balance})." + )] + ResourcesBoundsExceedBalance { + l1_max_amount: u64, + l1_max_price: u128, + l1_data_max_amount: u64, + l1_data_max_price: u128, + l2_max_amount: u64, + l2_max_price: u128, + balance: BigUint, + }, + #[error( + "Resource {resource} bounds (max amount: {max_amount}, max price): {max_price}) exceed \ balance ({balance})." )] GasBoundsExceedBalance { @@ -47,7 +63,11 @@ pub enum TransactionFeeError { "Max {resource} amount ({max_gas_amount}) is lower than the minimal gas amount: \ {minimal_gas_amount}." )] - MaxGasAmountTooLow { resource: Resource, max_gas_amount: u64, minimal_gas_amount: u64 }, + MaxGasAmountTooLow { + resource: Resource, + max_gas_amount: GasAmount, + minimal_gas_amount: GasAmount, + }, #[error("Missing L1 gas bounds in resource bounds.")] MissingL1GasBounds, #[error(transparent)] @@ -63,14 +83,14 @@ pub enum TransactionExecutionError { ContractClassVersionMismatch { declare_version: TransactionVersion, cairo_version: u64 }, #[error( "Contract constructor execution has failed:\n{}", - String::from(gen_transaction_execution_error_trace(self)) + String::from(gen_tx_execution_error_trace(self)) )] ContractConstructorExecutionFailed(#[from] ConstructorEntryPointExecutionError), #[error("Class with hash {:#064x} is already declared.", **class_hash)] DeclareTransactionError { class_hash: ClassHash }, #[error( "Transaction execution has failed:\n{}", - String::from(gen_transaction_execution_error_trace(self)) + String::from(gen_tx_execution_error_trace(self)) )] ExecutionError { error: EntryPointExecutionError, @@ -105,7 +125,7 @@ pub enum TransactionExecutionError { TransactionTooLarge, #[error( "Transaction validation has failed:\n{}", - String::from(gen_transaction_execution_error_trace(self)) + String::from(gen_tx_execution_error_trace(self)) )] ValidateTransactionError { error: EntryPointExecutionError, diff --git a/crates/blockifier/src/transaction/execution_flavors_test.rs b/crates/blockifier/src/transaction/execution_flavors_test.rs index b924dbb293a..1e8d63eef94 100644 --- a/crates/blockifier/src/transaction/execution_flavors_test.rs +++ b/crates/blockifier/src/transaction/execution_flavors_test.rs @@ -2,11 +2,13 @@ use assert_matches::assert_matches; use pretty_assertions::assert_eq; use rstest::rstest; use starknet_api::core::ContractAddress; +use starknet_api::execution_resources::GasAmount; use starknet_api::test_utils::invoke::InvokeTxArgs; use starknet_api::test_utils::NonceManager; use starknet_api::transaction::{ Calldata, Fee, + GasVectorComputationMode, Resource, TransactionSignature, TransactionVersion, @@ -18,7 +20,7 @@ use starknet_types_core::felt::Felt; use crate::context::{BlockContext, ChainInfo}; use crate::execution::syscalls::SyscallSelector; use crate::fee::fee_utils::get_fee_by_gas_vector; -use crate::fee::resources::{GasVector, GasVectorComputationMode}; +use crate::fee::resources::GasVector; use crate::state::cached_state::CachedState; use crate::state::state_api::StateReader; use crate::test_utils::contracts::FeatureContract; @@ -29,7 +31,6 @@ use crate::test_utils::{ create_trivial_calldata, get_syscall_resources, get_tx_resources, - u64_from_usize, CairoVersion, BALANCE, MAX_FEE, @@ -50,7 +51,8 @@ use crate::transaction::test_utils::{ }; use crate::transaction::transaction_types::TransactionType; use crate::transaction::transactions::ExecutableTransaction; -const VALIDATE_GAS_OVERHEAD: u64 = 21; +use crate::utils::u128_from_usize; +const VALIDATE_GAS_OVERHEAD: GasAmount = GasAmount(21); struct FlavorTestInitialState { pub state: CachedState, @@ -104,33 +106,43 @@ fn check_balance( /// Returns the amount of L1 gas and derived fee, given base gas amount and a boolean indicating /// if validation is to be done. -fn gas_and_fee(base_gas: u64, validate_mode: bool, fee_type: &FeeType) -> (u64, Fee) { +fn gas_and_fee( + base_gas: GasAmount, + add_validation_overhead: bool, + fee_type: &FeeType, +) -> (GasAmount, Fee) { // Validation incurs a constant gas overhead. - let gas = base_gas + if validate_mode { VALIDATE_GAS_OVERHEAD } else { 0 }; + let gas = base_gas + if add_validation_overhead { VALIDATE_GAS_OVERHEAD } else { GasAmount(0) }; ( gas, get_fee_by_gas_vector( &BlockContext::create_for_account_testing().block_info, - GasVector::from_l1_gas(gas.into()), + GasVector::from_l1_gas(gas), fee_type, ), ) } +// Calculates the actual gas used by a transaction. Removing the validation overhead if requested, +// as it's already considered in the tx_execution_info. fn calculate_actual_gas( tx_execution_info: &TransactionExecutionInfo, block_context: &BlockContext, -) -> u128 { - tx_execution_info - .receipt - .resources - .to_gas_vector( - &block_context.versioned_constants, - block_context.block_info.use_kzg_da, - &GasVectorComputationMode::NoL2Gas, - ) - .unwrap() - .l1_gas + remove_validation_overhead: bool, +) -> GasAmount { + GasAmount( + tx_execution_info + .receipt + .resources + .to_gas_vector( + &block_context.versioned_constants, + block_context.block_info.use_kzg_da, + &GasVectorComputationMode::NoL2Gas, + ) + .l1_gas + .0 + - if remove_validation_overhead { VALIDATE_GAS_OVERHEAD.0 } else { 0 }, + ) } /// Asserts gas used and reported fee are as expected. @@ -139,17 +151,17 @@ fn check_gas_and_fee( block_context: &BlockContext, tx_execution_info: &TransactionExecutionInfo, fee_type: &FeeType, - expected_actual_gas: u64, + expected_actual_gas: GasAmount, expected_actual_fee: Fee, expected_cost_of_resources: Fee, ) { - assert_eq!(calculate_actual_gas(tx_execution_info, block_context), expected_actual_gas.into()); + assert_eq!(calculate_actual_gas(tx_execution_info, block_context, false), expected_actual_gas); assert_eq!(tx_execution_info.receipt.fee, expected_actual_fee); // Future compatibility: resources other than the L1 gas usage may affect the fee (currently, // `calculate_tx_fee` is simply the result of `calculate_tx_gas_usage_vector` times gas price). assert_eq!( - tx_execution_info.receipt.resources.calculate_tx_fee(block_context, fee_type).unwrap(), + tx_execution_info.receipt.resources.calculate_tx_fee(block_context, fee_type), expected_cost_of_resources ); } @@ -172,7 +184,7 @@ fn get_pre_validate_test_args( let max_fee = Fee(MAX_FEE); // The max resource bounds fixture is not used here because this function already has the // maximum number of arguments. - let resource_bounds = l1_resource_bounds(MAX_L1_GAS_AMOUNT, MAX_L1_GAS_PRICE); + let resource_bounds = l1_resource_bounds(MAX_L1_GAS_AMOUNT.into(), MAX_L1_GAS_PRICE); let FlavorTestInitialState { state, account_address, test_contract_address, nonce_manager, .. } = create_flavors_test_state(&block_context.chain_info, cairo_version); @@ -246,7 +258,7 @@ fn test_simulate_validate_pre_validate_with_charge_fee( // First scenario: minimal fee not covered. Actual fee is precomputed. let err = account_invoke_tx(invoke_tx_args! { max_fee: Fee(10), - resource_bounds: l1_resource_bounds(10, 10), + resource_bounds: l1_resource_bounds(GasAmount(10), 10), nonce: nonce_manager.next(account_address), ..pre_validation_base_args.clone() }) @@ -280,7 +292,7 @@ fn test_simulate_validate_pre_validate_with_charge_fee( (BALANCE / gas_price).try_into().expect("Failed to convert u128 to u64."); let result = account_invoke_tx(invoke_tx_args! { max_fee: Fee(BALANCE + 1), - resource_bounds: l1_resource_bounds(balance_over_gas_price + 10, gas_price.into()), + resource_bounds: l1_resource_bounds(u128::from(balance_over_gas_price + 10).into(), gas_price.into()), nonce: nonce_manager.next(account_address), ..pre_validation_base_args.clone() }) @@ -311,7 +323,7 @@ fn test_simulate_validate_pre_validate_with_charge_fee( // Third scenario: L1 gas price bound lower than the price on the block. if !is_deprecated { let err = account_invoke_tx(invoke_tx_args! { - resource_bounds: l1_resource_bounds(MAX_L1_GAS_AMOUNT, u128::from(gas_price) - 1), + resource_bounds: l1_resource_bounds(MAX_L1_GAS_AMOUNT.into(), u128::from(gas_price) - 1), nonce: nonce_manager.next(account_address), ..pre_validation_base_args }) @@ -355,13 +367,14 @@ fn test_simulate_validate_pre_validate_not_charge_fee( }) .execute(&mut state, &block_context, charge_fee, false) .unwrap(); - let base_gas = calculate_actual_gas(&tx_execution_info, &block_context).try_into().unwrap(); + let base_gas = calculate_actual_gas(&tx_execution_info, &block_context, false); assert!( base_gas - > u64_from_usize( + > u128_from_usize( get_syscall_resources(SyscallSelector::CallContract).n_steps + get_tx_resources(TransactionType::InvokeFunction).n_steps ) + .into() ); let (actual_gas_used, actual_fee) = gas_and_fee(base_gas, validate, &fee_type); @@ -387,7 +400,7 @@ fn test_simulate_validate_pre_validate_not_charge_fee( } // First scenario: minimal fee not covered. Actual fee is precomputed. - execute_and_check_gas_and_fee!(Fee(10), l1_resource_bounds(10, 10)); + execute_and_check_gas_and_fee!(Fee(10), l1_resource_bounds(GasAmount(10), 10)); // Second scenario: resource bounds greater than balance. let gas_price = block_context.block_info.gas_prices.get_l1_gas_price_by_fee_type(&fee_type); @@ -395,14 +408,14 @@ fn test_simulate_validate_pre_validate_not_charge_fee( (BALANCE / gas_price).try_into().expect("Failed to convert u128 to u64."); execute_and_check_gas_and_fee!( Fee(BALANCE + 1), - l1_resource_bounds(balance_over_gas_price + 10, gas_price.into()) + l1_resource_bounds(u128::from(balance_over_gas_price + 10).into(), gas_price.into()) ); // Third scenario: L1 gas price bound lower than the price on the block. if !is_deprecated { execute_and_check_gas_and_fee!( pre_validation_base_args.max_fee, - l1_resource_bounds(MAX_L1_GAS_AMOUNT, u128::from(gas_price) - 1) + l1_resource_bounds(MAX_L1_GAS_AMOUNT.into(), u128::from(gas_price) - 1) ); } } @@ -485,7 +498,7 @@ fn test_simulate_charge_fee_no_validation_fail_validate( max_l1_resource_bounds: ValidResourceBounds, ) { let validate = false; - let transaction_execution_info = execute_fail_validation( + let tx_execution_info = execute_fail_validation( only_query, validate, charge_fee, @@ -497,15 +510,17 @@ fn test_simulate_charge_fee_no_validation_fail_validate( // Validation scenario: fallible validation. let block_context = BlockContext::create_for_account_testing(); - let base_gas = - calculate_actual_gas(&transaction_execution_info, &block_context).try_into().unwrap(); - assert!(base_gas > u64_from_usize(get_tx_resources(TransactionType::InvokeFunction).n_steps)); + let base_gas = calculate_actual_gas(&tx_execution_info, &block_context, validate); + assert!( + base_gas + > u128_from_usize(get_tx_resources(TransactionType::InvokeFunction).n_steps).into() + ); let (actual_gas_used, actual_fee) = gas_and_fee(base_gas, validate, &fee_type); // The reported fee should be the actual cost, regardless of whether or not fee is charged. check_gas_and_fee( &block_context, - &transaction_execution_info, + &tx_execution_info, &fee_type, actual_gas_used, actual_fee, @@ -556,11 +571,6 @@ fn test_simulate_validate_charge_fee_mid_execution( }; // First scenario: logic error. Should result in revert; actual fee should be shown. - let (revert_gas_used, revert_fee) = gas_and_fee( - u64_from_usize(get_tx_resources(TransactionType::InvokeFunction).n_steps + 1719), - validate, - &fee_type, - ); let tx_execution_info = account_invoke_tx(invoke_tx_args! { calldata: recurse_calldata(test_contract_address, true, 3), nonce: nonce_manager.next(account_address), @@ -568,6 +578,12 @@ fn test_simulate_validate_charge_fee_mid_execution( }) .execute(&mut state, &block_context, charge_fee, validate) .unwrap(); + let base_gas = calculate_actual_gas(&tx_execution_info, &block_context, validate); + let (revert_gas_used, revert_fee) = gas_and_fee(base_gas, validate, &fee_type); + assert!( + base_gas + > u128_from_usize(get_tx_resources(TransactionType::InvokeFunction).n_steps).into() + ); assert!(tx_execution_info.is_reverted()); check_gas_and_fee( &block_context, @@ -588,16 +604,17 @@ fn test_simulate_validate_charge_fee_mid_execution( // Second scenario: limit resources via sender bounds. Should revert if and only if step limit // is derived from sender bounds (`charge_fee` mode). - let (gas_bound, fee_bound) = gas_and_fee(6107, validate, &fee_type); + let (gas_bound, fee_bound) = gas_and_fee(GasAmount(6111), validate, &fee_type); // If `charge_fee` is true, execution is limited by sender bounds, so less resources will be // used. Otherwise, execution is limited by block bounds, so more resources will be used. - let (limited_gas_used, limited_fee) = gas_and_fee(7759, validate, &fee_type); + let (limited_gas_used, limited_fee) = gas_and_fee(GasAmount(7763), validate, &fee_type); let (unlimited_gas_used, unlimited_fee) = gas_and_fee( - u64_from_usize( + u128_from_usize( get_syscall_resources(SyscallSelector::CallContract).n_steps + get_tx_resources(TransactionType::InvokeFunction).n_steps + 5730, - ), + ) + .into(), validate, &fee_type, ); @@ -635,16 +652,16 @@ fn test_simulate_validate_charge_fee_mid_execution( // whether or not `charge_fee` is true. let mut low_step_block_context = block_context.clone(); low_step_block_context.versioned_constants.invoke_tx_max_n_steps = 10000; - let (huge_gas_limit, huge_fee) = gas_and_fee(100000, validate, &fee_type); + let (huge_gas_limit, huge_fee) = gas_and_fee(GasAmount(100000), validate, &fee_type); // Gas usage does not depend on `validate` flag in this scenario, because we reach the block // step limit during execution anyway. The actual limit when execution phase starts is slightly // lower when `validate` is true, but this is not reflected in the actual gas usage. let invoke_tx_max_n_steps_as_u64: u64 = low_step_block_context.versioned_constants.invoke_tx_max_n_steps.into(); - let block_limit_gas = invoke_tx_max_n_steps_as_u64 + 1652; + let block_limit_gas = u128::from(invoke_tx_max_n_steps_as_u64 + 1652).into(); let block_limit_fee = get_fee_by_gas_vector( &block_context.block_info, - GasVector::from_l1_gas(block_limit_gas.into()), + GasVector::from_l1_gas(block_limit_gas), &fee_type, ); let tx_execution_info = account_invoke_tx(invoke_tx_args! { @@ -709,22 +726,23 @@ fn test_simulate_validate_charge_fee_post_execution( // If `charge_fee` is false - we do not revert, and simply report the fee and resources as used. // If `charge_fee` is true, we revert, charge the maximal allowed fee (derived from sender // bounds), and report resources base on execution steps reverted + other overhead. - let base_gas_bound = 8000; + let base_gas_bound = GasAmount(8010); let (just_not_enough_gas_bound, just_not_enough_fee_bound) = gas_and_fee(base_gas_bound, validate, &fee_type); // `__validate__` and overhead resources + number of reverted steps, comes out slightly more // than the gas bound. let (revert_gas_usage, revert_fee) = gas_and_fee( - u64_from_usize(get_tx_resources(TransactionType::InvokeFunction).n_steps) + 5730, + (u128_from_usize(get_tx_resources(TransactionType::InvokeFunction).n_steps) + 5730).into(), validate, &fee_type, ); let (unlimited_gas_used, unlimited_fee) = gas_and_fee( - u64_from_usize( + u128_from_usize( get_syscall_resources(SyscallSelector::CallContract).n_steps + get_tx_resources(TransactionType::InvokeFunction).n_steps + 5730, - ), + ) + .into(), validate, &fee_type, ); @@ -764,16 +782,17 @@ fn test_simulate_validate_charge_fee_post_execution( // Second scenario: balance too low. // Execute a transfer, and make sure we get the expected result. let (success_actual_gas, actual_fee) = gas_and_fee( - u64_from_usize( + u128_from_usize( get_syscall_resources(SyscallSelector::CallContract).n_steps + get_tx_resources(TransactionType::InvokeFunction).n_steps + 4260, - ), + ) + .into(), validate, &fee_type, ); let (fail_actual_gas, fail_actual_fee) = gas_and_fee( - u64_from_usize(get_tx_resources(TransactionType::InvokeFunction).n_steps + 2252), + u128_from_usize(get_tx_resources(TransactionType::InvokeFunction).n_steps + 2252).into(), validate, &fee_type, ); diff --git a/crates/blockifier/src/transaction/objects_test.rs b/crates/blockifier/src/transaction/objects_test.rs index 6f9abad81da..a5bd07b8f93 100644 --- a/crates/blockifier/src/transaction/objects_test.rs +++ b/crates/blockifier/src/transaction/objects_test.rs @@ -114,14 +114,14 @@ fn call_info_with_deep_inner_calls( #[case(0, 2)] #[case(1, 3)] #[case(2, 0)] -fn test_events_counter_in_transaction_execution_info( +fn test_events_counter_in_tx_execution_info( #[case] n_execute_events: usize, #[case] n_inner_calls: usize, ) { let n_validate_events = 2; let n_fee_transfer_events = 1; - let transaction_execution_info = TransactionExecutionInfo { + let tx_execution_info = TransactionExecutionInfo { validate_call_info: Some(call_info_with_x_events(n_validate_events, 0)), execute_call_info: Some(call_info_with_x_events(n_execute_events, n_inner_calls)), fee_transfer_call_info: Some(call_info_with_x_events(n_fee_transfer_events, 0)), @@ -129,7 +129,7 @@ fn test_events_counter_in_transaction_execution_info( }; assert_eq!( - transaction_execution_info.summarize().event_summary.n_events, + tx_execution_info.summarize().event_summary.n_events, n_validate_events + n_execute_events + n_fee_transfer_events + n_inner_calls ); } @@ -138,16 +138,14 @@ fn test_events_counter_in_transaction_execution_info( #[case(0)] #[case(1)] #[case(20)] -fn test_events_counter_in_transaction_execution_info_with_inner_call_info( - #[case] n_execute_events: usize, -) { +fn test_events_counter_in_tx_execution_info_with_inner_call_info(#[case] n_execute_events: usize) { let n_fee_transfer_events = 2; let n_inner_calls = 3; let n_execution_events = 1; let n_events_for_each_inner_call = 2; let n_inner_calls_of_each_inner_call = 1; - let transaction_execution_info = TransactionExecutionInfo { + let tx_execution_info = TransactionExecutionInfo { validate_call_info: Some(call_info_with_deep_inner_calls( n_execution_events, n_inner_calls, @@ -160,7 +158,7 @@ fn test_events_counter_in_transaction_execution_info_with_inner_call_info( }; assert_eq!( - transaction_execution_info.summarize().event_summary.n_events, + tx_execution_info.summarize().event_summary.n_events, n_execute_events + n_fee_transfer_events + n_execution_events @@ -184,7 +182,7 @@ fn test_summarize( let execute_call_info = execute_params.to_call_info(); let fee_transfer_call_info = fee_transfer_params.to_call_info(); - let transaction_execution_info = TransactionExecutionInfo { + let tx_execution_info = TransactionExecutionInfo { validate_call_info: Some(validate_call_info), execute_call_info: Some(execute_call_info), fee_transfer_call_info: Some(fee_transfer_call_info), @@ -222,7 +220,7 @@ fn test_summarize( }; // Call the summarize method - let actual_summary = transaction_execution_info.summarize(); + let actual_summary = tx_execution_info.summarize(); // Compare the actual result with the expected result assert_eq!(actual_summary.executed_class_hashes, expected_summary.executed_class_hashes); diff --git a/crates/blockifier/src/transaction/post_execution_test.rs b/crates/blockifier/src/transaction/post_execution_test.rs index 4c53f79f7f1..63489007828 100644 --- a/crates/blockifier/src/transaction/post_execution_test.rs +++ b/crates/blockifier/src/transaction/post_execution_test.rs @@ -1,14 +1,21 @@ use assert_matches::assert_matches; use rstest::rstest; use starknet_api::core::{ContractAddress, PatriciaKey}; +use starknet_api::execution_resources::GasAmount; use starknet_api::state::StorageKey; -use starknet_api::transaction::{Calldata, Fee, Resource, TransactionVersion, ValidResourceBounds}; +use starknet_api::transaction::{ + Calldata, + Fee, + GasVectorComputationMode, + Resource, + TransactionVersion, + ValidResourceBounds, +}; use starknet_api::{felt, invoke_tx_args, patricia_key}; use starknet_types_core::felt::Felt; use crate::context::{BlockContext, ChainInfo}; use crate::fee::fee_checks::FeeCheckError; -use crate::fee::resources::GasVectorComputationMode; use crate::state::state_api::StateReader; use crate::test_utils::contracts::FeatureContract; use crate::test_utils::initial_test_state::test_state; @@ -253,7 +260,7 @@ fn test_revert_on_resource_overuse( assert_eq!(execution_info_measure.revert_error, None); let actual_fee = execution_info_measure.receipt.fee; // TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion works. - let actual_gas_usage: u64 = execution_info_measure + let actual_gas_usage = execution_info_measure .receipt .resources .to_gas_vector( @@ -261,10 +268,7 @@ fn test_revert_on_resource_overuse( block_context.block_info.use_kzg_da, &GasVectorComputationMode::NoL2Gas, ) - .unwrap() - .l1_gas - .try_into() - .expect("Failed to convert u128 to u64."); + .l1_gas; // Run the same function, with a different written value (to keep cost high), with the actual // resources used as upper bounds. Make sure execution does not revert. @@ -292,7 +296,7 @@ fn test_revert_on_resource_overuse( &block_context, invoke_tx_args! { max_fee: low_max_fee, - resource_bounds: l1_resource_bounds(actual_gas_usage - 1, MAX_L1_GAS_PRICE), + resource_bounds: l1_resource_bounds(GasAmount(actual_gas_usage.0 - 1), MAX_L1_GAS_PRICE), nonce: nonce_manager.next(account_address), calldata: write_a_lot_calldata(), ..base_args diff --git a/crates/blockifier/src/transaction/test_utils.rs b/crates/blockifier/src/transaction/test_utils.rs index bf40dcf832b..b467c0c6e83 100644 --- a/crates/blockifier/src/transaction/test_utils.rs +++ b/crates/blockifier/src/transaction/test_utils.rs @@ -1,5 +1,6 @@ use rstest::fixture; use starknet_api::core::{ClassHash, ContractAddress, Nonce}; +use starknet_api::execution_resources::GasAmount; use starknet_api::test_utils::deploy_account::DeployAccountTxArgs; use starknet_api::test_utils::invoke::InvokeTxArgs; use starknet_api::test_utils::NonceManager; @@ -8,6 +9,7 @@ use starknet_api::transaction::{ Calldata, ContractAddressSalt, Fee, + GasVectorComputationMode, InvokeTransactionV0, InvokeTransactionV1, InvokeTransactionV3, @@ -87,9 +89,26 @@ pub fn max_fee() -> Fee { Fee(MAX_FEE) } +// TODO(Amos, 1/10/2024): Delete this fixture and use `create_resource_bounds` #[fixture] pub fn max_l1_resource_bounds() -> ValidResourceBounds { - l1_resource_bounds(MAX_L1_GAS_AMOUNT, MAX_L1_GAS_PRICE) + create_resource_bounds(&GasVectorComputationMode::NoL2Gas) +} + +pub fn create_resource_bounds(computation_mode: &GasVectorComputationMode) -> ValidResourceBounds { + match computation_mode { + GasVectorComputationMode::NoL2Gas => { + l1_resource_bounds(MAX_L1_GAS_AMOUNT.into(), MAX_L1_GAS_PRICE) + } + GasVectorComputationMode::All => create_all_resource_bounds( + MAX_L1_GAS_AMOUNT, + MAX_L1_GAS_PRICE, + DEFAULT_L2_GAS_MAX_AMOUNT, + DEFAULT_STRK_L2_GAS_PRICE, + DEFAULT_L1_DATA_GAS_MAX_AMOUNT, + DEFAULT_STRK_L1_DATA_GAS_PRICE, + ), + } } #[fixture] @@ -303,8 +322,11 @@ pub fn run_invoke_tx( /// Creates a `ResourceBoundsMapping` with the given `max_amount` and `max_price` for L1 gas limits. /// No guarantees on the values of the other resources bounds. -pub fn l1_resource_bounds(max_amount: u64, max_price: u128) -> ValidResourceBounds { - ValidResourceBounds::L1Gas(ResourceBounds { max_amount, max_price_per_unit: max_price }) +pub fn l1_resource_bounds(max_amount: GasAmount, max_price: u128) -> ValidResourceBounds { + ValidResourceBounds::L1Gas(ResourceBounds { + max_amount: max_amount.0.try_into().unwrap(), + max_price_per_unit: max_price, + }) } #[fixture] @@ -315,6 +337,24 @@ pub fn all_resource_bounds( #[default(DEFAULT_STRK_L2_GAS_PRICE)] l2_max_price: u128, #[default(DEFAULT_L1_DATA_GAS_MAX_AMOUNT)] l1_data_max_amount: u64, #[default(DEFAULT_STRK_L1_DATA_GAS_PRICE)] l1_data_max_price: u128, +) -> ValidResourceBounds { + create_all_resource_bounds( + l1_max_amount, + l1_max_price, + l2_max_amount, + l2_max_price, + l1_data_max_amount, + l1_data_max_price, + ) +} + +fn create_all_resource_bounds( + l1_max_amount: u64, + l1_max_price: u128, + l2_max_amount: u64, + l2_max_price: u128, + l1_data_max_amount: u64, + l1_data_max_price: u128, ) -> ValidResourceBounds { ValidResourceBounds::AllResources(AllResourceBounds { l1_gas: ResourceBounds { max_amount: l1_max_amount, max_price_per_unit: l1_max_price }, diff --git a/crates/blockifier/src/transaction/transaction_execution.rs b/crates/blockifier/src/transaction/transaction_execution.rs index 08b08e98d6f..c564896ea7a 100644 --- a/crates/blockifier/src/transaction/transaction_execution.rs +++ b/crates/blockifier/src/transaction/transaction_execution.rs @@ -33,29 +33,29 @@ use crate::transaction::transactions::{ // TODO: Move into transaction.rs, makes more sense to be defined there. #[derive(Clone, Debug, derive_more::From)] pub enum Transaction { - AccountTransaction(AccountTransaction), - L1HandlerTransaction(L1HandlerTransaction), + Account(AccountTransaction), + L1Handler(L1HandlerTransaction), } impl Transaction { pub fn nonce(&self) -> Nonce { match self { - Self::AccountTransaction(tx) => tx.nonce(), - Self::L1HandlerTransaction(tx) => tx.tx.nonce, + Self::Account(tx) => tx.nonce(), + Self::L1Handler(tx) => tx.tx.nonce, } } pub fn sender_address(&self) -> ContractAddress { match self { - Self::AccountTransaction(tx) => tx.sender_address(), - Self::L1HandlerTransaction(tx) => tx.tx.contract_address, + Self::Account(tx) => tx.sender_address(), + Self::L1Handler(tx) => tx.tx.contract_address, } } pub fn tx_hash(tx: &Transaction) -> TransactionHash { match tx { - Transaction::AccountTransaction(tx) => tx.tx_hash(), - Transaction::L1HandlerTransaction(tx) => tx.tx_hash, + Transaction::Account(tx) => tx.tx_hash(), + Transaction::L1Handler(tx) => tx.tx_hash, } } @@ -69,7 +69,7 @@ impl Transaction { ) -> TransactionExecutionResult { match tx { StarknetApiTransaction::L1Handler(l1_handler) => { - Ok(Self::L1HandlerTransaction(L1HandlerTransaction { + Ok(Self::L1Handler(L1HandlerTransaction { tx: l1_handler, tx_hash, paid_fee_on_l1: paid_fee_on_l1 @@ -85,7 +85,7 @@ impl Transaction { } false => DeclareTransaction::new(declare, tx_hash, non_optional_class_info), }; - Ok(Self::AccountTransaction(AccountTransaction::Declare(declare_tx?))) + Ok(declare_tx?.into()) } StarknetApiTransaction::DeployAccount(deploy_account) => { let contract_address = match deployed_contract_address { @@ -107,14 +107,14 @@ impl Transaction { DeployAccountTransaction::new(deploy_account, tx_hash, contract_address) } }; - Ok(Self::AccountTransaction(AccountTransaction::DeployAccount(deploy_account_tx))) + Ok(deploy_account_tx.into()) } StarknetApiTransaction::Invoke(invoke) => { let invoke_tx = match only_query { true => InvokeTransaction::new_for_query(invoke, tx_hash), false => InvokeTransaction::new(invoke, tx_hash), }; - Ok(Self::AccountTransaction(AccountTransaction::Invoke(invoke_tx))) + Ok(invoke_tx.into()) } _ => unimplemented!(), } @@ -124,8 +124,8 @@ impl Transaction { impl TransactionInfoCreator for Transaction { fn create_tx_info(&self) -> TransactionInfo { match self { - Self::AccountTransaction(account_tx) => account_tx.create_tx_info(), - Self::L1HandlerTransaction(l1_handler_tx) => l1_handler_tx.create_tx_info(), + Self::Account(account_tx) => account_tx.create_tx_info(), + Self::L1Handler(l1_handler_tx) => l1_handler_tx.create_tx_info(), } } } @@ -157,7 +157,7 @@ impl ExecutableTransaction for L1HandlerTransaction { CallInfo::summarize_many(execute_call_info.iter()), &state.get_actual_state_changes()?, &execution_resources, - )?; + ); let paid_fee = self.paid_fee_on_l1; // For now, assert only that any amount of fee was paid. @@ -193,12 +193,10 @@ impl ExecutableTransaction for Transaction { // AccountTransaction::execute_raw. let concurrency_mode = execution_flags.concurrency_mode; let tx_execution_info = match self { - Self::AccountTransaction(account_tx) => { + Self::Account(account_tx) => { account_tx.execute_raw(state, block_context, execution_flags)? } - Self::L1HandlerTransaction(tx) => { - tx.execute_raw(state, block_context, execution_flags)? - } + Self::L1Handler(tx) => tx.execute_raw(state, block_context, execution_flags)?, }; // Check if the transaction is too large to fit any block. @@ -221,3 +219,21 @@ impl ExecutableTransaction for Transaction { Ok(tx_execution_info) } } + +impl From for Transaction { + fn from(value: DeclareTransaction) -> Self { + Self::Account(AccountTransaction::Declare(value)) + } +} + +impl From for Transaction { + fn from(value: DeployAccountTransaction) -> Self { + Self::Account(AccountTransaction::DeployAccount(value)) + } +} + +impl From for Transaction { + fn from(value: InvokeTransaction) -> Self { + Self::Account(AccountTransaction::Invoke(value)) + } +} diff --git a/crates/blockifier/src/transaction/transactions.rs b/crates/blockifier/src/transaction/transactions.rs index 696cbede624..6c451ae44a3 100644 --- a/crates/blockifier/src/transaction/transactions.rs +++ b/crates/blockifier/src/transaction/transactions.rs @@ -534,14 +534,15 @@ impl Executable for InvokeTransaction { initial_gas: *remaining_gas, }; - let call_info = execute_call.execute(state, resources, context).map_err(|error| { - TransactionExecutionError::ExecutionError { - error, - class_hash, - storage_address, - selector: entry_point_selector, - } - })?; + let call_info = + execute_call.non_reverting_execute(state, resources, context).map_err(|error| { + TransactionExecutionError::ExecutionError { + error, + class_hash, + storage_address, + selector: entry_point_selector, + } + })?; update_remaining_gas(remaining_gas, &call_info); Ok(Some(call_info)) @@ -635,7 +636,7 @@ impl Executable for L1HandlerTransaction { initial_gas: *remaining_gas, }; - execute_call.execute(state, resources, context).map(Some).map_err(|error| { + execute_call.non_reverting_execute(state, resources, context).map(Some).map_err(|error| { TransactionExecutionError::ExecutionError { error, class_hash, diff --git a/crates/blockifier/src/transaction/transactions_test.rs b/crates/blockifier/src/transaction/transactions_test.rs index 964d9cd179e..6e0c5d139b8 100644 --- a/crates/blockifier/src/transaction/transactions_test.rs +++ b/crates/blockifier/src/transaction/transactions_test.rs @@ -10,6 +10,7 @@ use pretty_assertions::assert_eq; use rstest::{fixture, rstest}; use starknet_api::core::{ChainId, ClassHash, ContractAddress, EthAddress, Nonce, PatriciaKey}; use starknet_api::deprecated_contract_class::EntryPointType; +use starknet_api::execution_resources::GasAmount; use starknet_api::state::StorageKey; use starknet_api::test_utils::invoke::InvokeTxArgs; use starknet_api::test_utils::NonceManager; @@ -21,6 +22,7 @@ use starknet_api::transaction::{ EventData, EventKey, Fee, + GasVectorComputationMode, L2ToL1Payload, ResourceBounds, TransactionSignature, @@ -60,6 +62,7 @@ use crate::execution::call_info::{ OrderedL2ToL1Message, Retdata, }; +use crate::execution::contract_class::TrackedResource; use crate::execution::entry_point::{CallEntryPoint, CallType}; use crate::execution::errors::{ConstructorEntryPointExecutionError, EntryPointExecutionError}; use crate::execution::syscalls::hint_processor::EmitEventError; @@ -74,8 +77,8 @@ use crate::fee::receipt::TransactionReceipt; use crate::fee::resources::{ ComputationResources, GasVector, - GasVectorComputationMode, StarknetResources, + StateResources, TransactionResources, }; use crate::state::cached_state::{CachedState, StateChangesCount, TransactionalState}; @@ -141,8 +144,8 @@ use crate::transaction::transaction_types::TransactionType; use crate::transaction::transactions::{ExecutableTransaction, L1HandlerTransaction}; use crate::versioned_constants::VersionedConstants; use crate::{ - check_transaction_execution_error_for_custom_hint, - check_transaction_execution_error_for_invalid_scenario, + check_tx_execution_error_for_custom_hint, + check_tx_execution_error_for_invalid_scenario, retdata, }; @@ -173,6 +176,7 @@ fn expected_validate_call_info( calldata: Calldata, storage_address: ContractAddress, cairo_version: CairoVersion, + tracked_resource: TrackedResource, ) -> Option { let retdata = match cairo_version { CairoVersion::Cairo0 => Retdata::default(), @@ -218,6 +222,7 @@ fn expected_validate_call_info( // The account contract we use for testing has trivial `validate` functions. resources, execution: CallExecution { retdata, gas_consumed, ..Default::default() }, + tracked_resource, ..Default::default() }) } @@ -302,9 +307,8 @@ fn get_expected_cairo_resources( starknet_resources: &StarknetResources, call_infos: Vec<&Option>, ) -> ExecutionResources { - let mut expected_cairo_resources = versioned_constants - .get_additional_os_tx_resources(tx_type, starknet_resources, false) - .unwrap(); + let mut expected_cairo_resources = + versioned_constants.get_additional_os_tx_resources(tx_type, starknet_resources, false); for call_info in call_infos { if let Some(call_info) = &call_info { expected_cairo_resources += &call_info.resources @@ -426,15 +430,16 @@ fn test_invoke_tx( let calldata = Calldata(Arc::clone(&invoke_tx.calldata().0)); let calldata_length = invoke_tx.calldata().0.len(); let signature_length = invoke_tx.signature().0.len(); + let state_changes_for_fee = StateChangesCount { + n_storage_updates: 1, + n_modified_contracts: 1, + ..StateChangesCount::default() + }; let starknet_resources = StarknetResources::new( calldata_length, signature_length, 0, - StateChangesCount { - n_storage_updates: 1, - n_modified_contracts: 1, - ..StateChangesCount::default() - }, + StateResources::new_for_testing(state_changes_for_fee), None, ExecutionSummary::default(), ); @@ -445,6 +450,10 @@ fn test_invoke_tx( let actual_execution_info = account_tx.execute(state, block_context, true, true).unwrap(); + let tracked_resource = account_contract + .get_class() + .tracked_resource(&versioned_constants.min_compiler_version_for_sierra_gas); + // Build expected validate call info. let expected_account_class_hash = account_contract.get_class_hash(); let expected_validate_call_info = expected_validate_call_info( @@ -454,6 +463,7 @@ fn test_invoke_tx( calldata, sender_address, account_cairo_version, + tracked_resource, ); // Build expected execute call info. @@ -489,13 +499,14 @@ fn test_invoke_tx( resources: ExecutionResources { n_steps: 23, n_memory_holes: 0, ..Default::default() }, ..Default::default() }], + tracked_resource, ..Default::default() }); // Build expected fee transfer call info. let fee_type = &tx_context.tx_info.fee_type(); let expected_actual_fee = - actual_execution_info.receipt.resources.calculate_tx_fee(block_context, fee_type).unwrap(); + actual_execution_info.receipt.resources.calculate_tx_fee(block_context, fee_type); let expected_fee_transfer_call_info = expected_fee_transfer_call_info( &tx_context, sender_address, @@ -503,7 +514,7 @@ fn test_invoke_tx( FeatureContract::ERC20(CairoVersion::Cairo0).get_class_hash(), ); - let da_gas = starknet_resources.get_state_changes_cost(use_kzg_da); + let da_gas = starknet_resources.state.to_gas_vector(use_kzg_da); let expected_cairo_resources = get_expected_cairo_resources( versioned_constants, @@ -511,7 +522,6 @@ fn test_invoke_tx( &starknet_resources, vec![&expected_validate_call_info, &expected_execute_call_info], ); - let state_changes_count = starknet_resources.state_changes_for_fee; let mut expected_actual_resources = TransactionResources { starknet_resources, computation: ComputationResources { @@ -522,18 +532,16 @@ fn test_invoke_tx( add_kzg_da_resources_to_resources_mapping( &mut expected_actual_resources.computation.vm_resources, - &state_changes_count, + &state_changes_for_fee, versioned_constants, use_kzg_da, ); - let total_gas = expected_actual_resources - .to_gas_vector( - &block_context.versioned_constants, - block_context.block_info.use_kzg_da, - &GasVectorComputationMode::NoL2Gas, - ) - .unwrap(); + let total_gas = expected_actual_resources.to_gas_vector( + &block_context.versioned_constants, + block_context.block_info.use_kzg_da, + &GasVectorComputationMode::NoL2Gas, + ); let expected_execution_info = TransactionExecutionInfo { validate_call_info: expected_validate_call_info, @@ -862,15 +870,18 @@ fn test_estimate_minimal_gas_vector( // The minimal gas estimate does not depend on tx version. let tx = &account_invoke_tx(valid_invoke_tx_args); let minimal_gas_vector = - estimate_minimal_gas_vector(block_context, tx, &gas_vector_computation_mode).unwrap(); + estimate_minimal_gas_vector(block_context, tx, &gas_vector_computation_mode); let minimal_l1_gas = minimal_gas_vector.l1_gas; let minimal_l2_gas = minimal_gas_vector.l2_gas; let minimal_l1_data_gas = minimal_gas_vector.l1_data_gas; if gas_vector_computation_mode == GasVectorComputationMode::NoL2Gas || !use_kzg_da { - assert!(minimal_l1_gas > 0); + assert!(minimal_l1_gas > GasAmount(0)); } - assert_eq!(minimal_l2_gas > 0, gas_vector_computation_mode == GasVectorComputationMode::All); - assert_eq!(minimal_l1_data_gas > 0, use_kzg_da); + assert_eq!( + minimal_l2_gas > GasAmount(0), + gas_vector_computation_mode == GasVectorComputationMode::All + ); + assert_eq!(minimal_l1_data_gas > GasAmount(0), use_kzg_da); } #[rstest] @@ -895,9 +906,9 @@ fn test_max_fee_exceeds_balance( let invalid_max_fee = Fee(BALANCE + 1); // TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion works. - let balance_over_gas_price: u64 = - (BALANCE / MAX_L1_GAS_PRICE).try_into().expect("Failed to convert u128 to u64."); - let invalid_resource_bounds = l1_resource_bounds(balance_over_gas_price + 1, MAX_L1_GAS_PRICE); + let balance_over_gas_price = BALANCE / MAX_L1_GAS_PRICE; + let invalid_resource_bounds = + l1_resource_bounds(GasAmount(balance_over_gas_price + 1), MAX_L1_GAS_PRICE); // V1 Invoke. let invalid_tx = account_invoke_tx(invoke_tx_args! { @@ -941,11 +952,11 @@ fn test_max_fee_exceeds_balance( #[rstest] fn test_insufficient_new_resource_bounds( - block_context: BlockContext, + mut block_context: BlockContext, + #[values(true, false)] use_kzg_da: bool, #[values(CairoVersion::Cairo0, CairoVersion::Cairo1)] account_cairo_version: CairoVersion, ) { - // TODO(Aner): test also with use_kzg_flag == true - + block_context.block_info.use_kzg_da = use_kzg_da; let block_context = &block_context; let account_contract = FeatureContract::AccountWithoutValidations(account_cairo_version); let test_contract = FeatureContract::TestContract(CairoVersion::Cairo0); @@ -969,69 +980,94 @@ fn test_insufficient_new_resource_bounds( } = block_context.block_info.gas_prices.get_gas_prices_by_fee_type(&FeeType::Strk); let minimal_gas_vector = - estimate_minimal_gas_vector(block_context, tx, &GasVectorComputationMode::All).unwrap(); + estimate_minimal_gas_vector(block_context, tx, &GasVectorComputationMode::All); let default_resource_bounds = AllResourceBounds { l1_gas: ResourceBounds { - max_amount: minimal_gas_vector.l1_gas.try_into().unwrap(), + max_amount: minimal_gas_vector.l1_gas.0.try_into().unwrap(), max_price_per_unit: actual_strk_l1_gas_price.into(), }, l2_gas: ResourceBounds { - max_amount: minimal_gas_vector.l2_gas.try_into().unwrap(), + max_amount: minimal_gas_vector.l2_gas.0.try_into().unwrap(), max_price_per_unit: actual_strk_l2_gas_price.into(), }, l1_data_gas: ResourceBounds { - max_amount: minimal_gas_vector.l1_data_gas.try_into().unwrap(), + max_amount: minimal_gas_vector.l1_data_gas.0.try_into().unwrap(), max_price_per_unit: actual_strk_l1_data_gas_price.into(), }, }; // Verify successful execution on default resource bounds. - account_invoke_tx(InvokeTxArgs { + let valid_resources_tx = account_invoke_tx(InvokeTxArgs { resource_bounds: ValidResourceBounds::AllResources(default_resource_bounds), ..valid_invoke_tx_args.clone() }) - .execute(state, block_context, true, true) - .expect("Transaction failed with default prices"); + .execute(state, block_context, true, true); - // Max gas price too low, new resource bounds. - // TODO(Aner): add a test for more than 1 insufficient resource price, after error message + let next_nonce = match valid_resources_tx { + Ok(_) => 1, + Err(err) => match err { + TransactionExecutionError::TransactionPreValidationError( + TransactionPreValidationError::TransactionFeeError( + TransactionFeeError::MaxGasAmountTooLow { .. }, + ), + ) => panic!("Transaction failed with expected minimal amount."), + TransactionExecutionError::TransactionPreValidationError( + TransactionPreValidationError::TransactionFeeError( + TransactionFeeError::MaxGasPriceTooLow { .. }, + ), + ) => panic!("Transaction failed with expected minimal price."), + // Ignore failures other than those above (e.g., post-validation errors). + _ => 0, + }, + }; + + // Max gas amount too low, new resource bounds. + // TODO(Aner): add a test for more than 1 insufficient resource amount, after error message // contains all insufficient resources. - for (insufficient_resource, insufficient_price_resource_bounds) in [ - ( - L1Gas, - AllResourceBounds { - l1_gas: ResourceBounds { - max_amount: default_resource_bounds.l1_gas.max_amount, - max_price_per_unit: u128::from(actual_strk_l1_gas_price) - 1, - }, - ..default_resource_bounds - }, - ), - ( - L2Gas, - AllResourceBounds { - l2_gas: ResourceBounds { - max_amount: default_resource_bounds.l2_gas.max_amount, - max_price_per_unit: u128::from(actual_strk_l2_gas_price) - 1, - }, - ..default_resource_bounds - }, - ), - ( - L1DataGas, - AllResourceBounds { - l1_data_gas: ResourceBounds { - max_amount: default_resource_bounds.l1_data_gas.max_amount, - max_price_per_unit: u128::from(actual_strk_l1_data_gas_price) - 1, - }, - ..default_resource_bounds - }, - ), + for (insufficient_resource, resource_bounds) in [ + (L1Gas, default_resource_bounds.l1_gas), + (L2Gas, default_resource_bounds.l2_gas), + (L1DataGas, default_resource_bounds.l1_data_gas), ] { + if resource_bounds.max_amount == 0 { + continue; + } + let mut invalid_resources = default_resource_bounds; + match insufficient_resource { + L1Gas => invalid_resources.l1_gas.max_amount -= 1, + L2Gas => invalid_resources.l2_gas.max_amount -= 1, + L1DataGas => invalid_resources.l1_data_gas.max_amount -= 1, + } let invalid_v3_tx = account_invoke_tx(InvokeTxArgs { - resource_bounds: ValidResourceBounds::AllResources(insufficient_price_resource_bounds), - nonce: nonce!(1), + resource_bounds: ValidResourceBounds::AllResources(invalid_resources), + nonce: nonce!(next_nonce), + ..valid_invoke_tx_args.clone() + }); + let execution_error = invalid_v3_tx.execute(state, block_context, true, true).unwrap_err(); + assert_matches!( + execution_error, + TransactionExecutionError::TransactionPreValidationError( + TransactionPreValidationError::TransactionFeeError( + TransactionFeeError::MaxGasAmountTooLow{ + resource, + ..})) + if resource == insufficient_resource + ); + } + + // Max gas price too low, new resource bounds. + for insufficient_resource in [L1Gas, L2Gas, L1DataGas] { + let mut invalid_resources = default_resource_bounds; + match insufficient_resource { + L1Gas => invalid_resources.l1_gas.max_price_per_unit -= 1, + L2Gas => invalid_resources.l2_gas.max_price_per_unit -= 1, + L1DataGas => invalid_resources.l1_data_gas.max_price_per_unit -= 1, + } + + let invalid_v3_tx = account_invoke_tx(InvokeTxArgs { + resource_bounds: ValidResourceBounds::AllResources(invalid_resources), + nonce: nonce!(next_nonce), ..valid_invoke_tx_args.clone() }); let execution_error = invalid_v3_tx.execute(state, block_context, true, true).unwrap_err(); @@ -1069,16 +1105,14 @@ fn test_insufficient_resource_bounds( // The minimal gas estimate does not depend on tx version. let tx = &account_invoke_tx(valid_invoke_tx_args.clone()); let minimal_l1_gas = - estimate_minimal_gas_vector(block_context, tx, &GasVectorComputationMode::NoL2Gas) - .unwrap() - .l1_gas; + estimate_minimal_gas_vector(block_context, tx, &GasVectorComputationMode::NoL2Gas).l1_gas; // Test V1 transaction. let gas_prices = &block_context.block_info.gas_prices; // TODO(Aner, 21/01/24) change to linear combination. let minimal_fee = - Fee(minimal_l1_gas * u128::from(gas_prices.get_l1_gas_price_by_fee_type(&FeeType::Eth))); + Fee(minimal_l1_gas.0 * u128::from(gas_prices.get_l1_gas_price_by_fee_type(&FeeType::Eth))); // Max fee too low (lower than minimal estimated fee). let invalid_max_fee = Fee(minimal_fee.0 - 1); let invalid_v1_tx = account_invoke_tx( @@ -1098,18 +1132,14 @@ fn test_insufficient_resource_bounds( // Test V3 transaction. let actual_strk_l1_gas_price = gas_prices.get_l1_gas_price_by_fee_type(&FeeType::Strk); - // Max L1 gas amount too low. + // Max L1 gas amount too low, old resource bounds. // TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion works. - let insufficient_max_l1_gas_amount = - (minimal_l1_gas - 1).try_into().expect("Failed to convert u128 to u64."); + let insufficient_max_l1_gas_amount = GasAmount(minimal_l1_gas.0 - 1); let invalid_v3_tx = account_invoke_tx(invoke_tx_args! { resource_bounds: l1_resource_bounds(insufficient_max_l1_gas_amount, actual_strk_l1_gas_price.into()), ..valid_invoke_tx_args.clone() }); let execution_error = invalid_v3_tx.execute(state, block_context, true, true).unwrap_err(); - // TODO(Ori, 1/2/2024): Write an indicative expect message explaining why the conversion works. - let minimal_l1_gas_as_u64 = - u64::try_from(minimal_l1_gas).expect("Failed to convert u128 to u64."); assert_matches!( execution_error, TransactionExecutionError::TransactionPreValidationError( @@ -1119,12 +1149,11 @@ fn test_insufficient_resource_bounds( max_gas_amount, minimal_gas_amount})) if max_gas_amount == insufficient_max_l1_gas_amount && - minimal_gas_amount == minimal_l1_gas_as_u64 && resource == L1Gas + minimal_gas_amount == minimal_l1_gas && resource == L1Gas ); // Max L1 gas price too low, old resource bounds. let insufficient_max_l1_gas_price = u128::from(actual_strk_l1_gas_price) - 1; - let minimal_l1_gas = minimal_l1_gas.try_into().unwrap(); let invalid_v3_tx = account_invoke_tx(invoke_tx_args! { resource_bounds: l1_resource_bounds(minimal_l1_gas, insufficient_max_l1_gas_price), ..valid_invoke_tx_args.clone() @@ -1162,11 +1191,9 @@ fn test_actual_fee_gt_resource_bounds( }; let tx = &account_invoke_tx(invoke_tx_args.clone()); let minimal_l1_gas = - estimate_minimal_gas_vector(block_context, tx, &GasVectorComputationMode::NoL2Gas) - .unwrap() - .l1_gas; + estimate_minimal_gas_vector(block_context, tx, &GasVectorComputationMode::NoL2Gas).l1_gas; let minimal_resource_bounds = l1_resource_bounds( - u64::try_from(minimal_l1_gas).unwrap(), + minimal_l1_gas, u128::from( block_context.block_info.gas_prices.get_l1_gas_price_by_fee_type(&FeeType::Strk), ), @@ -1181,7 +1208,7 @@ fn test_actual_fee_gt_resource_bounds( // Test error. assert!(execution_error.starts_with(&format!("Insufficient max {resource}", resource = L1Gas))); // Test that fee was charged. - let minimal_fee = Fee(minimal_l1_gas + let minimal_fee = Fee(minimal_l1_gas.0 * u128::from( block_context.block_info.gas_prices.get_l1_gas_price_by_fee_type(&FeeType::Strk), )); @@ -1262,6 +1289,7 @@ fn declare_validate_callinfo( declared_class_hash: ClassHash, account_class_hash: ClassHash, account_address: ContractAddress, + tracked_resource: TrackedResource, ) -> Option { // V0 transactions do not run validate. if version == TransactionVersion::ZERO { @@ -1274,6 +1302,7 @@ fn declare_validate_callinfo( calldata![declared_class_hash.0], account_address, declared_contract_version, + tracked_resource, ) } } @@ -1328,11 +1357,12 @@ fn test_declare_tx( let class_info = calculate_class_info_for_testing(empty_contract.get_class()); let sender_address = account.get_instance_address(0); let mut nonce_manager = NonceManager::default(); + let state_changes_for_fee = declare_expected_state_changes_count(tx_version); let starknet_resources = StarknetResources::new( 0, 0, class_info.code_size(), - declare_expected_state_changes_count(tx_version), + StateResources::new_for_testing(state_changes_for_fee), None, ExecutionSummary::default(), ); @@ -1367,11 +1397,14 @@ fn test_declare_tx( class_hash, account.get_class_hash(), sender_address, + account + .get_class() + .tracked_resource(&versioned_constants.min_compiler_version_for_sierra_gas), ); // Build expected fee transfer call info. let expected_actual_fee = - actual_execution_info.receipt.resources.calculate_tx_fee(block_context, fee_type).unwrap(); + actual_execution_info.receipt.resources.calculate_tx_fee(block_context, fee_type); let expected_fee_transfer_call_info = expected_fee_transfer_call_info( tx_context, sender_address, @@ -1379,14 +1412,13 @@ fn test_declare_tx( FeatureContract::ERC20(CairoVersion::Cairo0).get_class_hash(), ); - let da_gas = starknet_resources.get_state_changes_cost(use_kzg_da); + let da_gas = starknet_resources.state.to_gas_vector(use_kzg_da); let expected_cairo_resources = get_expected_cairo_resources( versioned_constants, TransactionType::Declare, &starknet_resources, vec![&expected_validate_call_info], ); - let state_changes_count = starknet_resources.state_changes_for_fee; let mut expected_actual_resources = TransactionResources { starknet_resources, computation: ComputationResources { @@ -1397,14 +1429,16 @@ fn test_declare_tx( add_kzg_da_resources_to_resources_mapping( &mut expected_actual_resources.computation.vm_resources, - &state_changes_count, + &state_changes_for_fee, versioned_constants, use_kzg_da, ); - let expected_total_gas = expected_actual_resources - .to_gas_vector(versioned_constants, use_kzg_da, &GasVectorComputationMode::NoL2Gas) - .unwrap(); + let expected_total_gas = expected_actual_resources.to_gas_vector( + versioned_constants, + use_kzg_da, + &GasVectorComputationMode::NoL2Gas, + ); let expected_execution_info = TransactionExecutionInfo { validate_call_info: expected_validate_call_info, @@ -1517,6 +1551,9 @@ fn test_deploy_account_tx( Calldata(validate_calldata.into()), deployed_account_address, cairo_version, + account + .get_class() + .tracked_resource(&versioned_constants.min_compiler_version_for_sierra_gas), ); // Build expected execute call info. @@ -1535,7 +1572,7 @@ fn test_deploy_account_tx( // Build expected fee transfer call info. let expected_actual_fee = - actual_execution_info.receipt.resources.calculate_tx_fee(block_context, fee_type).unwrap(); + actual_execution_info.receipt.resources.calculate_tx_fee(block_context, fee_type); let expected_fee_transfer_call_info = expected_fee_transfer_call_info( tx_context, deployed_account_address, @@ -1573,13 +1610,11 @@ fn test_deploy_account_tx( use_kzg_da, ); - let expected_total_gas = actual_resources - .to_gas_vector( - &block_context.versioned_constants, - block_context.block_info.use_kzg_da, - &GasVectorComputationMode::NoL2Gas, - ) - .unwrap(); + let expected_total_gas = actual_resources.to_gas_vector( + &block_context.versioned_constants, + block_context.block_info.use_kzg_da, + &GasVectorComputationMode::NoL2Gas, + ); let expected_execution_info = TransactionExecutionInfo { validate_call_info: expected_validate_call_info, @@ -1725,11 +1760,7 @@ fn test_validate_accounts_tx( ..default_args }); let error = account_tx.execute(state, block_context, true, true).unwrap_err(); - check_transaction_execution_error_for_invalid_scenario!( - cairo_version, - error, - validate_constructor, - ); + check_tx_execution_error_for_invalid_scenario!(cairo_version, error, validate_constructor,); // Try to call another contract (forbidden). let account_tx = create_account_tx_for_validate_test_nonce_0(FaultyAccountTxCreatorArgs { @@ -1742,7 +1773,7 @@ fn test_validate_accounts_tx( ..default_args }); let error = account_tx.execute(state, block_context, true, true).unwrap_err(); - check_transaction_execution_error_for_custom_hint!( + check_tx_execution_error_for_custom_hint!( &error, "Unauthorized syscall call_contract in execution mode Validate.", validate_constructor, @@ -1758,7 +1789,7 @@ fn test_validate_accounts_tx( ..default_args }); let error = account_tx.execute(state, block_context, true, true).unwrap_err(); - check_transaction_execution_error_for_custom_hint!( + check_tx_execution_error_for_custom_hint!( &error, "Unauthorized syscall get_block_hash in execution mode Validate.", validate_constructor, @@ -1773,7 +1804,7 @@ fn test_validate_accounts_tx( ..default_args }); let error = account_tx.execute(state, block_context, true, true).unwrap_err(); - check_transaction_execution_error_for_custom_hint!( + check_tx_execution_error_for_custom_hint!( &error, "Unauthorized syscall get_sequencer_address in execution mode Validate.", validate_constructor, @@ -2002,7 +2033,11 @@ fn test_only_query_flag( } #[rstest] -fn test_l1_handler(#[values(false, true)] use_kzg_da: bool) { +fn test_l1_handler( + #[values(false, true)] use_kzg_da: bool, + #[values(GasVectorComputationMode::NoL2Gas, GasVectorComputationMode::All)] + gas_vector_computation_mode: GasVectorComputationMode, +) { let cairo_version = CairoVersion::Cairo1; let test_contract = FeatureContract::TestContract(cairo_version); let chain_info = &ChainInfo::create_for_testing(); @@ -2043,19 +2078,30 @@ fn test_l1_handler(#[values(false, true)] use_kzg_da: bool) { builtin_instance_counter: HashMap::from([(BuiltinName::range_check, 6)]), }, accessed_storage_keys: HashSet::from_iter(vec![accessed_storage_key]), + tracked_resource: test_contract + .get_class() + .tracked_resource(&versioned_constants.min_compiler_version_for_sierra_gas), ..Default::default() }; // Build the expected resource mapping. // TODO(Nimrod, 1/5/2024): Change these hard coded values to match to the transaction resources // (currently matches only starknet resources). - let expected_gas = match use_kzg_da { - true => GasVector { l1_gas: 16023, l1_data_gas: 128, l2_gas: 0 }, - false => GasVector::from_l1_gas(17675), + let mut expected_gas = match use_kzg_da { + true => GasVector { + l1_gas: 16023_u32.into(), + l1_data_gas: 128_u32.into(), + l2_gas: 0_u32.into(), + }, + false => GasVector::from_l1_gas(17675_u32.into()), }; + if gas_vector_computation_mode == GasVectorComputationMode::All { + expected_gas += GasVector::from_l2_gas(25_u32.into()); + } + let expected_da_gas = match use_kzg_da { - true => GasVector::from_l1_data_gas(128), - false => GasVector::from_l1_gas(1652), + true => GasVector::from_l1_data_gas(128_u32.into()), + false => GasVector::from_l1_gas(1652_u32.into()), }; let state_changes_count = StateChangesCount { @@ -2098,17 +2144,15 @@ fn test_l1_handler(#[values(false, true)] use_kzg_da: bool) { actual_execution_info.receipt.resources.starknet_resources.to_gas_vector( versioned_constants, use_kzg_da, - &GasVectorComputationMode::NoL2Gas + &gas_vector_computation_mode ) ); - let total_gas = expected_tx_resources - .to_gas_vector( - versioned_constants, - block_context.block_info.use_kzg_da, - &GasVectorComputationMode::NoL2Gas, - ) - .unwrap(); + let total_gas = expected_tx_resources.to_gas_vector( + versioned_constants, + block_context.block_info.use_kzg_da, + &GasVectorComputationMode::NoL2Gas, + ); // Build the expected execution info. let expected_execution_info = TransactionExecutionInfo { @@ -2142,8 +2186,7 @@ fn test_l1_handler(#[values(false, true)] use_kzg_da: bool) { let error = tx_no_fee.execute(state, block_context, true, true).unwrap_err(); // Today, we check that the paid_fee is positive, no matter what was the actual fee. let expected_actual_fee = - (expected_execution_info.receipt.resources.calculate_tx_fee(block_context, &FeeType::Eth)) - .unwrap(); + expected_execution_info.receipt.resources.calculate_tx_fee(block_context, &FeeType::Eth); assert_matches!( error, TransactionExecutionError::TransactionFeeError( @@ -2153,7 +2196,7 @@ fn test_l1_handler(#[values(false, true)] use_kzg_da: bool) { } #[rstest] -fn test_execute_tx_with_invalid_transaction_version( +fn test_execute_tx_with_invalid_tx_version( block_context: BlockContext, max_l1_resource_bounds: ValidResourceBounds, ) { diff --git a/crates/blockifier/src/versioned_constants.rs b/crates/blockifier/src/versioned_constants.rs index 578970d4d6e..dff21a000d7 100644 --- a/crates/blockifier/src/versioned_constants.rs +++ b/crates/blockifier/src/versioned_constants.rs @@ -1,7 +1,7 @@ use std::collections::{HashMap, HashSet}; -use std::io; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::{Arc, LazyLock}; +use std::{fs, io}; use cairo_vm::types::builtin_name::BuiltinName; use cairo_vm::vm::runners::cairo_runner::ExecutionResources; @@ -13,16 +13,16 @@ use semver::Version; use serde::de::Error as DeserializationError; use serde::{Deserialize, Deserializer}; use serde_json::{Map, Number, Value}; +use starknet_api::execution_resources::GasAmount; +use starknet_api::transaction::GasVectorComputationMode; use strum::IntoEnumIterator; use strum_macros::{EnumCount, EnumIter}; use thiserror::Error; use crate::execution::deprecated_syscalls::hint_processor::SyscallCounter; -use crate::execution::errors::PostExecutionError; use crate::execution::execution_utils::poseidon_hash_many_cost; use crate::execution::syscalls::SyscallSelector; -use crate::fee::resources::{GasVectorComputationMode, StarknetResources}; -use crate::transaction::errors::TransactionExecutionError; +use crate::fee::resources::StarknetResources; use crate::transaction::transaction_types::TransactionType; #[cfg(test)] @@ -31,20 +31,42 @@ pub mod test; /// Auto-generate getters for listed versioned constants versions. macro_rules! define_versioned_constants { - ($(($variant:ident, $path_to_json:expr)),* $(,)?) => { + ($(($variant:ident, $path_to_json:expr, $version_str:expr)),*, $latest_variant:ident) => { /// Enum of all the Starknet versions supporting versioned constants. #[derive(Clone, Debug, EnumCount, EnumIter, Hash, Eq, PartialEq)] pub enum StarknetVersion { $($variant,)* } + impl StarknetVersion { + pub fn path_to_versioned_constants_json(&self) -> &'static str { + match self { + $(StarknetVersion::$variant => $path_to_json,)* + } + } + + pub fn latest() -> Self { + StarknetVersion::$latest_variant + } + } + + impl From for String { + fn from(version: StarknetVersion) -> Self { + match version { + $(StarknetVersion::$variant => String::from( + stringify!($variant) + ).to_lowercase().replace("_", "."),)* + } + } + } + // Static (lazy) instances of the versioned constants. // For internal use only; for access to a static instance use the `StarknetVersion` enum. paste! { $( pub(crate) const []: &str = include_str!($path_to_json); - static []: LazyLock = LazyLock::new(|| { + pub static []: LazyLock = LazyLock::new(|| { serde_json::from_str([]) .expect(&format!("Versioned constants {} is malformed.", $path_to_json)) }); @@ -63,16 +85,53 @@ macro_rules! define_versioned_constants { } } } + + pub static VERSIONED_CONSTANTS_LATEST_JSON: LazyLock = LazyLock::new(|| { + let latest_variant = StarknetVersion::$latest_variant; + let path_to_json: PathBuf = [ + env!("CARGO_MANIFEST_DIR"), "src", latest_variant.path_to_versioned_constants_json() + ].iter().collect(); + fs::read_to_string(path_to_json.clone()) + .expect(&format!("Failed to read file {}.", path_to_json.display())) + }); + + impl TryFrom<&str> for StarknetVersion { + type Error = VersionedConstantsError; + fn try_from(raw_version: &str) -> Result { + match raw_version { + $( + $version_str => Ok(StarknetVersion::$variant), + )* + _ => Err(VersionedConstantsError::InvalidVersion { version: raw_version.to_string()}), + } + } + } + + #[cfg(test)] + mod tests { + use crate::versioned_constants::StarknetVersion; + + #[test] + fn test_variant_name_string_consistency() { + $( + assert_eq!( + "v".to_owned() + $version_str, + String::from(StarknetVersion::$variant) + ); + )* + } + } }; } define_versioned_constants! { - (V0_13_0, "../resources/versioned_constants_13_0.json"), - (V0_13_1, "../resources/versioned_constants_13_1.json"), - (V0_13_1_1, "../resources/versioned_constants_13_1_1.json"), - (V0_13_2, "../resources/versioned_constants_13_2.json"), - (V0_13_2_1, "../resources/versioned_constants_13_2_1.json"), - (Latest, "../resources/versioned_constants.json"), + (V0_13_0, "../resources/versioned_constants_0_13_0.json", "0.13.0"), + (V0_13_1, "../resources/versioned_constants_0_13_1.json", "0.13.1"), + (V0_13_1_1, "../resources/versioned_constants_0_13_1_1.json", "0.13.1.1"), + (V0_13_2, "../resources/versioned_constants_0_13_2.json", "0.13.2"), + (V0_13_2_1, "../resources/versioned_constants_0_13_2_1.json", "0.13.2.1"), + (V0_13_3, "../resources/versioned_constants_0_13_3.json", "0.13.3"), + V0_13_3 } pub type ResourceCost = Ratio; @@ -145,15 +204,19 @@ pub struct VersionedConstants { } impl VersionedConstants { - /// Get the constants for the specified Starknet version. + /// Gets the constants that shipped with the current version of the Blockifier. + /// To use custom constants, initialize the struct from a file using `from_path`. + pub fn latest_constants() -> &'static Self { + Self::get(StarknetVersion::latest()) + } + + /// Gets the constants for the specified Starknet version. pub fn get(version: StarknetVersion) -> &'static Self { version.into() } - /// Get the constants that shipped with the current version of the Blockifier. - /// To use custom constants, initialize the struct from a file using `try_from`. - pub fn latest_constants() -> &'static Self { - Self::get(StarknetVersion::Latest) + pub fn from_path(path: &Path) -> Result { + Ok(serde_json::from_reader(std::fs::File::open(path)?)?) } /// Converts from L1 gas price to L2 gas price with **upward rounding**. @@ -162,9 +225,9 @@ impl VersionedConstants { } /// Converts from L1 gas amount to L2 gas amount with **upward rounding**. - pub fn convert_l1_to_l2_gas_amount_round_up(&self, l1_gas_amount: u128) -> u128 { + pub fn convert_l1_to_l2_gas_amount_round_up(&self, l1_gas_amount: GasAmount) -> GasAmount { // The amount ratio is the inverse of the price ratio. - *(self.l1_to_l2_gas_price_ratio().inv() * l1_gas_amount).ceil().numer() + GasAmount(*(self.l1_to_l2_gas_price_ratio().inv() * l1_gas_amount.0).ceil().numer()) } /// Returns the following ratio: L2_gas_price/L1_gas_price. @@ -205,11 +268,11 @@ impl VersionedConstants { tx_type: TransactionType, starknet_resources: &StarknetResources, use_kzg_da: bool, - ) -> Result { + ) -> ExecutionResources { self.os_resources.get_additional_os_tx_resources( tx_type, starknet_resources.archival_data.calldata_length, - starknet_resources.get_onchain_data_segment_length(), + starknet_resources.state.get_onchain_data_segment_length(), use_kzg_da, ) } @@ -217,7 +280,7 @@ impl VersionedConstants { pub fn get_additional_os_syscall_resources( &self, syscall_counter: &SyscallCounter, - ) -> Result { + ) -> ExecutionResources { self.os_resources.get_additional_os_syscall_resources(syscall_counter) } @@ -290,10 +353,6 @@ impl VersionedConstants { GasVectorComputationMode::NoL2Gas => &self.deprecated_l2_resource_gas_costs, } } - - pub fn from_path(path: &Path) -> Result { - Ok(serde_json::from_reader(std::fs::File::open(path)?)?) - } } #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] @@ -406,14 +465,14 @@ impl OsResources { calldata_length: usize, data_segment_length: usize, use_kzg_da: bool, - ) -> Result { + ) -> ExecutionResources { let mut os_additional_vm_resources = self.resources_for_tx_type(&tx_type, calldata_length); if use_kzg_da { os_additional_vm_resources += &self.os_kzg_da_resources(data_segment_length); } - Ok(os_additional_vm_resources) + os_additional_vm_resources } /// Calculates the additional resources needed for the OS to run the given syscalls; @@ -421,7 +480,7 @@ impl OsResources { fn get_additional_os_syscall_resources( &self, syscall_counter: &SyscallCounter, - ) -> Result { + ) -> ExecutionResources { let mut os_additional_resources = ExecutionResources::default(); for (syscall_selector, count) in syscall_counter { let syscall_resources = @@ -431,7 +490,7 @@ impl OsResources { os_additional_resources += &(syscall_resources * *count); } - Ok(os_additional_resources) + os_additional_resources } fn resources_params_for_tx_type(&self, tx_type: &TransactionType) -> &ResourcesParams { @@ -546,7 +605,7 @@ impl OsConstants { // not used by the blockifier but included for transparency. These constanst will be ignored // during the creation of the struct containing the gas costs. - const ADDITIONAL_FIELDS: [&'static str; 27] = [ + const ADDITIONAL_FIELDS: [&'static str; 29] = [ "block_hash_contract_address", "constructor_entry_point_selector", "default_entry_point_selector", @@ -556,6 +615,8 @@ impl OsConstants { "error_block_number_out_of_range", "error_invalid_input_len", "error_invalid_argument", + "error_entry_point_failed", + "error_entry_point_not_found", "error_out_of_gas", "execute_entry_point_selector", "l1_gas", @@ -693,6 +754,8 @@ pub enum VersionedConstantsError { IoError(#[from] io::Error), #[error("JSON file cannot be serialized into VersionedConstants: {0}")] ParseError(#[from] serde_json::Error), + #[error("Invalid version: {version:?}")] + InvalidVersion { version: String }, } #[derive(Debug, Error)] diff --git a/crates/blockifier/src/versioned_constants_test.rs b/crates/blockifier/src/versioned_constants_test.rs index 94e6615877d..32ebe68d76e 100644 --- a/crates/blockifier/src/versioned_constants_test.rs +++ b/crates/blockifier/src/versioned_constants_test.rs @@ -39,7 +39,7 @@ fn test_successful_gas_costs_parsing() { /// Assert versioned constants overrides are used when provided. #[test] fn test_versioned_constants_overrides() { - let versioned_constants = VERSIONED_CONSTANTS_LATEST.clone(); + let versioned_constants = VersionedConstants::latest_constants().clone(); let updated_invoke_tx_max_n_steps = versioned_constants.invoke_tx_max_n_steps + 1; let updated_validate_max_n_steps = versioned_constants.validate_max_n_steps + 1; let updated_max_recursion_depth = versioned_constants.max_recursion_depth + 1; diff --git a/crates/blockifier_regression_test/Cargo.toml b/crates/blockifier_regression_test/Cargo.toml index e03b480c93e..92a5bf3b65f 100644 --- a/crates/blockifier_regression_test/Cargo.toml +++ b/crates/blockifier_regression_test/Cargo.toml @@ -5,11 +5,27 @@ edition.workspace = true repository.workspace = true license.workspace = true +[features] +blockifier_regression_https_testing = [] + [dependencies] blockifier.workspace = true +cairo-lang-starknet-classes.workspace = true +cairo-lang-utils.workspace = true +cairo-vm.workspace = true +flate2.workspace = true +papyrus_execution.workspace = true +serde.workspace = true +serde_json.workspace = true +starknet-core.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true starknet_gateway.workspace = true +[dev-dependencies] +assert_matches.workspace = true +pretty_assertions.workspace = true +rstest.workspace = true + [lints] workspace = true diff --git a/crates/blockifier_regression_test/resources/block_700000/tx_hashes_block_700000.json b/crates/blockifier_regression_test/resources/block_700000/tx_hashes_block_700000.json new file mode 100644 index 00000000000..a9192a1c92f --- /dev/null +++ b/crates/blockifier_regression_test/resources/block_700000/tx_hashes_block_700000.json @@ -0,0 +1,12 @@ +[ + "0x47165a9a9c97e8829a4778f2a4b6fae4366aefc35b51d484bf04c458168351b", + "0x57c0b8578fc39ae6ddadfec8973861f0481f9b056c2c6cbf682c8388ccf70e0", + "0x518d8ad0393e8895fefc0f05660f57208c3b743a7ca55e94729f21f4f182c34", + "0x7c593acf831585f0d1a5c318ab4d5c0ed75c2fbd6d253bf211029a8e6ecf7d1", + "0x2583a7ef17ac289e4e64bf3bf20aab7725dbfcc19d709af08c8306cf2037970", + "0x1c23a4eb97bd0539b0d15e9483fc34b9021d15139d954ec2dfea098abc9f48c", + "0x60c6cfe335697fbad3fb2b04edecc946e114d3e6fe1447fe6445f9fc7dc9998", + "0x15006aeeddec46275565f6d0bf344130bee1b5774b1424d024c1c907dead80", + "0x797cbf65f46fa84e738e99d56a938a344414166e30091a9bf0dbaba01f71b32", + "0xa7c7db686c7f756ceb7ca85a759caef879d425d156da83d6a836f86851983" +] \ No newline at end of file diff --git a/crates/blockifier_regression_test/resources/raw_rpc_json_objects/block_header.json b/crates/blockifier_regression_test/resources/raw_rpc_json_objects/block_header.json new file mode 100644 index 00000000000..120b7ff2b72 --- /dev/null +++ b/crates/blockifier_regression_test/resources/raw_rpc_json_objects/block_header.json @@ -0,0 +1,35 @@ +{ + "block_hash": "0x29851b3a78e95b3c9469ea53c0ca902f4df92be129f1929fdb4dde0eb0cda4c", + "block_number": 700000, + "l1_da_mode": "BLOB", + "l1_data_gas_price": { + "price_in_fri": "0x1459", + "price_in_wei": "0x1" + }, + "l1_gas_price": { + "price_in_fri": "0xd28710bbc88", + "price_in_wei": "0xa5883d4c" + }, + "l2_gas_price": { + "price_in_fri": "0x1", + "price_in_wei": "0x1" + }, + "new_root": "0x2c82376b7e0d7cddec192d51198bb74b96ee5aaf94268cbc15ae7551adf502", + "parent_hash": "0x7f1f233dee2c50193842d0f6af87695d7d8b26292d0e70e349df4d71b2f9398", + "sequencer_address": "0x1176a1bd84444c89232ec27754698e5d2e7e1a7f1539f12027f28b23ec9f3d8", + "starknet_version": "0.13.2.1", + "status": "ACCEPTED_ON_L1", + "timestamp": 1725724084, + "transactions": [ + "0x47165a9a9c97e8829a4778f2a4b6fae4366aefc35b51d484bf04c458168351b", + "0x57c0b8578fc39ae6ddadfec8973861f0481f9b056c2c6cbf682c8388ccf70e0", + "0x518d8ad0393e8895fefc0f05660f57208c3b743a7ca55e94729f21f4f182c34", + "0x7c593acf831585f0d1a5c318ab4d5c0ed75c2fbd6d253bf211029a8e6ecf7d1", + "0x2583a7ef17ac289e4e64bf3bf20aab7725dbfcc19d709af08c8306cf2037970", + "0x1c23a4eb97bd0539b0d15e9483fc34b9021d15139d954ec2dfea098abc9f48c", + "0x60c6cfe335697fbad3fb2b04edecc946e114d3e6fe1447fe6445f9fc7dc9998", + "0x15006aeeddec46275565f6d0bf344130bee1b5774b1424d024c1c907dead80", + "0x797cbf65f46fa84e738e99d56a938a344414166e30091a9bf0dbaba01f71b32", + "0xa7c7db686c7f756ceb7ca85a759caef879d425d156da83d6a836f86851983" + ] +} \ No newline at end of file diff --git a/crates/blockifier_regression_test/resources/raw_rpc_json_objects/deprecated_contract_class.json b/crates/blockifier_regression_test/resources/raw_rpc_json_objects/deprecated_contract_class.json new file mode 100644 index 00000000000..cd04db42da2 --- /dev/null +++ b/crates/blockifier_regression_test/resources/raw_rpc_json_objects/deprecated_contract_class.json @@ -0,0 +1,122 @@ +{ + "abi": [ + { + "data": [ + { + "name": "implementation", + "type": "felt" + } + ], + "keys": [], + "name": "Upgraded", + "type": "event" + }, + { + "inputs": [ + { + "name": "implementation_address", + "type": "felt" + }, + { + "name": "initializer_selector", + "type": "felt" + }, + { + "name": "calldata_len", + "type": "felt" + }, + { + "name": "calldata", + "type": "felt*" + } + ], + "name": "constructor", + "outputs": [], + "type": "constructor" + }, + { + "inputs": [], + "name": "get_implementation", + "outputs": [ + { + "name": "implementation", + "type": "felt" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "name": "selector", + "type": "felt" + }, + { + "name": "calldata_size", + "type": "felt" + }, + { + "name": "calldata", + "type": "felt*" + } + ], + "name": "__default__", + "outputs": [ + { + "name": "retdata_size", + "type": "felt" + }, + { + "name": "retdata", + "type": "felt*" + } + ], + "type": "function" + }, + { + "inputs": [ + { + "name": "selector", + "type": "felt" + }, + { + "name": "calldata_size", + "type": "felt" + }, + { + "name": "calldata", + "type": "felt*" + } + ], + "name": "__l1_default__", + "outputs": [], + "type": "l1_handler" + } + ], + "entry_points_by_type": { + "CONSTRUCTOR": [ + { + "offset": "0x91", + "selector": "0x28ffe4ff0f226a9107253e17a904099aa4f63a02a5621de0576e5aa71bc5194" + } + ], + "EXTERNAL": [ + { + "offset": "0xde", + "selector": "0x0" + }, + { + "offset": "0xbd", + "selector": "0x21691762da057c1b71f851f9b709e0c143628acf6e0cbc9735411a65663d747" + } + ], + "L1_HANDLER": [ + { + "offset": "0xf7", + "selector": "0x0" + } + ] + }, + "program": "H4sIAAAAAAAA/+x9a2/buLb2XzH8aWZ2dsA7pQLzIW09s4s3bedN0jOzT1EIskSnQm3ZW1LaZA/63w90syWLlEhdnHSmAYLEsrjW4uJ6Hi5eRP0530XBRsyfzebg3gKVHwiB2Q+cn83mbpJEwfIuEfH82ez9h7PZPPBFmASrQETppT/njrNxg9Bxzv/lxh+f3wXrJAizL3wRJ0HoJsE2/TyPEzf69MWNxLnnBtH23NtuNtsw/+As83JxTcjZbJ487LK6uOvAjedfz2YHdb9F2/sHqaLIO7/b3UauL+LzdbCM3OihuLtdouP4YuXerRPHKeR628hNtllF38/FfSKi0F2nUiL3ixOEu7uk/LC9S9JPqYN23vzZDAFSUba6C73MPJW+84voNnfn6m69dkI3b0L1rWezeRz8N70Jn83mG7FZlu0Ri7Xwkm2UfdiuVrFI5s9m4Gw2z129t0msk8wez12vfTdxnUJgpRTsLlUvgKQFfpp//VrxRpxEd16i9sWrzW4deEFi4JNakRbfPMSp3c4u0XLPT5mNO+GLKBZhs1TTPcZBnquI3PBWON5H4X1qapH71MylVyK5i3JY1kX9EIlk3/jPZqnks1lxLf/4049V2KR/U7lBGLQH9PWr/128/cW5fPvi4vI6U/zZXd+JwtelOG8bxg2z17AdiGvofHRDfy2iOhQP4MNQA3xVNd34a97914NgrY7aKFSX+g5EmYvUWDRBWk1kb7Bll1KLi2A9Qlr16z26ILI60VUp2AWtxq2HqCFHURNsdmuxEWGS9fWO6/uRiGNtlGWedNfBf0XkyCGqATZnLUK9sFAiFPdFaNVVmvBUFvmOzYZ/xgBmVV5vVN6KxKnHugycnwPxpYJK3p1wNuV2gVNV4hA9oB49Xf6WCNSM5K6S3wNa5SZ1XNdvzPM9/ViXqOod8sU4zUkbqn30mP0XiqT0ctG6cV1E+0iveqtTSSeHK3bqyanMhi+Ru9uJKB57xImQmgDkOpXob7vdHPoKaV241yk2mjFqjFTYoxgQnc2q5PBsZg7/s9kR9suR16BRmKJq5phUCCq/8J00umRgkU6lGMHAEaG39YUTZe1xvhEbbyef5ZE7PS9QltNRPXy0Sbgm8LRGnB0lBka80RBPs+SYJv3lcDhwnKaWZYTGWpCbouLUmOw1KCVUC4Q6A9O22wfFusn4TafYaMb8xVA3bBCmEGSAt2qEGoX7qZHWa6Bp2VpIMxhsapQaFOo9Bp0Gpcc27S8GxlFGie3yDKApifg+MKkDVQKaClxAT7gckUGvSJXJOMQrOpooiUTilC3ROk2imDjonNRQza/2Q8pR5QZBuk3WyAA/UtWyNCcHZjkLXf08EjKPTFPjFPbB6Ql6N+na/y8Xl9cLAz3L7XZdFNJXcprdDy17G1KlZYE06OOd64mOQgpS6SpgCogWee2g1S04okEDFgFapJr2eC2inFgnaTv0P0A5O2GmxDxWlEIOzQWPeqBQfJHVrXN1z7ydZcb1DsZOYX/vxQlDz00DQZmmEWGpNZSqrKErJ+uNdJiDcqwBlpmK3sj6vurXE1jTrwOaKR8Pao7rb4okyzTfyYsag6ZabDycFFIfO3EszOiLUFnx0X00SY9QyB47MM8/uvFHZNCU6f1FoX5NF26jTbatqbYjSn8dO9lG7q2QiOlnTiHPiYTr91xRr4kYZsWXKEjEQDNyGaZ2tOxWM+YtuSxzImuTMyJqFWoeneoUdvXmPh1507t1GnZUKBuRLhUa0r9tuTPvMaBt0zUaig7Cpm/zTNfYcdsUqp4rVmfKzfx6ikzZcCq5pzPanpuIB+fJraqnB9oJExWFBU8mc1HYV0lhpFRk9RjFt+kajYoOwqanokzX2FTUFPp9pN/Dg7pD/rLUZJSWWTM9pT322KPDrKc2GDmvGiRlOFu5raqnstEoriJNvZ6gtZo9MvByw8YmRInU74zYx4UnHS3lKsfkvf0zbH7PmYSDgB5QPC48ZnZRkf34EwUVY/ojWSVkIq9NFNkVDdME8kkHIhW1T2f0UTHq0TMYiS2PkbbcXL0z3rOTldFX8S6/YEykZTkT/qyXGYUA9iJ7EJS87LhmjU1He8HXi8vFi5u3V/UdaciGNiLEIgBSTqCNMbQpBJRwBCBhAFBsE5sibts2JhRhzC1kEWhByAgHFqbpRYsBTPW57GDTGMy4l+au11vpPlZF9Gf3F6XM4/9cbIKkbRBAsdEgoCa3F0gOBdWp/dTbhOrGDMFYU4B6WrVX5j75RGm9KpMBO5OuRhLqgaRUpCM+izDp2X9VBPRA1sn2uN6KJHv+WEQ9k5myvhJBciu6maiFUZR74rvFqghFs6BxH9ctt4MbDAVMYKAasIcnSbqh263HuBPs3H89TMY+h2zftq43nHweJF+CWFSHpTrt3C7l0Nj0qCO41+vRzmbzBz2+P5vN7x039J0HvWmX7P77bXR8v/z0muL+5u3EsPvR8uLCe7sb2BBVEYdW4MernD2XNoXn7LZBmJwvvN/Sv1nd/tPleBNhGz03p9lB/U7aX+3ANsul6PeFDQNMOqKWyaCeEXM0n6SYep0Ct5GI79bJsNlSrSpeB7ehm9xFQ1muIadlH8Hd0vkkHrS9thFx7N6KYTmuvBJ+4CWO63kijs9fBl5ykf2r7QNFcXWomFR7F4nPsmfzWqImFF9kJcYJnAY2db0kA7UiNEaHklkVs1nGE81Ed04yXrx8eeU8f/vuzctaQvNPCBiHgCObAkg5phzZFkXAIghAm1AMMLIBx5wAQBCl1LIsajEObVJ7jFuWDKlMeX3xh3N98/bq4teF8+pm8dpJ0776QI2ynrLdOBZR4iAKnGVg0lds3OTjcWlTV5dDoBcXl5fOi7dvbq4uXtw40tkmBCyKEceEU4QA4YhYDNqQW8RCXXmqWq+7Xr/Yhknkeq140hOgTmoi8Z87ESf6qY2WwqtCatFh7bZhLPQzD00VhdhuJBuZPNTXpRx1Nm9+ImwhWn7IZQvrl6NpxQmXGodVSg6hbcn55UdcytNR9RGXfQNheMsVgtS9UPWAZO3mKwrptNogp7xcXC5+vbhZOBlnyakKQsapTQCD3MIIMAthaDMOGSS9qWqv9xI6/7p48/JycaXQjhEnAFILIJsji1BEOKXMIsi2GOY2hBZjvP44nKElv12+/bdcOacIIMIsSgCyusb+ag1it94+9Aq1oughuOzJqDhX1U3CbIjw4fRbN7O/T5uUywZT7tqNYyfN+rTJ9pilndjVHaedzaoHWTk9yVciQZOI8wAR0edi/bOll9ZMnnWjZ0ijN9j6eEzV3m+2d7h7V6opvz0WjgXoRMIw+q+PUvVSZfU4tVe2vHj96sZZ/M/ijSJVhsgCHNgYQEoBxpQTmxOrLxkvNkGy2C/nmIbRofSIqdon8RA3TwVviZS0gGZonM3m8kPHW0jhJNnYr4sb5/nl2xf/z3nz7vVzVe8PCbGAZUPALEwpwgBAgi0OECYUIMAt43FiU//Nq9eL65uL178pEhCCbGIDzDFiiDFCLWYhSBGmnFvcRpxyTiGkvROQ1JI0+1pcOekgfXF9LTcktSLNu7gFsY0BtwghiBILYAA5oxYEiBE8yIpyyNpqB0PQJjbFDFiAEhsSDlN/2BxADDhgiEAbWQgAa5Ax14v//27x5kWXVyC1EbSBhTG1oG1DDjCg2ALUQja1CYPpV6k1NA2cIQbd/OG8evPLW4UZGHKAbGwDBAiECAIM0KD63/zhXL/69c3FzburRTs4kMUowgQATjjkkCHEOQDc5v31i+T5eut9enOX0lovpjwS0TY8GiuDravszmTbdiVrKxme0crNHu7zZoZ7vAPGrKcar4YD8jiFKHUdl+ndTngI5BPU8ybYiDhxN7tBVTxIOSV49lonxk9Fz3gQahg/iv+fHpCazhupoppwSmoBPm2FX2R7iS4qo7Ee9awLORGeakqng9ORmlHQJDV9DN8/KSzJPTdONTuRJNlsN3Fti5mNoVA6EnMqMNXVTginY0XjAEpu/jht8LRApfDfWFXtBlavGbxBdb5OGyD0BndTDTknAtex3unQ1dQ0CrxUFRipHZ4UwJQuHK2ynRCLyyInxNjN/atwte1byaL0ifCUa5sORaX8UbBTN3aQf58UTo6cNLBinZhI7p2gDNBhAZSrHDa7nhq+30TZv+4HEeoVs5GRs1c5JXwqSkbCUMPs4T5/Ymhq+myMKnZun4nLe5trWC1ranEt9qfcQXP56vnVxdW/8w00XbtZCGYIY0IRpwTaFqScA8oJQxRwy6aUU2gjZCFCGGSU9V5TqhklX01CmDOAGLcJRhxAyDmyKWSs95aey/xJshflCzhNI6Na/hR7Dyv6vpGthxKLBzp6io2H5rtg/rJbDq8Xb146rxfX1xe/Lpybtyk9KGgBA8htYHFmYYAJtm3Aoc0saiOCOeeg/1peudP6anHxUrGOCIBlA2ZjzAEDkDMACAIWGsAFpdLfr17dKFYvEbWQBSCnAFmcAArSujLGkM1x/8qK0H+dP+Bxs72E1w9xbzpSiDpTvlzeGCnJ1nhz7s59WG9dXxLuLSApCzWe7DZ9j7yO1/J9+Ffl4SPGXq+UP0XKWdHX3Qm0PXCnp2F4HyAxeKCfm31AIwUzjWyjsB7JFwNyUpkcddI9wpFw2ib9vj84p2edfi9PzVEd9DZp057NZN7q/QBZx4i5l58aE0PHCednEcXa54OkDvK87V2YOL0fwNi4985K6NN7ywCpJQdSDJDatvsmkRvGbp6pNfM71bbf9PJHNwid4GifcHMj+dAwaJzVIT06gqgPozFR0XmMhLYMdUoh3xnasZVUc8Sr3BnaEmrNpHmCFKLiHt2DMYxltUypGJ+fM0pNhxyJY6So/xEbOtKPvReJlYhE6IkMgn9WAOjunJQePwXhrdMMq9SW22h7t0uJpdqNzN97bpz8sNrN/jH74Z/8x7P88JGffvyQWlsooKBTAVQqyOS/3yv4MPtHSm3FESdfP2TD1d5BWPWPIp4qrbD3nm5LFIf5ZI9ytFHgEAas6ujPgU0pahZUzSi08GDHjELbUxjqGQWNeYjJybHmt8H0qJb2NAiyZl/bqxQOj9v0fOO0qTET0GhNvhaRmvEoNOZRaMajsItH+TAeVTpoYiZ11tD56Ib+WrS+VgaqTzrvrW8cgm0I/M61Q1w4Ku0qBT89Bq6a+pTIuGrXxLxcVaVF0Rkp6HM0Mubo7MF3fY5GJ+RolbMmoGvJoddSjkbqV3+ZKelPzE0paqSfaHdVzaTB7KaW9jQorWafmscykOi+zsZQ6wQsVZOvRU0ZFvSpCaupicipCUMjasJqaiIZNeFh1KT00IR81Pl6HDwkaaxpGc5I0hfiHK8FmR6LYHDk3HBIa705Z5C4p0Vhw19IY6prQubKFegNfM2yKqKmLiqnLmI28iVq6qJjUlfTRUbclU3hi+XdbblTNbxbrw/T+u/n4J4AbgEA+Wq1Sn9TQeAe5n8QsJblF3y1EvlVYgFWFrEAAMVVj9kMccQgR9wmmEHmMa/4DgCU3slXK/ughACACznpVbd2FRVXl7WruLjq1a6S4qpf2odK++yDfXxvfGoKrX9ZXGX1q8OrTzwMicUgE4wwj1GO/jYOoZgTtkodwjijFDHKICON6nvS6ntS472DdFwzE9W/HGAm54jZnDDaMHQpNXTZ8D0qrjbNXzbMNwowyvzUNEI5Y5QJ3vTlo4USba2OjGAKHEAISPpl8VuIS//b/0CwMvrx9nWFuNRZi2uJb5G/xAQI5jFgYw4BYhzDFeGAAh/bWfh6lr0kq+USQGQTyBi2CPSo7UN3hesNUTTGaroaEmkNYa3dQFHLo3armwH3TnHLCO9yVaVIWdFqEWSVGiqRlN97LGh8xwhcq4931MgqsEnuLa76Oh7Bro/JyvU5RksKVwL40IWYghUk9gpBF3grSFwbW8izPSb4EjHkEQE8y1+uoDWWcRP4c08biviAKZGIyc3Yx7tUYXGVGlw1CYwmKCQtMkUs+zWFro7vTxYYS1Ezg0mNYwZeblRP3/enQwOsVa/BDY/aIh7qxoggUpP9sdFwskr7TMNkLjXOkl61pVfd6cFutYfWN4MRbmtgBOlcVbZpme8vG4OVTGPl6iEXE5XE+JDA+m3DiaogC4PSjKp4VPq0OoZpMxBIr8Ljq4g0DYQNQb5UUPNqozIt9Z8Co22jANjWLZtdlWVm9SLLb5bnyiGNwfiqORioJeT7wQBQDQYa0M0y+z2g2kYBKjPRsoIXvw8yZNiago7FiTPco9RC3k/Lr8qTcq2rrQh4Wh2k62lwgjz3elpQRkKjgzye+Wm7OqylB2KvNTN5LBcL+5sLb1unI/wWwhuuxgrvv1fILr+VJOnD2WxevqQqW0DaCV9EscjeUlV5qXB248cgTPKlUpAv5XlbP1uh2ojNNnp47+4+zH6exeJ2I8IkPnd9P1+/XK23XyQLcft1LSfw9683rSzZqZcDwdfiOS0Rx8FyLZzY2+7y9cXKYlztyPv9m7Hbvi/u+pAt8cGjSpaLduWGqOpGqR/KSv9c/nM2qyzy/Rz4cXXRz8grA3ceg1av1nf/6nhVZcO8xwMOuaMR0nd0ZUfaU/K5elch1HY/OrX7q7ui85bAsL0lqhtwHtv9LdulkLbP8Sl8XtvamDuadHBLbbvAU/G0bHsH1nY1OaWr811bua9ph68PD3c9tqOVj+ERbS/TU3i58pRu0VUyevqEwKLVvqsNyY6zcYPQcVLV9f+/RO5uJ6K4+fm88jKfspbWI6c9sK11e1XyViROsNmtRWp69tYgR4Rp9Zwo35JW9ND0EdqX1rbVt6V8varupN2fL1bu3TpxnLSi+aP7QZiLTqtZFXYwfuOG7q14zKe6Jn8kYfKNxZNv/5voCeUPX7/+XwAAAP//QZpCgmDPAAA=" +} \ No newline at end of file diff --git a/crates/blockifier_regression_test/resources/raw_rpc_json_objects/transactions.json b/crates/blockifier_regression_test/resources/raw_rpc_json_objects/transactions.json new file mode 100644 index 00000000000..fbdfc3e4aaa --- /dev/null +++ b/crates/blockifier_regression_test/resources/raw_rpc_json_objects/transactions.json @@ -0,0 +1,73 @@ +{ + "invoke_v1": { + "calldata": [ + "0x1", + "0x422d33a3638dcc4c62e72e1d6942cd31eb643ef596ccac2351e0e21f6cd4bf4", + "0xcaffbd1bd76bd7f24a3fa1d69d1b2588a86d1f9d2359b13f6a84b7e1cbd126", + "0x5", + "0x5265736f6c766552616e646f6d4576656e74", + "0x3", + "0x0", + "0x1", + "0x10c3" + ], + "max_fee": "0x65f2d0057b2", + "nonce": "0x4d8", + "sender_address": "0x4e6989d518d5eb7e516dc5673f855fe7c5cfece1880a1047d6a8e8465be0d69", + "signature": [ + "0x59b7462ea9566750ab7d7d08acbb6d339bfda921b053f1f8a5fd6b96c3656f9", + "0x2f11c36d10a03f21be918a5feeeb9922c11a2713fecd51f5e6bac5167856ecb" + ], + "transaction_hash": "0x47165a9a9c97e8829a4778f2a4b6fae4366aefc35b51d484bf04c458168351b", + "type": "INVOKE", + "version": "0x1" + }, + "invoke_v3": { + "account_deployment_data": [], + "calldata": [ + "0x2", + "0x4878d1148318a31829523ee9c6a5ee563af6cd87f90a30809e5b0d27db8a9b", + "0xa72371689866be053cc37a071de4216af73c9ffff96319b2576f7bf1e15290", + "0x4", + "0x2cb8f1439c83d9856179cbeae626e676bbb9b68e0bdda5423aa1988018acebe", + "0xba43b7400", + "0x2750a0009", + "0x422d33a3638dcc4c62e72e1d6942cd31eb643ef596ccac2351e0e21f6cd4bf4", + "0x422d33a3638dcc4c62e72e1d6942cd31eb643ef596ccac2351e0e21f6cd4bf4", + "0xcaffbd1bd76bd7f24a3fa1d69d1b2588a86d1f9d2359b13f6a84b7e1cbd126", + "0x6", + "0x41636365707444656c6976657279", + "0x4", + "0x9", + "0x2750a", + "0x1", + "0xcf" + ], + "fee_data_availability_mode": "L1", + "nonce": "0x1412", + "nonce_data_availability_mode": "L1", + "paymaster_data": [], + "resource_bounds": { + "l1_gas": { + "max_amount": "0x368", + "max_price_per_unit": "0x13bca990eeea" + }, + "l2_gas": { + "max_amount": "0x0", + "max_price_per_unit": "0x0" + } + }, + "sender_address": "0x614872dd2f3324f3e9a047d0cfaab9e9573bbdfa6081877f49f7108116b8ae0", + "signature": [ + "0x1", + "0x0", + "0x61050eb8efaa6db97248c8c987c1a9df82527c26c204681396108cbd5babaf8", + "0x358bc995a221c3aa96cf2a1e6b5b8959b6d626b6fe89381d627f378dc39402a", + "0xf247079c5e0c5be56f4c0f37452304dc63b4b883b89d5a66e3202a855515ef" + ], + "tip": "0x0", + "transaction_hash": "0xa7c7db686c7f756ceb7ca85a759caef879d425d156da83d6a836f86851983", + "type": "INVOKE", + "version": "0x3" + } +} \ No newline at end of file diff --git a/crates/blockifier_regression_test/src/lib.rs b/crates/blockifier_regression_test/src/lib.rs index 8a2ceaf9a82..4816987910e 100644 --- a/crates/blockifier_regression_test/src/lib.rs +++ b/crates/blockifier_regression_test/src/lib.rs @@ -1 +1 @@ -pub mod test_state_reader; +pub mod state_reader; diff --git a/crates/blockifier_regression_test/src/state_reader.rs b/crates/blockifier_regression_test/src/state_reader.rs new file mode 100644 index 00000000000..9ce3f8a8ac7 --- /dev/null +++ b/crates/blockifier_regression_test/src/state_reader.rs @@ -0,0 +1,8 @@ +pub mod compile; +#[cfg(test)] +pub mod raw_rpc_json_test; +#[cfg(test)] +#[cfg(feature = "blockifier_regression_https_testing")] +pub mod rpc_https_test; +pub mod test_state_reader; +pub mod utils; diff --git a/crates/blockifier_regression_test/src/state_reader/compile.rs b/crates/blockifier_regression_test/src/state_reader/compile.rs new file mode 100644 index 00000000000..b3203c5659b --- /dev/null +++ b/crates/blockifier_regression_test/src/state_reader/compile.rs @@ -0,0 +1,105 @@ +// This module contains code taken from starknet-replay. +// For more information, see the original repository at: +// `` + +use std::collections::HashMap; +use std::io::{self, Read}; +use std::sync::Arc; + +use blockifier::execution::contract_class::{ContractClass, ContractClassV0, ContractClassV0Inner}; +use blockifier::state::state_api::StateResult; +use cairo_lang_starknet_classes::contract_class::ContractEntryPoints; +use cairo_lang_utils::bigint::BigUintAsHex; +use cairo_vm::types::program::Program; +use flate2::bufread; +use serde::Deserialize; +use starknet_api::core::EntryPointSelector; +use starknet_api::deprecated_contract_class::{EntryPoint, EntryPointOffset, EntryPointType}; +use starknet_api::hash::StarkHash; +use starknet_core::types::{ + CompressedLegacyContractClass, + FlattenedSierraClass, + LegacyContractEntryPoint, + LegacyEntryPointsByType, +}; +use starknet_gateway::errors::serde_err_to_state_err; + +#[derive(Debug, Deserialize)] +pub struct MiddleSierraContractClass { + pub sierra_program: Vec, + pub contract_class_version: String, + pub entry_points_by_type: ContractEntryPoints, +} + +/// Maps `LegacyEntryPointsByType` to a `HashMap` where each `EntryPointType` +/// is associated with a vector of `EntryPoint`. Converts selectors and offsets +/// from legacy format to new `EntryPoint` struct. +pub fn map_entry_points_by_type_legacy( + entry_points_by_type: LegacyEntryPointsByType, +) -> HashMap> { + let entry_types_to_points = HashMap::from([ + (EntryPointType::Constructor, entry_points_by_type.constructor), + (EntryPointType::External, entry_points_by_type.external), + (EntryPointType::L1Handler, entry_points_by_type.l1_handler), + ]); + + let to_contract_entry_point = |entrypoint: &LegacyContractEntryPoint| -> EntryPoint { + let felt: StarkHash = StarkHash::from_bytes_be(&entrypoint.selector.to_bytes_be()); + EntryPoint { + offset: EntryPointOffset(usize::try_from(entrypoint.offset).unwrap()), + selector: EntryPointSelector(felt), + } + }; + + let mut entry_points_by_type_map = HashMap::new(); + for (entry_point_type, entry_points) in entry_types_to_points.into_iter() { + let values = entry_points.iter().map(to_contract_entry_point).collect::>(); + entry_points_by_type_map.insert(entry_point_type, values); + } + + entry_points_by_type_map +} + +/// Uncompresses a Gz Encoded vector of bytes and returns a string or error +/// Here &[u8] implements BufRead +pub fn decode_reader(bytes: Vec) -> io::Result { + let mut gz = bufread::GzDecoder::new(&bytes[..]); + let mut s = String::new(); + gz.read_to_string(&mut s)?; + Ok(s) +} + +/// Compile a FlattenedSierraClass to a ContractClass V1 (casm) using cairo_lang_starknet_classes. +pub fn sierra_to_contact_class_v1(sierra: FlattenedSierraClass) -> StateResult { + let middle_sierra: MiddleSierraContractClass = { + let v = serde_json::to_value(sierra).map_err(serde_err_to_state_err); + serde_json::from_value(v?).map_err(serde_err_to_state_err)? + }; + let sierra = cairo_lang_starknet_classes::contract_class::ContractClass { + sierra_program: middle_sierra.sierra_program, + contract_class_version: middle_sierra.contract_class_version, + entry_points_by_type: middle_sierra.entry_points_by_type, + sierra_program_debug_info: None, + abi: None, + }; + + let casm = + cairo_lang_starknet_classes::casm_contract_class::CasmContractClass::from_contract_class( + sierra, + false, + usize::MAX, + ) + .unwrap(); + Ok(ContractClass::V1(casm.try_into().unwrap())) +} + +/// Compile a CompressedLegacyContractClass to a ContractClass V0 using cairo_lang_starknet_classes. +pub fn legacy_to_contract_class_v0( + legacy: CompressedLegacyContractClass, +) -> StateResult { + let as_str = decode_reader(legacy.program).unwrap(); + let program = Program::from_bytes(as_str.as_bytes(), None).unwrap(); + let entry_points_by_type = map_entry_points_by_type_legacy(legacy.entry_points_by_type); + let inner = Arc::new(ContractClassV0Inner { program, entry_points_by_type }); + Ok(ContractClass::V0(ContractClassV0(inner))) +} diff --git a/crates/blockifier_regression_test/src/state_reader/raw_rpc_json_test.rs b/crates/blockifier_regression_test/src/state_reader/raw_rpc_json_test.rs new file mode 100644 index 00000000000..96b6dcfce31 --- /dev/null +++ b/crates/blockifier_regression_test/src/state_reader/raw_rpc_json_test.rs @@ -0,0 +1,67 @@ +use assert_matches::assert_matches; +use blockifier::blockifier::block::BlockInfo; +use pretty_assertions::assert_eq; +use rstest::{fixture, rstest}; +use starknet_api::block::BlockNumber; +use starknet_api::test_utils::read_json_file; +use starknet_api::transaction::{InvokeTransaction, Transaction}; +use starknet_core::types::ContractClass; +use starknet_gateway::rpc_objects::BlockHeader; + +use crate::state_reader::compile::legacy_to_contract_class_v0; +use crate::state_reader::utils::deserialize_transaction_json_to_starknet_api_tx; + +#[fixture] +fn block_header() -> BlockHeader { + serde_json::from_value(read_json_file("raw_rpc_json_objects/block_header.json")) + .expect("Failed to deserialize block header") +} + +#[fixture] +fn deprecated_contract_class() -> ContractClass { + serde_json::from_value(read_json_file("raw_rpc_json_objects/deprecated_contract_class.json")) + .expect("Failed to deserialize deprecated contact class") +} + +/// Test that deserialize block header from JSON file works(in the fixture). +#[rstest] +fn test_deserialize_block_header(block_header: BlockHeader) { + assert_eq!(block_header.block_number, BlockNumber(700000)); +} + +/// Test that converting a block header to block info works. +#[rstest] +fn test_block_header_to_block_info(block_header: BlockHeader) { + let block_info: BlockInfo = + block_header.try_into().expect("Failed to convert BlockHeader to block info"); + // Sanity check. + assert_eq!(block_info.block_number, BlockNumber(700000)); +} + +#[rstest] +fn test_compile_deprecated_contract_class(deprecated_contract_class: ContractClass) { + match deprecated_contract_class { + ContractClass::Legacy(legacy) => { + // Compile the contract class. + assert!(legacy_to_contract_class_v0(legacy).is_ok()); + } + _ => panic!("Expected a legacy contract class"), + } +} + +#[test] +fn deserialize_invoke_txs() { + let invoke_tx_v1 = deserialize_transaction_json_to_starknet_api_tx( + read_json_file("raw_rpc_json_objects/transactions.json")["invoke_v1"].clone(), + ) + .expect("Failed to deserialize invoke v1 tx"); + + assert_matches!(invoke_tx_v1, Transaction::Invoke(InvokeTransaction::V1(..))); + + let invoke_tx_v3 = deserialize_transaction_json_to_starknet_api_tx( + read_json_file("raw_rpc_json_objects/transactions.json")["invoke_v3"].clone(), + ) + .expect("Failed to deserialize invoke v3 tx"); + + assert_matches!(invoke_tx_v3, Transaction::Invoke(InvokeTransaction::V3(..))); +} diff --git a/crates/blockifier_regression_test/src/state_reader/rpc_https_test.rs b/crates/blockifier_regression_test/src/state_reader/rpc_https_test.rs new file mode 100644 index 00000000000..b0144724d7f --- /dev/null +++ b/crates/blockifier_regression_test/src/state_reader/rpc_https_test.rs @@ -0,0 +1,90 @@ +use assert_matches::assert_matches; +use blockifier::blockifier::block::BlockInfo; +use blockifier::versioned_constants::StarknetVersion; +use pretty_assertions::assert_eq; +use rstest::{fixture, rstest}; +use starknet_api::block::BlockNumber; +use starknet_api::core::ClassHash; +use starknet_api::test_utils::read_json_file; +use starknet_api::transaction::Transaction; +use starknet_api::{class_hash, felt}; +use starknet_core::types::ContractClass::{Legacy, Sierra}; + +use crate::state_reader::compile::legacy_to_contract_class_v0; +use crate::state_reader::test_state_reader::TestStateReader; + +const EXAMPLE_INVOKE_TX_HASH: &str = + "0xa7c7db686c7f756ceb7ca85a759caef879d425d156da83d6a836f86851983"; + +const EXAMPLE_BLOCK_NUMBER: u64 = 700000; + +const EXAMPLE_CONTACT_CLASS_HASH: &str = + "0x3131fa018d520a037686ce3efddeab8f28895662f019ca3ca18a626650f7d1e"; + +#[fixture] +pub fn test_block_number() -> BlockNumber { + BlockNumber(EXAMPLE_BLOCK_NUMBER) +} + +#[fixture] +pub fn test_state_reader(test_block_number: BlockNumber) -> TestStateReader { + TestStateReader::new_for_testing(test_block_number) +} + +#[rstest] +pub fn test_get_block_info(test_state_reader: TestStateReader, test_block_number: BlockNumber) { + assert_matches!( + test_state_reader.get_block_info(), + Ok(BlockInfo { block_number, .. }) if block_number == test_block_number + ); +} + +#[rstest] +pub fn test_get_starknet_version(test_state_reader: TestStateReader) { + assert_eq!(test_state_reader.get_starknet_version().unwrap(), StarknetVersion::V0_13_2_1) +} + +#[rstest] +pub fn test_get_contract_class(test_state_reader: TestStateReader, test_block_number: BlockNumber) { + // An example of existing class hash in Mainnet. + let class_hash = class_hash!(EXAMPLE_CONTACT_CLASS_HASH); + + // Test getting the contract class using RPC request. + let deprecated_contract_class = + test_state_reader.get_contract_class(&class_hash).unwrap_or_else(|err| { + panic!( + "Error retrieving deprecated contract class for class hash {}: {} + This class hash exist in Mainnet Block Number: {}", + class_hash, test_block_number, err + ); + }); + + // Test compiling the contract class. + match deprecated_contract_class { + Legacy(legacy) => { + // Test compiling the contract class. + assert!(legacy_to_contract_class_v0(legacy).is_ok()); + } + // This contract class is deprecated. + Sierra(_) => panic!("Expected a legacy contract class"), + } +} + +#[rstest] +pub fn test_get_tx_hashes(test_state_reader: TestStateReader, test_block_number: BlockNumber) { + let block_number_int = test_block_number.0; + let expected_tx_hashes: Vec = serde_json::from_value(read_json_file( + format!("block_{block_number_int}/tx_hashes_block_{block_number_int}.json").as_str(), + )) + .unwrap_or_else(|err| panic!("Failed to deserialize txs hash to Vector of String {}", err)); + let actual_tx_hashes = test_state_reader.get_tx_hashes().unwrap_or_else(|err| { + panic!("Error retrieving txs hash: {}", err); + }); + assert_eq!(actual_tx_hashes, expected_tx_hashes); +} + +#[rstest] +pub fn test_get_tx_by_hash(test_state_reader: TestStateReader) { + let actual_tx = test_state_reader.get_tx_by_hash(EXAMPLE_INVOKE_TX_HASH).unwrap(); + assert_matches!(actual_tx, Transaction::Invoke(..)); +} diff --git a/crates/blockifier_regression_test/src/state_reader/test_state_reader.rs b/crates/blockifier_regression_test/src/state_reader/test_state_reader.rs new file mode 100644 index 00000000000..2bb67ac64e4 --- /dev/null +++ b/crates/blockifier_regression_test/src/state_reader/test_state_reader.rs @@ -0,0 +1,181 @@ +use blockifier::blockifier::block::BlockInfo; +use blockifier::blockifier::config::TransactionExecutorConfig; +use blockifier::blockifier::transaction_executor::TransactionExecutor; +use blockifier::bouncer::BouncerConfig; +use blockifier::context::BlockContext; +use blockifier::execution::contract_class::ContractClass as BlockifierContractClass; +use blockifier::state::cached_state::CachedState; +use blockifier::state::errors::StateError; +use blockifier::state::state_api::{StateReader, StateResult}; +use blockifier::versioned_constants::{StarknetVersion, VersionedConstants}; +use serde_json::{json, to_value}; +use starknet_api::block::{BlockNumber, GasPrice}; +use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce}; +use starknet_api::state::StorageKey; +use starknet_api::transaction::Transaction; +use starknet_core::types::ContractClass as StarknetContractClass; +use starknet_core::types::ContractClass::{Legacy, Sierra}; +use starknet_gateway::config::RpcStateReaderConfig; +use starknet_gateway::errors::serde_err_to_state_err; +use starknet_gateway::rpc_objects::{BlockHeader, GetBlockWithTxHashesParams, ResourcePrice}; +use starknet_gateway::rpc_state_reader::RpcStateReader; +use starknet_types_core::felt::Felt; + +use crate::state_reader::compile::{legacy_to_contract_class_v0, sierra_to_contact_class_v1}; +use crate::state_reader::utils::{ + deserialize_transaction_json_to_starknet_api_tx, + get_chain_info, + get_rpc_state_reader_config, +}; + +pub struct TestStateReader(RpcStateReader); + +impl StateReader for TestStateReader { + fn get_nonce_at(&self, contract_address: ContractAddress) -> StateResult { + self.0.get_nonce_at(contract_address) + } + + fn get_storage_at( + &self, + contract_address: ContractAddress, + key: StorageKey, + ) -> StateResult { + self.0.get_storage_at(contract_address, key) + } + + fn get_class_hash_at(&self, contract_address: ContractAddress) -> StateResult { + self.0.get_class_hash_at(contract_address) + } + + /// Returns the contract class of the given class hash. + /// Compile the contract class if it is Sierra. + fn get_compiled_contract_class( + &self, + class_hash: ClassHash, + ) -> StateResult { + match self.get_contract_class(&class_hash)? { + Sierra(sierra) => sierra_to_contact_class_v1(sierra), + Legacy(legacy) => legacy_to_contract_class_v0(legacy), + } + } + + fn get_compiled_class_hash(&self, class_hash: ClassHash) -> StateResult { + self.0.get_compiled_class_hash(class_hash) + } +} + +impl TestStateReader { + pub fn new(config: &RpcStateReaderConfig, block_number: BlockNumber) -> Self { + Self(RpcStateReader::from_number(config, block_number)) + } + + pub fn new_for_testing(block_number: BlockNumber) -> Self { + TestStateReader::new(&get_rpc_state_reader_config(), block_number) + } + + /// Get the block info of the current block. + /// If l2_gas_price is not present in the block header, it will be set to 1. + pub fn get_block_info(&self) -> StateResult { + let get_block_params = GetBlockWithTxHashesParams { block_id: self.0.block_id }; + let default_l2_price = + ResourcePrice { price_in_wei: GasPrice(1), price_in_fri: GasPrice(1) }; + + let mut json = + self.0.send_rpc_request("starknet_getBlockWithTxHashes", get_block_params)?; + + let block_header_map = json.as_object_mut().ok_or(StateError::StateReadError( + "starknet_getBlockWithTxHashes should return JSON value of type Object".to_string(), + ))?; + + if block_header_map.get("l2_gas_price").is_none() { + // In old blocks, the l2_gas_price field is not present. + block_header_map.insert( + "l2_gas_price".to_string(), + to_value(default_l2_price).map_err(serde_err_to_state_err)?, + ); + } + + Ok(serde_json::from_value::(json) + .map_err(serde_err_to_state_err)? + .try_into()?) + } + + pub fn get_starknet_version(&self) -> StateResult { + let get_block_params = GetBlockWithTxHashesParams { block_id: self.0.block_id }; + let raw_version: String = serde_json::from_value( + self.0.send_rpc_request("starknet_getBlockWithTxHashes", get_block_params)? + ["starknet_version"] + .clone(), + ) + .map_err(serde_err_to_state_err)?; + StarknetVersion::try_from(raw_version.as_str()).map_err(|err| { + StateError::StateReadError(format!("Failed to match starknet version: {}", err)) + }) + } + + /// Get all transaction hashes in the current block. + pub fn get_tx_hashes(&self) -> StateResult> { + let get_block_params = GetBlockWithTxHashesParams { block_id: self.0.block_id }; + let raw_tx_hashes = serde_json::from_value( + self.0.send_rpc_request("starknet_getBlockWithTxHashes", &get_block_params)? + ["transactions"] + .clone(), + ) + .map_err(serde_err_to_state_err)?; + serde_json::from_value(raw_tx_hashes).map_err(serde_err_to_state_err) + } + + pub fn get_tx_by_hash(&self, tx_hash: &str) -> StateResult { + let method = "starknet_getTransactionByHash"; + let params = json!({ + "transaction_hash": tx_hash, + }); + deserialize_transaction_json_to_starknet_api_tx(self.0.send_rpc_request(method, params)?) + .map_err(serde_err_to_state_err) + } + + pub fn get_contract_class(&self, class_hash: &ClassHash) -> StateResult { + let params = json!({ + "block_id": self.0.block_id, + "class_hash": class_hash.0.to_string(), + }); + let contract_class: StarknetContractClass = + serde_json::from_value(self.0.send_rpc_request("starknet_getClass", params.clone())?) + .map_err(serde_err_to_state_err)?; + Ok(contract_class) + } + + pub fn get_all_txs_in_block(&self) -> StateResult> { + // TODO(Aviv): Use batch request to get all txs in a block. + let txs: Vec<_> = self + .get_tx_hashes()? + .iter() + .map(|tx_hash| self.get_tx_by_hash(tx_hash)) + .collect::>()?; + Ok(txs) + } + + pub fn get_versioned_constants(&self) -> StateResult<&'static VersionedConstants> { + Ok(self.get_starknet_version()?.into()) + } + + pub fn get_block_context(&self) -> StateResult { + Ok(BlockContext::new( + self.get_block_info()?, + get_chain_info(), + self.get_versioned_constants()?.clone(), + BouncerConfig::max(), + )) + } + + pub fn get_transaction_executor( + test_state_reader: TestStateReader, + ) -> StateResult> { + let block_context = test_state_reader.get_block_context()?; + Ok(TransactionExecutor::::new( + CachedState::new(test_state_reader), + block_context, + TransactionExecutorConfig::default(), + )) + } +} diff --git a/crates/blockifier_regression_test/src/state_reader/utils.rs b/crates/blockifier_regression_test/src/state_reader/utils.rs new file mode 100644 index 00000000000..5f94c0b05a8 --- /dev/null +++ b/crates/blockifier_regression_test/src/state_reader/utils.rs @@ -0,0 +1,94 @@ +use blockifier::context::{ChainInfo, FeeTokenAddresses}; +use papyrus_execution::{ETH_FEE_CONTRACT_ADDRESS, STRK_FEE_CONTRACT_ADDRESS}; +use serde_json::Value; +use starknet_api::core::{ChainId, ContractAddress, PatriciaKey}; +use starknet_api::transaction::{ + DeclareTransaction, + DeployAccountTransaction, + InvokeTransaction, + Transaction, +}; +use starknet_api::{contract_address, felt, patricia_key}; +use starknet_gateway::config::RpcStateReaderConfig; + +pub const RPC_NODE_URL: &str = "https://free-rpc.nethermind.io/mainnet-juno/"; +pub const JSON_RPC_VERSION: &str = "2.0"; + +/// Returns the fee token addresses of mainnet. +pub fn get_fee_token_addresses() -> FeeTokenAddresses { + FeeTokenAddresses { + strk_fee_token_address: contract_address!(STRK_FEE_CONTRACT_ADDRESS), + eth_fee_token_address: contract_address!(ETH_FEE_CONTRACT_ADDRESS), + } +} + +/// Returns the RPC state reader configuration with the constants RPC_NODE_URL and JSON_RPC_VERSION. +pub fn get_rpc_state_reader_config() -> RpcStateReaderConfig { + RpcStateReaderConfig { + url: RPC_NODE_URL.to_string(), + json_rpc_version: JSON_RPC_VERSION.to_string(), + } +} + +/// Returns the chain info of mainnet. +pub fn get_chain_info() -> ChainInfo { + ChainInfo { chain_id: ChainId::Mainnet, fee_token_addresses: get_fee_token_addresses() } +} + +pub fn deserialize_transaction_json_to_starknet_api_tx( + mut raw_transaction: Value, +) -> serde_json::Result { + let tx_type: String = serde_json::from_value(raw_transaction["type"].clone())?; + let tx_version: String = serde_json::from_value(raw_transaction["version"].clone())?; + + match (tx_type.as_str(), tx_version.as_str()) { + ("INVOKE", "0x0") => { + Ok(Transaction::Invoke(InvokeTransaction::V0(serde_json::from_value(raw_transaction)?))) + } + ("INVOKE", "0x1") => { + Ok(Transaction::Invoke(InvokeTransaction::V1(serde_json::from_value(raw_transaction)?))) + } + ("INVOKE", "0x3") => { + let resource_bounds = raw_transaction + .get_mut("resource_bounds") + .expect("Invoke v3 tx should contain resource_bounds field") + .as_object_mut() + .expect("resource_bounds should be an object"); + + // In old invoke v3 transaction, the resource bounds names are lowercase. + // need to convert to uppercase for deserialization to work. + if let Some(l1_gas_value) = resource_bounds.remove("l1_gas") { + resource_bounds.insert("L1_GAS".to_string(), l1_gas_value); + + let l2_gas_value = resource_bounds + .remove("l2_gas") + .expect("If invoke v3 tx contains l1_gas, it should contain l2_gas"); + resource_bounds.insert("L2_GAS".to_string(), l2_gas_value); + } + + Ok(Transaction::Invoke(InvokeTransaction::V3(serde_json::from_value(raw_transaction)?))) + } + ("DEPLOY_ACCOUNT", "0x1") => Ok(Transaction::DeployAccount(DeployAccountTransaction::V1( + serde_json::from_value(raw_transaction)?, + ))), + ("DEPLOY_ACCOUNT", "0x3") => Ok(Transaction::DeployAccount(DeployAccountTransaction::V3( + serde_json::from_value(raw_transaction)?, + ))), + ("DECLARE", "0x0") => Ok(Transaction::Declare(DeclareTransaction::V0( + serde_json::from_value(raw_transaction)?, + ))), + ("DECLARE", "0x1") => Ok(Transaction::Declare(DeclareTransaction::V1( + serde_json::from_value(raw_transaction)?, + ))), + ("DECLARE", "0x2") => Ok(Transaction::Declare(DeclareTransaction::V2( + serde_json::from_value(raw_transaction)?, + ))), + ("DECLARE", "0x3") => Ok(Transaction::Declare(DeclareTransaction::V3( + serde_json::from_value(raw_transaction)?, + ))), + ("L1_HANDLER", _) => Ok(Transaction::L1Handler(serde_json::from_value(raw_transaction)?)), + (tx_type, tx_version) => Err(serde::de::Error::custom(format!( + "unimplemented transaction type: {tx_type} version: {tx_version}" + ))), + } +} diff --git a/crates/blockifier_regression_test/src/test_state_reader.rs b/crates/blockifier_regression_test/src/test_state_reader.rs deleted file mode 100644 index c836fa399a1..00000000000 --- a/crates/blockifier_regression_test/src/test_state_reader.rs +++ /dev/null @@ -1,48 +0,0 @@ -use blockifier::blockifier::block::BlockInfo; -use blockifier::execution::contract_class::ContractClass; -use blockifier::state::state_api::{StateReader, StateResult}; -use starknet_api::block::BlockNumber; -use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce}; -use starknet_api::state::StorageKey; -use starknet_gateway::config::RpcStateReaderConfig; -use starknet_gateway::rpc_state_reader::RpcStateReader; -use starknet_gateway::state_reader::MempoolStateReader; -use starknet_types_core::felt::Felt; - -pub struct TestStateReader(RpcStateReader); - -impl StateReader for TestStateReader { - fn get_nonce_at(&self, contract_address: ContractAddress) -> StateResult { - self.0.get_nonce_at(contract_address) - } - - fn get_storage_at( - &self, - contract_address: ContractAddress, - key: StorageKey, - ) -> StateResult { - self.0.get_storage_at(contract_address, key) - } - - fn get_class_hash_at(&self, contract_address: ContractAddress) -> StateResult { - self.0.get_class_hash_at(contract_address) - } - - fn get_compiled_contract_class(&self, _class_hash: ClassHash) -> StateResult { - todo!() - } - - fn get_compiled_class_hash(&self, class_hash: ClassHash) -> StateResult { - self.0.get_compiled_class_hash(class_hash) - } -} - -impl TestStateReader { - pub fn new(config: &RpcStateReaderConfig, block_number: BlockNumber) -> Self { - Self(RpcStateReader::from_number(config, block_number)) - } - - pub fn get_block_info(&self) -> StateResult { - self.0.get_block_info() - } -} diff --git a/crates/committer_cli/src/tests/utils/objects.rs b/crates/committer_cli/src/tests/utils/objects.rs index b15e41471c9..db2b8d31409 100644 --- a/crates/committer_cli/src/tests/utils/objects.rs +++ b/crates/committer_cli/src/tests/utils/objects.rs @@ -86,10 +86,10 @@ pub(crate) fn get_thin_state_diff() -> ThinStateDiff { pub(crate) fn get_tx_data(execution_status: &TransactionExecutionStatus) -> TransactionHashingData { TransactionHashingData { - transaction_signature: Some(TransactionSignature(vec![ + transaction_signature: TransactionSignature(vec![ Felt::from_bytes_be_slice(&[1_u8]), Felt::from_bytes_be_slice(&[2_u8]), - ])), + ]), transaction_output: get_transaction_output_for_hash(execution_status), transaction_hash: TransactionHash(Felt::from_bytes_be_slice(&[3_u8])), } diff --git a/crates/consensus_manager/Cargo.toml b/crates/consensus_manager/Cargo.toml index 4793d62d039..88321d4ab86 100644 --- a/crates/consensus_manager/Cargo.toml +++ b/crates/consensus_manager/Cargo.toml @@ -16,6 +16,4 @@ serde.workspace = true starknet_batcher_types.workspace = true starknet_consensus_manager_types.workspace = true starknet_mempool_infra.workspace = true -tokio.workspace = true -tracing.workspace = true validator.workspace = true diff --git a/crates/consensus_manager/src/communication.rs b/crates/consensus_manager/src/communication.rs index 79e31ec9b65..1fe90e064b6 100644 --- a/crates/consensus_manager/src/communication.rs +++ b/crates/consensus_manager/src/communication.rs @@ -1,23 +1,19 @@ use async_trait::async_trait; use starknet_consensus_manager_types::communication::{ ConsensusManagerRequest, - ConsensusManagerRequestAndResponseSender, ConsensusManagerResponse, }; use starknet_mempool_infra::component_definitions::ComponentRequestHandler; -use starknet_mempool_infra::component_server::LocalActiveComponentServer; -use tokio::sync::mpsc::Receiver; +use starknet_mempool_infra::component_server::WrapperServer; use crate::consensus_manager::ConsensusManager; -pub type LocalConsensusManagerServer = - LocalActiveComponentServer; +pub type ConsensusManagerServer = WrapperServer; -pub fn create_local_consensus_manager_server( +pub fn create_consensus_manager_server( consensus_manager: ConsensusManager, - rx_consensus_manager: Receiver, -) -> LocalConsensusManagerServer { - LocalActiveComponentServer::new(consensus_manager, rx_consensus_manager) +) -> ConsensusManagerServer { + WrapperServer::new(consensus_manager) } #[async_trait] diff --git a/crates/consensus_manager/src/consensus_manager.rs b/crates/consensus_manager/src/consensus_manager.rs index ccdc1f9a2dd..46162abdd10 100644 --- a/crates/consensus_manager/src/consensus_manager.rs +++ b/crates/consensus_manager/src/consensus_manager.rs @@ -1,8 +1,5 @@ -use async_trait::async_trait; use starknet_batcher_types::communication::SharedBatcherClient; -use starknet_mempool_infra::component_runner::ComponentStarter; -use starknet_mempool_infra::errors::ComponentError; -use tracing::info; +use starknet_mempool_infra::component_definitions::ComponentStarter; use crate::config::ConsensusManagerConfig; @@ -27,10 +24,4 @@ pub fn create_consensus_manager( ConsensusManager::new(config, batcher_client) } -#[async_trait] -impl ComponentStarter for ConsensusManager { - async fn start(&mut self) -> Result<(), ComponentError> { - info!("ConsensusManager::start()"); - Ok(()) - } -} +impl ComponentStarter for ConsensusManager {} diff --git a/crates/gateway/src/communication.rs b/crates/gateway/src/communication.rs index ee691b42397..f169fc93504 100644 --- a/crates/gateway/src/communication.rs +++ b/crates/gateway/src/communication.rs @@ -12,12 +12,12 @@ use tracing::instrument; use crate::gateway::Gateway; -pub type GatewayServer = LocalComponentServer; +pub type LocalGatewayServer = LocalComponentServer; pub fn create_gateway_server( gateway: Gateway, rx_gateway: Receiver, -) -> GatewayServer { +) -> LocalGatewayServer { LocalComponentServer::new(gateway, rx_gateway) } diff --git a/crates/gateway/src/gateway.rs b/crates/gateway/src/gateway.rs index 1f045ce385b..b0c625b6d3f 100644 --- a/crates/gateway/src/gateway.rs +++ b/crates/gateway/src/gateway.rs @@ -1,17 +1,15 @@ use std::clone::Clone; use std::sync::Arc; -use async_trait::async_trait; use starknet_api::executable_transaction::Transaction; use starknet_api::rpc_transaction::RpcTransaction; use starknet_api::transaction::TransactionHash; use starknet_gateway_types::errors::GatewaySpecError; -use starknet_mempool_infra::component_runner::ComponentStarter; -use starknet_mempool_infra::errors::ComponentError; -use starknet_mempool_types::communication::{MempoolWrapperInput, SharedMempoolClient}; -use starknet_mempool_types::mempool_types::{AccountState, MempoolInput}; +use starknet_mempool_infra::component_definitions::ComponentStarter; +use starknet_mempool_types::communication::{AddTransactionArgsWrapper, SharedMempoolClient}; +use starknet_mempool_types::mempool_types::{AccountState, AddTransactionArgs}; use starknet_sierra_compile::config::SierraToCasmCompilationConfig; -use tracing::{error, info, instrument}; +use tracing::{error, instrument}; use crate::compilation::GatewayCompiler; use crate::config::{GatewayConfig, RpcStateReaderConfig}; @@ -76,7 +74,7 @@ async fn internal_add_tx( app_state: AppState, tx: RpcTransaction, ) -> GatewayResult { - let mempool_input = tokio::task::spawn_blocking(move || { + let add_tx_args = tokio::task::spawn_blocking(move || { process_tx( app_state.stateless_tx_validator, app_state.stateful_tx_validator.as_ref(), @@ -91,10 +89,10 @@ async fn internal_add_tx( GatewaySpecError::UnexpectedError { data: "Internal server error".to_owned() } })??; - let tx_hash = mempool_input.tx.tx_hash(); + let tx_hash = add_tx_args.tx.tx_hash(); - let mempool_wrapper_input = MempoolWrapperInput { mempool_input, message_metadata: None }; - app_state.mempool_client.add_tx(mempool_wrapper_input).await.map_err(|e| { + let add_tx_args = AddTransactionArgsWrapper { args: add_tx_args, p2p_message_metadata: None }; + app_state.mempool_client.add_tx(add_tx_args).await.map_err(|e| { error!("Failed to send tx to mempool: {}", e); GatewaySpecError::UnexpectedError { data: "Internal server error".to_owned() } })?; @@ -108,7 +106,7 @@ fn process_tx( state_reader_factory: &dyn StateReaderFactory, gateway_compiler: GatewayCompiler, tx: RpcTransaction, -) -> GatewayResult { +) -> GatewayResult { // TODO(Arni, 1/5/2024): Perform congestion control. // Perform stateless validations. @@ -128,16 +126,16 @@ fn process_tx( } let mut validator = stateful_tx_validator.instantiate_validator(state_reader_factory)?; - let sender_address = executable_tx.contract_address(); - let nonce = validator.get_nonce(sender_address).map_err(|e| { - error!("Failed to get nonce for sender address {}: {}", sender_address, e); + let address = executable_tx.contract_address(); + let nonce = validator.get_nonce(address).map_err(|e| { + error!("Failed to get nonce for sender address {}: {}", address, e); GatewaySpecError::UnexpectedError { data: "Internal server error.".to_owned() } })?; stateful_tx_validator.run_validate(&executable_tx, nonce, validator)?; // TODO(Arni): Add the Sierra and the Casm to the mempool input. - Ok(MempoolInput { tx: executable_tx, account_state: AccountState { sender_address, nonce } }) + Ok(AddTransactionArgs { tx: executable_tx, account_state: AccountState { address, nonce } }) } pub fn create_gateway( @@ -152,10 +150,4 @@ pub fn create_gateway( Gateway::new(config, state_reader_factory, gateway_compiler, mempool_client) } -#[async_trait] -impl ComponentStarter for Gateway { - async fn start(&mut self) -> Result<(), ComponentError> { - info!("Gateway::start()"); - Ok(()) - } -} +impl ComponentStarter for Gateway {} diff --git a/crates/gateway/src/gateway_test.rs b/crates/gateway/src/gateway_test.rs index 28d5000bce4..955b1091149 100644 --- a/crates/gateway/src/gateway_test.rs +++ b/crates/gateway/src/gateway_test.rs @@ -8,8 +8,8 @@ use starknet_api::core::{ChainId, CompiledClassHash, ContractAddress}; use starknet_api::executable_transaction::{InvokeTransaction, Transaction}; use starknet_api::rpc_transaction::{RpcDeclareTransaction, RpcTransaction}; use starknet_gateway_types::errors::GatewaySpecError; -use starknet_mempool_types::communication::{MempoolWrapperInput, MockMempoolClient}; -use starknet_mempool_types::mempool_types::{AccountState, MempoolInput}; +use starknet_mempool_types::communication::{AddTransactionArgsWrapper, MockMempoolClient}; +use starknet_mempool_types::mempool_types::{AccountState, AddTransactionArgs}; use starknet_sierra_compile::config::SierraToCasmCompilationConfig; use crate::compilation::GatewayCompiler; @@ -54,7 +54,7 @@ fn create_tx() -> (RpcTransaction, SenderAddress) { // TODO: add test with Some broadcasted message metadata #[tokio::test] async fn test_add_tx() { - let (rpc_tx, sender_address) = create_tx(); + let (rpc_tx, address) = create_tx(); let rpc_invoke_tx = assert_matches!(rpc_tx.clone(), RpcTransaction::Invoke(rpc_invoke_tx) => rpc_invoke_tx); let executable_tx = Transaction::Invoke( @@ -64,16 +64,14 @@ async fn test_add_tx() { let tx_hash = executable_tx.tx_hash(); let mut mock_mempool_client = MockMempoolClient::new(); + let add_tx_args = AddTransactionArgs { + tx: executable_tx, + account_state: AccountState { address, nonce: *rpc_tx.nonce() }, + }; mock_mempool_client .expect_add_tx() .once() - .with(eq(MempoolWrapperInput { - mempool_input: MempoolInput { - tx: executable_tx, - account_state: AccountState { sender_address, nonce: *rpc_tx.nonce() }, - }, - message_metadata: None, - })) + .with(eq(AddTransactionArgsWrapper { args: add_tx_args, p2p_message_metadata: None })) .return_once(|_| Ok(())); let state_reader_factory = local_test_state_reader_factory(CairoVersion::Cairo1, false); let app_state = app_state(Arc::new(mock_mempool_client), state_reader_factory); diff --git a/crates/gateway/src/lib.rs b/crates/gateway/src/lib.rs index 3d805f511f6..fe5bc05321a 100644 --- a/crates/gateway/src/lib.rs +++ b/crates/gateway/src/lib.rs @@ -4,7 +4,7 @@ mod compiler_version; pub mod config; pub mod errors; pub mod gateway; -mod rpc_objects; +pub mod rpc_objects; pub mod rpc_state_reader; #[cfg(test)] mod rpc_state_reader_test; diff --git a/crates/gateway/src/rpc_objects.rs b/crates/gateway/src/rpc_objects.rs index 06156f601ad..565ca0e8b85 100644 --- a/crates/gateway/src/rpc_objects.rs +++ b/crates/gateway/src/rpc_objects.rs @@ -74,6 +74,7 @@ pub struct BlockHeader { pub timestamp: BlockTimestamp, pub l1_gas_price: ResourcePrice, pub l1_data_gas_price: ResourcePrice, + pub l2_gas_price: ResourcePrice, pub l1_da_mode: L1DataAvailabilityMode, pub starknet_version: String, } @@ -90,9 +91,8 @@ impl TryInto for BlockHeader { parse_gas_price(self.l1_gas_price.price_in_fri)?, parse_gas_price(self.l1_data_gas_price.price_in_wei)?, parse_gas_price(self.l1_data_gas_price.price_in_fri)?, - // TODO(Aner): add to BlockHeader and take the value from it. - NonZeroU128::MIN, - NonZeroU128::MIN, + parse_gas_price(self.l2_gas_price.price_in_wei)?, + parse_gas_price(self.l2_gas_price.price_in_fri)?, ), use_kzg_da: matches!(self.l1_da_mode, L1DataAvailabilityMode::Blob), }) diff --git a/crates/gateway/src/rpc_state_reader_test.rs b/crates/gateway/src/rpc_state_reader_test.rs index 570a79ac815..e63aeea7ea7 100644 --- a/crates/gateway/src/rpc_state_reader_test.rs +++ b/crates/gateway/src/rpc_state_reader_test.rs @@ -74,6 +74,10 @@ async fn test_get_block_info() { price_in_wei: GasPrice(1), price_in_fri: GasPrice(1), }, + l2_gas_price: ResourcePrice { + price_in_wei: GasPrice(1), + price_in_fri: GasPrice(1), + }, ..Default::default() }) .unwrap(), diff --git a/crates/http_server/Cargo.toml b/crates/http_server/Cargo.toml index 3a98ede5dbb..ce122531d2d 100644 --- a/crates/http_server/Cargo.toml +++ b/crates/http_server/Cargo.toml @@ -9,7 +9,6 @@ repository.workspace = true workspace = true [dependencies] -async-trait.workspace = true axum.workspace = true papyrus_config.workspace = true serde.workspace = true diff --git a/crates/http_server/src/communication.rs b/crates/http_server/src/communication.rs index 7e1d13e4573..6037af22703 100644 --- a/crates/http_server/src/communication.rs +++ b/crates/http_server/src/communication.rs @@ -1,8 +1,8 @@ -use starknet_mempool_infra::component_server::{create_empty_server, EmptyServer}; +use starknet_mempool_infra::component_server::{create_empty_server, WrapperServer}; use crate::http_server::HttpServer as HttpServerComponent; -pub type HttpServer = EmptyServer; +pub type HttpServer = WrapperServer; pub fn create_http_server(http_server: HttpServerComponent) -> HttpServer { create_empty_server(http_server) diff --git a/crates/http_server/src/http_server.rs b/crates/http_server/src/http_server.rs index 91c0b5277ac..c216f8cf29e 100644 --- a/crates/http_server/src/http_server.rs +++ b/crates/http_server/src/http_server.rs @@ -1,17 +1,17 @@ +use std::any::type_name; use std::clone::Clone; use std::net::SocketAddr; -use async_trait::async_trait; use axum::extract::State; use axum::routing::{get, post}; -use axum::{Json, Router}; +use axum::{async_trait, Json, Router}; use starknet_api::rpc_transaction::RpcTransaction; use starknet_api::transaction::TransactionHash; use starknet_gateway::errors::GatewayRunError; use starknet_gateway_types::communication::SharedGatewayClient; use starknet_gateway_types::errors::GatewaySpecError; use starknet_gateway_types::gateway_types::GatewayInput; -use starknet_mempool_infra::component_runner::ComponentStarter; +use starknet_mempool_infra::component_definitions::ComponentStarter; use starknet_mempool_infra::errors::ComponentError; use tracing::{error, info, instrument}; @@ -98,7 +98,7 @@ pub fn create_http_server( #[async_trait] impl ComponentStarter for HttpServer { async fn start(&mut self) -> Result<(), ComponentError> { - info!("HttpServer::start()"); + info!("Starting component {}.", type_name::()); self.run().await.map_err(|_| ComponentError::InternalComponentError) } } diff --git a/crates/mempool/Cargo.toml b/crates/mempool/Cargo.toml index 5de8ce8e61d..7829b786144 100644 --- a/crates/mempool/Cargo.toml +++ b/crates/mempool/Cargo.toml @@ -11,6 +11,8 @@ workspace = true [dependencies] async-trait.workspace = true derive_more.workspace = true +pretty_assertions = { workspace = true, optional = true } +starknet-types-core = { workspace = true, optional = true } starknet_api.workspace = true starknet_mempool_infra.workspace = true starknet_mempool_types.workspace = true @@ -20,8 +22,14 @@ tokio.workspace = true assert_matches.workspace = true itertools.workspace = true mempool_test_utils.workspace = true -pretty_assertions.workspace = true rstest.workspace = true -starknet-types-core.workspace = true starknet_api = { workspace = true, features = ["testing"] } tokio.workspace = true +# Enable test utils feature for integration tests. +starknet_mempool = { workspace = true, features = ["testing"] } + +[features] +testing = ["pretty_assertions", "starknet-types-core"] + +[package.metadata.cargo-machete] +ignored = ["starknet-types-core"] diff --git a/crates/mempool/src/communication.rs b/crates/mempool/src/communication.rs index 8afdf21bac2..9a7b3d1fc73 100644 --- a/crates/mempool/src/communication.rs +++ b/crates/mempool/src/communication.rs @@ -1,26 +1,25 @@ use async_trait::async_trait; use starknet_api::executable_transaction::Transaction; -use starknet_mempool_infra::component_definitions::ComponentRequestHandler; -use starknet_mempool_infra::component_runner::ComponentStarter; +use starknet_mempool_infra::component_definitions::{ComponentRequestHandler, ComponentStarter}; use starknet_mempool_infra::component_server::LocalComponentServer; use starknet_mempool_types::communication::{ + AddTransactionArgsWrapper, MempoolRequest, MempoolRequestAndResponseSender, MempoolResponse, - MempoolWrapperInput, }; -use starknet_mempool_types::mempool_types::MempoolResult; +use starknet_mempool_types::mempool_types::{CommitBlockArgs, MempoolResult}; use tokio::sync::mpsc::Receiver; use crate::mempool::Mempool; -pub type MempoolServer = +pub type LocalMempoolServer = LocalComponentServer; pub fn create_mempool_server( mempool: Mempool, rx_mempool: Receiver, -) -> MempoolServer { +) -> LocalMempoolServer { let communication_wrapper = MempoolCommunicationWrapper::new(mempool); LocalComponentServer::new(communication_wrapper, rx_mempool) } @@ -35,8 +34,12 @@ impl MempoolCommunicationWrapper { MempoolCommunicationWrapper { mempool } } - fn add_tx(&mut self, mempool_wrapper_input: MempoolWrapperInput) -> MempoolResult<()> { - self.mempool.add_tx(mempool_wrapper_input.mempool_input) + fn add_tx(&mut self, args_wrapper: AddTransactionArgsWrapper) -> MempoolResult<()> { + self.mempool.add_tx(args_wrapper.args) + } + + fn commit_block(&mut self, args: CommitBlockArgs) -> MempoolResult<()> { + self.mempool.commit_block(args) } fn get_txs(&mut self, n_txs: usize) -> MempoolResult> { @@ -48,8 +51,11 @@ impl MempoolCommunicationWrapper { impl ComponentRequestHandler for MempoolCommunicationWrapper { async fn handle_request(&mut self, request: MempoolRequest) -> MempoolResponse { match request { - MempoolRequest::AddTransaction(mempool_wrapper_input) => { - MempoolResponse::AddTransaction(self.add_tx(mempool_wrapper_input)) + MempoolRequest::AddTransaction(args) => { + MempoolResponse::AddTransaction(self.add_tx(args)) + } + MempoolRequest::CommitBlock(args) => { + MempoolResponse::CommitBlock(self.commit_block(args)) } MempoolRequest::GetTransactions(n_txs) => { MempoolResponse::GetTransactions(self.get_txs(n_txs)) @@ -58,5 +64,4 @@ impl ComponentRequestHandler for MempoolCommuni } } -#[async_trait] impl ComponentStarter for MempoolCommunicationWrapper {} diff --git a/crates/mempool/src/lib.rs b/crates/mempool/src/lib.rs index 6dc5fe9fa5a..6c062a4786f 100644 --- a/crates/mempool/src/lib.rs +++ b/crates/mempool/src/lib.rs @@ -3,3 +3,6 @@ pub mod mempool; pub(crate) mod suspended_transaction_pool; pub(crate) mod transaction_pool; pub(crate) mod transaction_queue; + +#[cfg(any(feature = "testing", test))] +pub mod test_utils; diff --git a/crates/mempool/src/mempool.rs b/crates/mempool/src/mempool.rs index b21e7a9120b..351af0b2ecd 100644 --- a/crates/mempool/src/mempool.rs +++ b/crates/mempool/src/mempool.rs @@ -4,7 +4,12 @@ use starknet_api::core::{ContractAddress, Nonce}; use starknet_api::executable_transaction::Transaction; use starknet_api::transaction::{Tip, TransactionHash, ValidResourceBounds}; use starknet_mempool_types::errors::MempoolError; -use starknet_mempool_types::mempool_types::{AccountState, MempoolInput, MempoolResult}; +use starknet_mempool_types::mempool_types::{ + AccountState, + AddTransactionArgs, + CommitBlockArgs, + MempoolResult, +}; use crate::transaction_pool::TransactionPool; use crate::transaction_queue::TransactionQueue; @@ -76,11 +81,13 @@ impl Mempool { /// Adds a new transaction to the mempool. /// TODO: support fee escalation and transactions with future nonces. /// TODO: check Account nonce and balance. - pub fn add_tx(&mut self, input: MempoolInput) -> MempoolResult<()> { - self.validate_input(&input)?; - let MempoolInput { tx, account_state: AccountState { sender_address, nonce } } = input; + pub fn add_tx(&mut self, args: AddTransactionArgs) -> MempoolResult<()> { + self.validate_input(&args)?; + + let AddTransactionArgs { tx, account_state } = args; self.tx_pool.insert(tx)?; - self.align_to_account_state(sender_address, nonce); + self.align_to_account_state(account_state); + Ok(()) } @@ -88,20 +95,16 @@ impl Mempool { /// updates account balances). // TODO: the part about resolving nonce gaps is incorrect if we delete txs in get_txs and then // push back. - // state_changes: a map that associates each account address with the state of the committed - // block. - pub fn commit_block( - &mut self, - state_changes: HashMap, - ) -> MempoolResult<()> { - for (&address, &nonce) in &state_changes { + pub fn commit_block(&mut self, args: CommitBlockArgs) -> MempoolResult<()> { + for (&address, &nonce) in &args.nonces { let next_nonce = nonce.try_increment().map_err(|_| MempoolError::FeltOutOfRange)?; - self.align_to_account_state(address, next_nonce); + let account_state = AccountState { address, nonce: next_nonce }; + self.align_to_account_state(account_state); } // Rewind nonces of addresses that were not included in block. let addresses_not_included_in_block = - self.mempool_state.keys().filter(|&key| !state_changes.contains_key(key)); + self.mempool_state.keys().filter(|&key| !args.nonces.contains_key(key)); for address in addresses_not_included_in_block { self.tx_queue.remove(*address); } @@ -116,16 +119,16 @@ impl Mempool { self.tx_queue._update_gas_price_threshold(threshold); } - fn validate_input(&self, input: &MempoolInput) -> MempoolResult<()> { - let sender_address = input.tx.contract_address(); - let tx_nonce = input.tx.nonce(); + fn validate_input(&self, args: &AddTransactionArgs) -> MempoolResult<()> { + let sender_address = args.tx.contract_address(); + let tx_nonce = args.tx.nonce(); let duplicate_nonce_error = MempoolError::DuplicateNonce { address: sender_address, nonce: tx_nonce }; // Stateless checks. // Check the input: transaction nonce against given account state. - let account_nonce = input.account_state.nonce; + let account_nonce = args.account_state.nonce; if account_nonce > tx_nonce { return Err(duplicate_nonce_error); } @@ -154,7 +157,7 @@ impl Mempool { fn enqueue_next_eligible_txs(&mut self, txs: &[TransactionReference]) -> MempoolResult<()> { for tx in txs { let current_account_state = - AccountState { sender_address: tx.sender_address, nonce: tx.nonce }; + AccountState { address: tx.sender_address, nonce: tx.nonce }; if let Some(next_tx_reference) = self.tx_pool.get_next_eligible_tx(current_account_state)? @@ -166,9 +169,8 @@ impl Mempool { Ok(()) } - // TODO: Consider creating an abstraction for the (address, nonce) tuple that is passed - // throughout the code. - fn align_to_account_state(&mut self, address: ContractAddress, nonce: Nonce) { + fn align_to_account_state(&mut self, account_state: AccountState) { + let AccountState { address, nonce } = account_state; // Maybe remove out-of-date transactions. // Note: != is equivalent to > in `add_tx`, as lower nonces are rejected in validation. if self.tx_queue.get_nonce(address).is_some_and(|queued_nonce| queued_nonce != nonce) { diff --git a/crates/mempool/src/mempool_test.rs b/crates/mempool/src/mempool_test.rs index 42cb106c0cf..8065b2b54cc 100644 --- a/crates/mempool/src/mempool_test.rs +++ b/crates/mempool/src/mempool_test.rs @@ -1,11 +1,10 @@ use std::cmp::Reverse; -use std::collections::HashMap; use assert_matches::assert_matches; use mempool_test_utils::starknet_api_test_utils::test_resource_bounds_mapping; use pretty_assertions::assert_eq; use rstest::{fixture, rstest}; -use starknet_api::core::{ContractAddress, Nonce, PatriciaKey}; +use starknet_api::core::{ContractAddress, PatriciaKey}; use starknet_api::executable_transaction::Transaction; use starknet_api::hash::StarkHash; use starknet_api::test_utils::invoke::executable_invoke_tx; @@ -14,13 +13,15 @@ use starknet_api::{contract_address, felt, invoke_tx_args, nonce, patricia_key}; use starknet_mempool_types::errors::MempoolError; use starknet_mempool_types::mempool_types::AccountState; -use crate::mempool::{AccountToNonce, Mempool, MempoolInput, TransactionReference}; +use crate::mempool::{AccountToNonce, AddTransactionArgs, Mempool, TransactionReference}; +use crate::test_utils::{add_tx, add_tx_expect_error, commit_block, get_txs_and_assert_expected}; use crate::transaction_pool::TransactionPool; use crate::transaction_queue::transaction_queue_test_utils::{ TransactionQueueContent, TransactionQueueContentBuilder, }; use crate::transaction_queue::TransactionQueue; +use crate::{add_tx_input, tx}; // Utils. @@ -34,25 +35,18 @@ struct MempoolContent { } impl MempoolContent { - fn assert_eq_pool_and_priority_queue_content(&self, mempool: &Mempool) { - self.assert_eq_tx_pool_content(mempool); - self.assert_eq_tx_priority_queue_content(mempool); - } - - fn assert_eq_tx_pool_content(&self, mempool: &Mempool) { - assert_eq!(self.tx_pool.as_ref().unwrap(), &mempool.tx_pool); - } - - fn assert_eq_tx_priority_queue_content(&self, mempool: &Mempool) { - self.tx_queue_content.as_ref().unwrap().assert_eq_priority_queue_content(&mempool.tx_queue); - } + fn assert_eq(&self, mempool: &Mempool) { + if let Some(tx_pool) = &self.tx_pool { + assert_eq!(&mempool.tx_pool, tx_pool); + } - fn _assert_eq_tx_pending_queue_content(&self, mempool: &Mempool) { - self.tx_queue_content.as_ref().unwrap()._assert_eq_pending_queue_content(&mempool.tx_queue); - } + if let Some(tx_queue_content) = &self.tx_queue_content { + tx_queue_content.assert_eq(&mempool.tx_queue); + } - fn assert_eq_account_nonces(&self, mempool: &Mempool) { - assert_eq!(self.account_nonces.as_ref().unwrap(), &mempool.account_nonces); + if let Some(account_nonces) = &self.account_nonces { + assert_eq!(&mempool.account_nonces, account_nonces); + } } } @@ -109,9 +103,14 @@ impl MempoolContentBuilder { fn with_account_nonces(mut self, account_nonce_pairs: A) -> Self where - A: IntoIterator, + A: IntoIterator, { - self.account_nonces = Some(account_nonce_pairs.into_iter().collect()); + self.account_nonces = Some( + account_nonce_pairs + .into_iter() + .map(|(address, nonce)| (contract_address!(address), nonce!(nonce))) + .collect(), + ); self } @@ -148,101 +147,6 @@ impl FromIterator for TransactionQueue { } } -#[track_caller] -fn add_tx(mempool: &mut Mempool, input: &MempoolInput) { - assert_eq!(mempool.add_tx(input.clone()), Ok(())); -} - -#[track_caller] -fn add_tx_expect_error(mempool: &mut Mempool, input: &MempoolInput, expected_error: MempoolError) { - assert_eq!(mempool.add_tx(input.clone()), Err(expected_error)); -} - -#[track_caller] -fn commit_block( - mempool: &mut Mempool, - state_changes: impl IntoIterator, -) { - let state_changes = HashMap::from_iter( - state_changes - .into_iter() - .map(|(address, nonce)| (contract_address!(address), nonce!(nonce))), - ); - assert_eq!(mempool.commit_block(state_changes), Ok(())); -} - -#[track_caller] -fn get_txs_and_assert_expected(mempool: &mut Mempool, n_txs: usize, expected_txs: &[Transaction]) { - let txs = mempool.get_txs(n_txs).unwrap(); - assert_eq!(txs, expected_txs); -} - -/// Creates an executable invoke transaction with the given field subset (the rest receive default -/// values). -macro_rules! tx { - (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, - tx_nonce: $tx_nonce:expr, resource_bounds: $resource_bounds:expr) => {{ - let sender_address = contract_address!($sender_address); - Transaction::Invoke(executable_invoke_tx(invoke_tx_args!{ - sender_address: sender_address, - tx_hash: TransactionHash(StarkHash::from($tx_hash)), - tip: Tip($tip), - nonce: nonce!($tx_nonce), - resource_bounds: $resource_bounds, - })) - }}; - (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, tx_nonce: $tx_nonce:expr) => { - tx!(tip: $tip, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: $tx_nonce, resource_bounds: ValidResourceBounds::AllResources(test_resource_bounds_mapping())) - }; - (tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, tx_nonce: $tx_nonce:expr) => { - tx!(tip: 0, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: $tx_nonce) - }; - (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr) => { - tx!(tip: $tip, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: 0) - }; - (tx_hash: $tx_hash:expr, tx_nonce: $tx_nonce:expr) => { - tx!(tip: 0, tx_hash: $tx_hash, sender_address: "0x0", tx_nonce: $tx_nonce) - }; - (tx_nonce: $tx_nonce:expr) => { - tx!(tip: 0, tx_hash: 0, sender_address: "0x0", tx_nonce: $tx_nonce) - }; -} - -/// Creates an input for `add_tx` with the given field subset (the rest receive default values). -macro_rules! add_tx_input { - (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, - tx_nonce: $tx_nonce:expr, account_nonce: $account_nonce:expr, resource_bounds: $resource_bounds:expr) => {{ - let tx = tx!(tip: $tip, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: $tx_nonce, resource_bounds: $resource_bounds); - let sender_address = contract_address!($sender_address); - let account_nonce = nonce!($account_nonce); - let account_state = AccountState { sender_address, nonce: account_nonce}; - - MempoolInput { tx, account_state } - }}; - (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, - tx_nonce: $tx_nonce:expr, account_nonce: $account_nonce:expr) => {{ - add_tx_input!(tip: $tip, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: $tx_nonce, account_nonce: $account_nonce, resource_bounds: ValidResourceBounds::AllResources(test_resource_bounds_mapping())) - }}; - (tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, tx_nonce: $tx_nonce:expr, account_nonce: $account_nonce:expr) => { - add_tx_input!(tip: 0, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: $tx_nonce, account_nonce: $account_nonce) - }; - (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr) => { - add_tx_input!(tip: $tip, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: 0, account_nonce: 0) - }; - (tx_hash: $tx_hash:expr, tx_nonce: $tx_nonce:expr, account_nonce: $account_nonce:expr) => { - add_tx_input!(tip: 1, tx_hash: $tx_hash, sender_address: "0x0", tx_nonce: $tx_nonce, account_nonce: $account_nonce) - }; - (tx_nonce: $tx_nonce:expr, account_nonce: $account_nonce:expr) => { - add_tx_input!(tip: 1, tx_hash: 0, sender_address: "0x0", tx_nonce: $tx_nonce, account_nonce: $account_nonce) - }; - (tip: $tip:expr, tx_hash: $tx_hash:expr) => { - add_tx_input!(tip: $tip, tx_hash: $tx_hash, sender_address: "0x0", tx_nonce: 0, account_nonce: 0) - }; - (tx_hash: $tx_hash:expr, tx_nonce: $tx_nonce:expr) => { - add_tx_input!(tip: 0, tx_hash: $tx_hash, sender_address: "0x0", tx_nonce: $tx_nonce, account_nonce: 0) - }; -} - // Fixtures. #[fixture] @@ -282,15 +186,13 @@ fn test_get_txs_returns_by_priority_order(#[case] n_requested_txs: usize) { // Assert: non-returned transactions are still in the mempool. let remaining_tx_references = remaining_txs.iter().map(TransactionReference::new); - let mempool_content = MempoolContentBuilder::new() - .with_pool(remaining_txs.to_vec()) - .with_priority_queue(remaining_tx_references) - .build(); - mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + let mempool_content = + MempoolContentBuilder::new().with_priority_queue(remaining_tx_references).build(); + mempool_content.assert_eq(&mempool); } #[rstest] -fn test_get_txs_multi_nonce() { +fn test_get_txs_removes_returned_txs_from_pool() { // Setup. let tx_nonce_0 = tx!(tx_hash: 1, sender_address: "0x0", tx_nonce: 0); let tx_nonce_1 = tx!(tx_hash: 2, sender_address: "0x0", tx_nonce: 1); @@ -307,7 +209,7 @@ fn test_get_txs_multi_nonce() { get_txs_and_assert_expected(&mut mempool, 3, &pool_txs); let expected_mempool_content = MempoolContentBuilder::new().with_pool([]).with_priority_queue([]).build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -333,9 +235,8 @@ fn test_get_txs_replenishes_queue_only_between_chunks() { 3, &[tx_address_0_nonce_0, tx_address_1_nonce_0, tx_address_0_nonce_1], ); - let expected_mempool_content = - MempoolContentBuilder::new().with_pool([]).with_priority_queue([]).build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + let expected_mempool_content = MempoolContentBuilder::new().with_priority_queue([]).build(); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -363,22 +264,19 @@ fn test_get_txs_replenishes_queue_multi_account_between_chunks() { get_txs_and_assert_expected(&mut mempool, 2, &[tx_address_0_nonce_0, tx_address_1_nonce_0]); let expected_queue_txs = [&tx_address_0_nonce_1, &tx_address_1_nonce_1].map(TransactionReference::new); - let expected_pool_txs = [tx_address_0_nonce_1, tx_address_1_nonce_1]; - let expected_mempool_content = MempoolContentBuilder::new() - .with_pool(expected_pool_txs) - .with_priority_queue(expected_queue_txs) - .build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + let expected_mempool_content = + MempoolContentBuilder::new().with_priority_queue(expected_queue_txs).build(); + expected_mempool_content.assert_eq(&mempool); } #[rstest] -fn test_get_txs_with_holes_multiple_accounts() { +fn test_get_txs_with_nonce_gap() { // Setup. let tx_address_0_nonce_1 = tx!(tx_hash: 2, sender_address: "0x0", tx_nonce: 1); let tx_address_1_nonce_0 = tx!(tx_hash: 3, sender_address: "0x1", tx_nonce: 0); let queue_txs = [TransactionReference::new(&tx_address_1_nonce_0)]; - let pool_txs = [&tx_address_0_nonce_1, &tx_address_1_nonce_0].map(|tx| tx.clone()); + let pool_txs = [tx_address_0_nonce_1, tx_address_1_nonce_0.clone()]; let mut mempool = MempoolContentBuilder::new() .with_pool(pool_txs) .with_priority_queue(queue_txs) @@ -386,27 +284,8 @@ fn test_get_txs_with_holes_multiple_accounts() { // Test and assert. get_txs_and_assert_expected(&mut mempool, 2, &[tx_address_1_nonce_0]); - let expected_mempool_content = MempoolContentBuilder::new() - .with_pool([tx_address_0_nonce_1]) - .with_priority_queue([]) - .build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); -} - -#[rstest] -fn test_get_txs_with_holes_single_account() { - // Setup. - let pool_txs = [tx!(tx_nonce: 1)]; - let mut mempool = MempoolContentBuilder::new() - .with_pool(pool_txs.clone()) - .with_priority_queue([]) - .build_into_mempool(); - - // Test and assert. - get_txs_and_assert_expected(&mut mempool, 1, &[]); - let expected_mempool_content = - MempoolContentBuilder::new().with_pool(pool_txs).with_priority_queue([]).build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + let expected_mempool_content = MempoolContentBuilder::new().with_priority_queue([]).build(); + expected_mempool_content.assert_eq(&mempool); } // TODO(Mohammad): simplify two queues reordering tests to use partial queue content test util. @@ -425,10 +304,6 @@ fn test_get_txs_while_decreasing_gas_price_threshold() { mempool._update_gas_price_threshold(1000000000000); get_txs_and_assert_expected(&mut mempool, 1, &[]); - // Updating the gas price threshold should happen in a new block creation. - let state_changes = []; - commit_block(&mut mempool, state_changes); - // Low gas price threshold, the transaction should be returned. mempool._update_gas_price_threshold(100); get_txs_and_assert_expected(&mut mempool, 1, &[tx]); @@ -451,10 +326,6 @@ fn test_get_txs_while_increasing_gas_price_threshold() { mempool._update_gas_price_threshold(100); get_txs_and_assert_expected(&mut mempool, 1, &[tx_nonce_0]); - // Updating the gas price threshold should happen in a new block creation. - let state_changes = []; - commit_block(&mut mempool, state_changes); - // High gas price threshold, no transactions should be returned. mempool._update_gas_price_threshold(1000000000000); get_txs_and_assert_expected(&mut mempool, 1, &[]); @@ -466,9 +337,9 @@ fn test_get_txs_while_increasing_gas_price_threshold() { fn test_add_tx(mut mempool: Mempool) { // Setup. let mut add_tx_inputs = [ - add_tx_input!(tip: 50, tx_hash: 1, sender_address: "0x0"), - add_tx_input!(tip: 100, tx_hash: 2, sender_address: "0x1"), - add_tx_input!(tip: 80, tx_hash: 3, sender_address: "0x2"), + add_tx_input!(tip: 50, tx_hash: 1, sender_address: "0x0", tx_nonce: 0, account_nonce: 0), + add_tx_input!(tip: 100, tx_hash: 2, sender_address: "0x1", tx_nonce: 1, account_nonce: 1), + add_tx_input!(tip: 80, tx_hash: 3, sender_address: "0x2", tx_nonce: 2, account_nonce: 2), ]; // Test. @@ -481,14 +352,16 @@ fn test_add_tx(mut mempool: Mempool) { add_tx_inputs.sort_by_key(|input| std::cmp::Reverse(input.tx.tip().unwrap())); // Assert: transactions are ordered by priority. + let expected_account_nonces = [("0x0", 0), ("0x1", 1), ("0x2", 2)]; let expected_queue_txs: Vec = add_tx_inputs.iter().map(|input| TransactionReference::new(&input.tx)).collect(); let expected_pool_txs = add_tx_inputs.into_iter().map(|input| input.tx); let expected_mempool_content = MempoolContentBuilder::new() + .with_account_nonces(expected_account_nonces) .with_pool(expected_pool_txs) .with_priority_queue(expected_queue_txs) .build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -515,7 +388,7 @@ fn test_add_tx_multi_nonce_success(mut mempool: Mempool) { .with_pool(expected_pool_txs) .with_priority_queue(expected_queue_txs) .build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -533,7 +406,7 @@ fn test_add_tx_with_duplicate_tx(mut mempool: Mempool) { // Assert: the original transaction remains. let expected_mempool_content = MempoolContentBuilder::new().with_pool([input.tx]).build(); - expected_mempool_content.assert_eq_tx_pool_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -544,11 +417,10 @@ fn test_add_tx_lower_than_queued_nonce() { let lower_nonce_input = add_tx_input!(tx_hash: 2, sender_address: "0x0", tx_nonce: 0, account_nonce: 0); - let MempoolInput { tx: valid_input_tx, account_state: AccountState { sender_address, nonce } } = - valid_input; + let AddTransactionArgs { tx: valid_input_tx, .. } = valid_input; let queue_txs = [TransactionReference::new(&valid_input_tx)]; let pool_txs = [valid_input_tx]; - let account_nonces = [(sender_address, nonce)]; + let account_nonces = [("0x0", 1)]; let expected_mempool_content = MempoolContentBuilder::new() .with_pool(pool_txs.clone()) .with_priority_queue(queue_txs) @@ -567,8 +439,7 @@ fn test_add_tx_lower_than_queued_nonce() { &lower_nonce_input, MempoolError::DuplicateNonce { address: contract_address!("0x0"), nonce: nonce!(0) }, ); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); - expected_mempool_content.assert_eq_account_nonces(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -589,7 +460,7 @@ fn test_add_tx_updates_queue_with_higher_account_nonce() { let expected_queue_txs = [TransactionReference::new(&higher_account_nonce_input.tx)]; let expected_mempool_content = MempoolContentBuilder::new().with_priority_queue(expected_queue_txs).build(); - expected_mempool_content.assert_eq_tx_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -615,7 +486,7 @@ fn test_add_tx_with_identical_tip_succeeds(mut mempool: Mempool) { // TODO: currently hash comparison tie-breaks the two. Once more robust tie-breaks are added // replace this assertion with a dedicated test. - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -643,7 +514,7 @@ fn test_add_tx_delete_tx_with_lower_nonce_than_account_nonce() { .with_pool(expected_pool_txs) .with_priority_queue(expected_queue_txs) .build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -663,11 +534,11 @@ fn test_add_tx_tip_priority_over_tx_hash(mut mempool: Mempool) { [&input_big_tip_small_hash.tx, &input_small_tip_big_hash.tx].map(TransactionReference::new); let expected_mempool_content = MempoolContentBuilder::new().with_priority_queue(expected_queue_txs).build(); - expected_mempool_content.assert_eq_tx_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] -fn test_add_tx_account_state_fills_hole(mut mempool: Mempool) { +fn test_add_tx_account_state_fills_nonce_gap(mut mempool: Mempool) { // Setup. let tx_input_nonce_1 = add_tx_input!(tx_hash: 1, tx_nonce: 1, account_nonce: 0); // Input that increments the account state. @@ -678,14 +549,14 @@ fn test_add_tx_account_state_fills_hole(mut mempool: Mempool) { // First, with gap. add_tx(&mut mempool, &tx_input_nonce_1); let expected_mempool_content = MempoolContentBuilder::new().with_priority_queue([]).build(); - expected_mempool_content.assert_eq_tx_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); // Then, fill it. add_tx(&mut mempool, &tx_input_nonce_2); let expected_queue_txs = [&tx_input_nonce_1.tx].map(TransactionReference::new); let expected_mempool_content = MempoolContentBuilder::new().with_priority_queue(expected_queue_txs).build(); - expected_mempool_content.assert_eq_tx_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -705,12 +576,11 @@ fn test_add_tx_sequential_nonces(mut mempool: Mempool) { .with_pool(expected_pool_txs) .with_priority_queue(expected_queue_txs) .build(); - - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] -fn test_add_tx_filling_hole(mut mempool: Mempool) { +fn test_add_tx_fills_nonce_gap(mut mempool: Mempool) { // Setup. let input_nonce_0 = add_tx_input!(tx_hash: 1, tx_nonce: 0, account_nonce: 0); let input_nonce_1 = add_tx_input!(tx_hash: 2, tx_nonce: 1, account_nonce: 0); @@ -722,7 +592,7 @@ fn test_add_tx_filling_hole(mut mempool: Mempool) { let expected_pool_txs = [input_nonce_1.tx.clone()]; let expected_mempool_content = MempoolContentBuilder::new().with_pool(expected_pool_txs).with_priority_queue([]).build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); // Test: add the first transaction, which fills the hole. add_tx(&mut mempool, &input_nonce_0); @@ -734,7 +604,7 @@ fn test_add_tx_filling_hole(mut mempool: Mempool) { .with_pool(expected_pool_txs) .with_priority_queue(expected_queue_txs) .build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } // `commit_block` tests. @@ -779,13 +649,13 @@ fn test_commit_block_includes_all_txs() { .build_into_mempool(); // Test. - let state_changes = [("0x0", 3), ("0x1", 2)]; - commit_block(&mut mempool, state_changes); + let nonces = [("0x0", 3), ("0x1", 2)]; + commit_block(&mut mempool, nonces); // Assert. let expected_mempool_content = MempoolContentBuilder::new().with_pool(pool_txs).with_priority_queue(queue_txs).build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -801,12 +671,12 @@ fn test_commit_block_rewinds_queued_nonce() { .build_into_mempool(); // Test. - let state_changes = [("0x0", 3), ("0x1", 3)]; - commit_block(&mut mempool, state_changes); + let nonces = [("0x0", 3), ("0x1", 3)]; + commit_block(&mut mempool, nonces); // Assert. let expected_mempool_content = MempoolContentBuilder::new().with_priority_queue([]).build(); - expected_mempool_content.assert_eq_tx_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] @@ -830,43 +700,28 @@ fn test_commit_block_from_different_leader() { .build_into_mempool(); // Test. - let state_changes = [ + let nonces = [ ("0x0", 5), // A hole, missing nonce 1 for address "0x1". ("0x1", 0), ("0x2", 1), ]; - commit_block(&mut mempool, state_changes); + commit_block(&mut mempool, nonces); // Assert. let expected_queue_txs = [&tx_address_0_nonce_6].map(TransactionReference::new); let expected_mempool_content = MempoolContentBuilder::new().with_priority_queue(expected_queue_txs).build(); - expected_mempool_content.assert_eq_tx_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } // `account_nonces` tests. -#[rstest] -fn test_account_nonces_update_in_add_tx(mut mempool: Mempool) { - // Setup. - let input = add_tx_input!(tx_nonce: 1, account_nonce: 1); - - // Test: update through new input. - add_tx(&mut mempool, &input); - - // Assert. - let AccountState { sender_address, nonce } = input.account_state; - let expected_mempool_content = - MempoolContentBuilder::new().with_account_nonces([(sender_address, nonce)]).build(); - expected_mempool_content.assert_eq_account_nonces(&mempool); -} - #[rstest] fn test_account_nonce_does_not_decrease_in_add_tx() { // Setup. let input_with_lower_account_nonce = add_tx_input!(tx_nonce: 0, account_nonce: 0); - let account_nonces = [(input_with_lower_account_nonce.account_state.sender_address, nonce!(2))]; + let account_nonces = [("0x0", 2)]; let mut mempool = MempoolContentBuilder::new().with_account_nonces(account_nonces).build_into_mempool(); @@ -876,138 +731,64 @@ fn test_account_nonce_does_not_decrease_in_add_tx() { // Assert: the account nonce is not updated. let expected_mempool_content = MempoolContentBuilder::new().with_account_nonces(account_nonces).build(); - expected_mempool_content.assert_eq_account_nonces(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] fn test_account_nonces_update_in_commit_block() { // Setup. - let input = add_tx_input!(tx_nonce: 2, account_nonce: 0); - let AccountState { sender_address, nonce } = input.account_state; - let pool_txs = [input.tx]; + let pool_txs = [tx!(tx_nonce: 2)]; let mut mempool = MempoolContentBuilder::new() .with_pool(pool_txs) - .with_account_nonces([(sender_address, nonce)]) + .with_account_nonces([("0x0", 0)]) .build_into_mempool(); - let committed_nonce = nonce!(0); // Test: update through a commit block. - let state_changes = HashMap::from([(sender_address, committed_nonce)]); - assert_eq!(mempool.commit_block(state_changes), Ok(())); + let nonces = [("0x0", 0)]; + commit_block(&mut mempool, nonces); // Assert. - let expected_mempool_content = MempoolContentBuilder::new() - .with_account_nonces([(sender_address, committed_nonce.try_increment().unwrap())]) - .build(); - expected_mempool_content.assert_eq_account_nonces(&mempool); + let expected_account_nonces = [("0x0", 1)]; // Account nonce advanced. + let expected_mempool_content = + MempoolContentBuilder::new().with_account_nonces(expected_account_nonces).build(); + expected_mempool_content.assert_eq(&mempool); } #[rstest] fn test_account_nonce_does_not_decrease_in_commit_block() { // Setup. - let input_account_nonce_2 = add_tx_input!(tx_nonce: 3, account_nonce: 2); - let AccountState { sender_address, nonce } = input_account_nonce_2.account_state; - let account_nonces = [(sender_address, nonce)]; - let pool_txs = [input_account_nonce_2.tx]; + let account_nonces = [("0x0", 2)]; + let pool_txs = [tx!(tx_nonce: 3)]; let mut mempool = MempoolContentBuilder::new() .with_pool(pool_txs) .with_account_nonces(account_nonces) .build_into_mempool(); // Test: commits state change of a lower account nonce. - let state_changes = HashMap::from([(sender_address, nonce!(0))]); - assert_eq!(mempool.commit_block(state_changes), Ok(())); + let nonces = [("0x0", 0)]; + commit_block(&mut mempool, nonces); // Assert: the account nonce is not updated. let expected_mempool_content = MempoolContentBuilder::new().with_account_nonces(account_nonces).build(); - expected_mempool_content.assert_eq_account_nonces(&mempool); + expected_mempool_content.assert_eq(&mempool); } #[rstest] fn test_account_nonces_removal_in_commit_block(mut mempool: Mempool) { // Test: commit block returns information about account that is not in the mempool. - let state_changes = [("0x0", 0)]; - commit_block(&mut mempool, state_changes); + let nonces = [("0x0", 0)]; + commit_block(&mut mempool, nonces); // Assert: account is not added to the mempool. let expected_mempool_content = MempoolContentBuilder::new().with_account_nonces([]).build(); - expected_mempool_content.assert_eq_account_nonces(&mempool); + expected_mempool_content.assert_eq(&mempool); } // Flow tests. #[rstest] -fn test_flow_filling_holes(mut mempool: Mempool) { - // Setup. - let input_address_0_nonce_0 = - add_tx_input!(tx_hash: 1, sender_address: "0x0", tx_nonce: 0, account_nonce: 0); - let input_address_0_nonce_1 = - add_tx_input!(tx_hash: 2, sender_address: "0x0", tx_nonce: 1, account_nonce: 0); - let input_address_1_nonce_0 = - add_tx_input!(tx_hash: 3, sender_address: "0x1", tx_nonce: 0, account_nonce: 0); - - add_tx(&mut mempool, &input_address_0_nonce_1); - add_tx(&mut mempool, &input_address_1_nonce_0); - - // Test and assert: only the eligible transaction is returned. - get_txs_and_assert_expected(&mut mempool, 2, &[input_address_1_nonce_0.tx]); - - add_tx(&mut mempool, &input_address_0_nonce_0); - - // Test and assert: all remaining transactions are returned. - get_txs_and_assert_expected( - &mut mempool, - 2, - &[input_address_0_nonce_0.tx, input_address_0_nonce_1.tx], - ); -} - -#[rstest] -fn test_flow_partial_commit_block() { - // Setup. - let tx_address_0_nonce_3 = tx!(tip: 10, tx_hash: 1, sender_address: "0x0", tx_nonce: 3); - let tx_address_0_nonce_5 = tx!(tip: 11, tx_hash: 2, sender_address: "0x0", tx_nonce: 5); - let tx_address_0_nonce_6 = tx!(tip: 12, tx_hash: 3, sender_address: "0x0", tx_nonce: 6); - let tx_address_1_nonce_0 = tx!(tip: 20, tx_hash: 4, sender_address: "0x1", tx_nonce: 0); - let tx_address_1_nonce_1 = tx!(tip: 21, tx_hash: 5, sender_address: "0x1", tx_nonce: 1); - let tx_address_1_nonce_2 = tx!(tip: 22, tx_hash: 6, sender_address: "0x1", tx_nonce: 2); - let tx_address_2_nonce_2 = tx!(tip: 0, tx_hash: 7, sender_address: "0x2", tx_nonce: 2); - - let queue_txs = [&tx_address_0_nonce_3, &tx_address_1_nonce_0, &tx_address_2_nonce_2] - .map(TransactionReference::new); - let pool_txs = [ - &tx_address_0_nonce_3, - &tx_address_0_nonce_5, - &tx_address_0_nonce_6, - &tx_address_1_nonce_0, - &tx_address_1_nonce_1, - &tx_address_1_nonce_2, - &tx_address_2_nonce_2, - ] - .map(|tx| tx.clone()); - let mut mempool = MempoolContentBuilder::new() - .with_pool(pool_txs) - .with_priority_queue(queue_txs) - .build_into_mempool(); - - // Test. - get_txs_and_assert_expected(&mut mempool, 2, &[tx_address_1_nonce_0, tx_address_0_nonce_3]); - get_txs_and_assert_expected(&mut mempool, 2, &[tx_address_1_nonce_1, tx_address_2_nonce_2]); - - // Not included in block: address "0x2" nonce 2, address "0x1" nonce 1. - let state_changes = [("0x0", 3), ("0x1", 0)]; - commit_block(&mut mempool, state_changes); - - // Assert. - let expected_pool_txs = [tx_address_0_nonce_5, tx_address_0_nonce_6, tx_address_1_nonce_2]; - let expected_mempool_content = - MempoolContentBuilder::new().with_pool(expected_pool_txs).with_priority_queue([]).build(); - expected_mempool_content.assert_eq_pool_and_priority_queue_content(&mempool); -} - -#[rstest] -fn test_flow_commit_block_closes_hole() { +fn test_flow_commit_block_fills_nonce_gap() { // Setup. let tx_nonce_3 = tx!(tx_hash: 1, sender_address: "0x0", tx_nonce: 3); let tx_input_nonce_4 = @@ -1022,14 +803,14 @@ fn test_flow_commit_block_closes_hole() { .build_into_mempool(); // Test. - let state_changes = [("0x0", 4)]; - commit_block(&mut mempool, state_changes); + let nonces = [("0x0", 4)]; + commit_block(&mut mempool, nonces); // Assert: hole was indeed closed. let expected_queue_txs = [&tx_nonce_5].map(TransactionReference::new); let expected_mempool_content = MempoolContentBuilder::new().with_priority_queue(expected_queue_txs).build(); - expected_mempool_content.assert_eq_tx_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); add_tx_expect_error( &mut mempool, @@ -1056,8 +837,8 @@ fn test_flow_send_same_nonce_tx_after_previous_not_included() { // Test. get_txs_and_assert_expected(&mut mempool, 2, &[tx_nonce_3, tx_input_nonce_4.tx.clone()]); - let state_changes = [("0x0", 3)]; // Transaction with nonce 4 is not included in the block. - commit_block(&mut mempool, state_changes); + let nonces = [("0x0", 3)]; // Transaction with nonce 4 is not included in the block. + commit_block(&mut mempool, nonces); add_tx(&mut mempool, &tx_input_nonce_4); @@ -1067,5 +848,5 @@ fn test_flow_send_same_nonce_tx_after_previous_not_included() { let expected_queue_txs = [TransactionReference::new(&tx_nonce_5)]; let expected_mempool_content = MempoolContentBuilder::new().with_priority_queue(expected_queue_txs).build(); - expected_mempool_content.assert_eq_tx_priority_queue_content(&mempool); + expected_mempool_content.assert_eq(&mempool); } diff --git a/crates/mempool/src/test_utils.rs b/crates/mempool/src/test_utils.rs new file mode 100644 index 00000000000..d8775d748dd --- /dev/null +++ b/crates/mempool/src/test_utils.rs @@ -0,0 +1,112 @@ +use std::collections::HashMap; + +use pretty_assertions::assert_eq; +use starknet_api::core::{ContractAddress, PatriciaKey}; +use starknet_api::executable_transaction::Transaction; +use starknet_api::{contract_address, felt, nonce, patricia_key}; +use starknet_mempool_types::errors::MempoolError; +use starknet_mempool_types::mempool_types::{AddTransactionArgs, CommitBlockArgs}; + +use crate::mempool::Mempool; + +/// Creates an executable invoke transaction with the given field subset (the rest receive default +/// values). +#[macro_export] +macro_rules! tx { + (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, + tx_nonce: $tx_nonce:expr, resource_bounds: $resource_bounds:expr) => {{ + let sender_address = contract_address!($sender_address); + Transaction::Invoke(executable_invoke_tx(invoke_tx_args!{ + sender_address: sender_address, + tx_hash: TransactionHash(StarkHash::from($tx_hash)), + tip: Tip($tip), + nonce: nonce!($tx_nonce), + resource_bounds: $resource_bounds, + })) + }}; + (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, tx_nonce: $tx_nonce:expr) => { + tx!(tip: $tip, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: $tx_nonce, resource_bounds: ValidResourceBounds::AllResources(test_resource_bounds_mapping())) + }; + (tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, tx_nonce: $tx_nonce:expr) => { + tx!(tip: 0, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: $tx_nonce) + }; + (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr) => { + tx!(tip: $tip, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: 0) + }; + (tx_hash: $tx_hash:expr, tx_nonce: $tx_nonce:expr) => { + tx!(tip: 0, tx_hash: $tx_hash, sender_address: "0x0", tx_nonce: $tx_nonce) + }; + (tx_nonce: $tx_nonce:expr) => { + tx!(tip: 0, tx_hash: 0, sender_address: "0x0", tx_nonce: $tx_nonce) + }; +} + +/// Creates an input for `add_tx` with the given field subset (the rest receive default values). +#[macro_export] +macro_rules! add_tx_input { + (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, + tx_nonce: $tx_nonce:expr, account_nonce: $account_nonce:expr, resource_bounds: $resource_bounds:expr) => {{ + let tx = tx!(tip: $tip, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: $tx_nonce, resource_bounds: $resource_bounds); + let address = contract_address!($sender_address); + let account_nonce = nonce!($account_nonce); + let account_state = AccountState { address, nonce: account_nonce}; + + AddTransactionArgs { tx, account_state } + }}; + (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, + tx_nonce: $tx_nonce:expr, account_nonce: $account_nonce:expr) => {{ + add_tx_input!(tip: $tip, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: $tx_nonce, account_nonce: $account_nonce, resource_bounds: ValidResourceBounds::AllResources(test_resource_bounds_mapping())) + }}; + (tx_hash: $tx_hash:expr, sender_address: $sender_address:expr, tx_nonce: $tx_nonce:expr, account_nonce: $account_nonce:expr) => { + add_tx_input!(tip: 0, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: $tx_nonce, account_nonce: $account_nonce) + }; + (tip: $tip:expr, tx_hash: $tx_hash:expr, sender_address: $sender_address:expr) => { + add_tx_input!(tip: $tip, tx_hash: $tx_hash, sender_address: $sender_address, tx_nonce: 0, account_nonce: 0) + }; + (tx_hash: $tx_hash:expr, tx_nonce: $tx_nonce:expr, account_nonce: $account_nonce:expr) => { + add_tx_input!(tip: 1, tx_hash: $tx_hash, sender_address: "0x0", tx_nonce: $tx_nonce, account_nonce: $account_nonce) + }; + (tx_nonce: $tx_nonce:expr, account_nonce: $account_nonce:expr) => { + add_tx_input!(tip: 1, tx_hash: 0, sender_address: "0x0", tx_nonce: $tx_nonce, account_nonce: $account_nonce) + }; + (tip: $tip:expr, tx_hash: $tx_hash:expr) => { + add_tx_input!(tip: $tip, tx_hash: $tx_hash, sender_address: "0x0", tx_nonce: 0, account_nonce: 0) + }; + (tx_hash: $tx_hash:expr, tx_nonce: $tx_nonce:expr) => { + add_tx_input!(tip: 0, tx_hash: $tx_hash, sender_address: "0x0", tx_nonce: $tx_nonce, account_nonce: 0) + }; +} + +#[track_caller] +pub fn add_tx(mempool: &mut Mempool, input: &AddTransactionArgs) { + assert_eq!(mempool.add_tx(input.clone()), Ok(())); +} + +#[track_caller] +pub fn add_tx_expect_error( + mempool: &mut Mempool, + input: &AddTransactionArgs, + expected_error: MempoolError, +) { + assert_eq!(mempool.add_tx(input.clone()), Err(expected_error)); +} + +#[track_caller] +pub fn commit_block(mempool: &mut Mempool, nonces: impl IntoIterator) { + let nonces = HashMap::from_iter( + nonces.into_iter().map(|(address, nonce)| (contract_address!(address), nonce!(nonce))), + ); + let args = CommitBlockArgs { nonces }; + + assert_eq!(mempool.commit_block(args), Ok(())); +} + +#[track_caller] +pub fn get_txs_and_assert_expected( + mempool: &mut Mempool, + n_txs: usize, + expected_txs: &[Transaction], +) { + let txs = mempool.get_txs(n_txs).unwrap(); + assert_eq!(txs, expected_txs); +} diff --git a/crates/mempool/src/transaction_pool.rs b/crates/mempool/src/transaction_pool.rs index d7cf267b78e..05332368cdd 100644 --- a/crates/mempool/src/transaction_pool.rs +++ b/crates/mempool/src/transaction_pool.rs @@ -99,10 +99,10 @@ impl TransactionPool { &self, current_account_state: AccountState, ) -> MempoolResult> { - let AccountState { sender_address, nonce } = current_account_state; + let AccountState { address, nonce } = current_account_state; // TOOD(Ayelet): Change to StarknetApiError. let next_nonce = nonce.try_increment().map_err(|_| MempoolError::FeltOutOfRange)?; - Ok(self.get_by_address_and_nonce(sender_address, next_nonce)) + Ok(self.get_by_address_and_nonce(address, next_nonce)) } pub fn contains_account(&self, address: ContractAddress) -> bool { diff --git a/crates/mempool/src/transaction_queue_test_utils.rs b/crates/mempool/src/transaction_queue_test_utils.rs index 4f7ed207c29..493d8224eeb 100644 --- a/crates/mempool/src/transaction_queue_test_utils.rs +++ b/crates/mempool/src/transaction_queue_test_utils.rs @@ -15,12 +15,14 @@ pub struct TransactionQueueContent { } impl TransactionQueueContent { - pub fn assert_eq_priority_queue_content(&self, tx_queue: &TransactionQueue) { - assert_eq!(self.priority_queue.as_ref().unwrap(), &tx_queue.priority_queue); - } + pub fn assert_eq(&self, tx_queue: &TransactionQueue) { + if let Some(priority_queue) = &self.priority_queue { + assert_eq!(&tx_queue.priority_queue, priority_queue); + } - pub fn _assert_eq_pending_queue_content(&self, tx_queue: &TransactionQueue) { - assert_eq!(self.pending_queue.as_ref().unwrap(), &tx_queue.pending_queue); + if let Some(pending_queue) = &self.pending_queue { + assert_eq!(&tx_queue.pending_queue, pending_queue); + } } pub fn complete_to_tx_queue(self) -> TransactionQueue { diff --git a/crates/mempool/tests/flow_test.rs b/crates/mempool/tests/flow_test.rs new file mode 100644 index 00000000000..3ab1bf410cb --- /dev/null +++ b/crates/mempool/tests/flow_test.rs @@ -0,0 +1,96 @@ +use mempool_test_utils::starknet_api_test_utils::test_resource_bounds_mapping; +use rstest::{fixture, rstest}; +use starknet_api::core::{ContractAddress, PatriciaKey}; +use starknet_api::executable_transaction::Transaction; +use starknet_api::hash::StarkHash; +use starknet_api::test_utils::invoke::executable_invoke_tx; +use starknet_api::transaction::{Tip, TransactionHash, ValidResourceBounds}; +use starknet_api::{contract_address, felt, invoke_tx_args, nonce, patricia_key}; +use starknet_mempool::mempool::Mempool; +use starknet_mempool::test_utils::{add_tx, commit_block, get_txs_and_assert_expected}; +use starknet_mempool::{add_tx_input, tx}; +use starknet_mempool_types::mempool_types::{AccountState, AddTransactionArgs}; + +// Fixtures. + +#[fixture] +fn mempool() -> Mempool { + Mempool::empty() +} + +// Tests. + +#[rstest] +fn test_add_tx_fills_nonce_gap(mut mempool: Mempool) { + // Setup. + let input_address_0_nonce_0 = + add_tx_input!(tx_hash: 1, sender_address: "0x0", tx_nonce: 0, account_nonce: 0); + let input_address_0_nonce_1 = + add_tx_input!(tx_hash: 2, sender_address: "0x0", tx_nonce: 1, account_nonce: 0); + let input_address_1_nonce_0 = + add_tx_input!(tx_hash: 3, sender_address: "0x1", tx_nonce: 0, account_nonce: 0); + + add_tx(&mut mempool, &input_address_0_nonce_1); + add_tx(&mut mempool, &input_address_1_nonce_0); + + // Test and assert: only the eligible transaction is returned. + get_txs_and_assert_expected(&mut mempool, 2, &[input_address_1_nonce_0.tx]); + + add_tx(&mut mempool, &input_address_0_nonce_0); + + // Test and assert: all remaining transactions are returned. + get_txs_and_assert_expected( + &mut mempool, + 2, + &[input_address_0_nonce_0.tx, input_address_0_nonce_1.tx], + ); +} + +#[rstest] +fn test_commit_block_includes_proposed_txs_subset(mut mempool: Mempool) { + // Setup. + let tx_address_0_nonce_3 = + add_tx_input!(tx_hash: 1, sender_address: "0x0", tx_nonce: 3, account_nonce: 3); + let tx_address_0_nonce_5 = + add_tx_input!(tx_hash: 2, sender_address: "0x0", tx_nonce: 5, account_nonce: 3); + let tx_address_0_nonce_6 = + add_tx_input!(tx_hash: 3, sender_address: "0x0", tx_nonce: 6, account_nonce: 3); + let tx_address_1_nonce_0 = + add_tx_input!(tx_hash: 4, sender_address: "0x1", tx_nonce: 0, account_nonce: 0); + let tx_address_1_nonce_1 = + add_tx_input!(tx_hash: 5, sender_address: "0x1", tx_nonce: 1, account_nonce: 0); + let tx_address_1_nonce_2 = + add_tx_input!(tx_hash: 6, sender_address: "0x1", tx_nonce: 2, account_nonce: 0); + let tx_address_2_nonce_2 = + add_tx_input!(tx_hash: 7, sender_address: "0x2", tx_nonce: 2, account_nonce: 2); + + for input in [ + &tx_address_0_nonce_5, + &tx_address_0_nonce_6, + &tx_address_0_nonce_3, + &tx_address_1_nonce_2, + &tx_address_1_nonce_1, + &tx_address_1_nonce_0, + &tx_address_2_nonce_2, + ] { + add_tx(&mut mempool, input); + } + + // Test. + get_txs_and_assert_expected( + &mut mempool, + 2, + &[tx_address_2_nonce_2.tx, tx_address_1_nonce_0.tx], + ); + get_txs_and_assert_expected( + &mut mempool, + 2, + &[tx_address_1_nonce_1.tx, tx_address_0_nonce_3.tx], + ); + + // Not included in block: address "0x2" nonce 2, address "0x1" nonce 1. + let nonces = [("0x0", 3), ("0x1", 0)]; + commit_block(&mut mempool, nonces); + + get_txs_and_assert_expected(&mut mempool, 2, &[]); +} diff --git a/crates/mempool_infra/src/component_client/local_component_client.rs b/crates/mempool_infra/src/component_client/local_component_client.rs index 39edc67978f..c52ffeb5c03 100644 --- a/crates/mempool_infra/src/component_client/local_component_client.rs +++ b/crates/mempool_infra/src/component_client/local_component_client.rs @@ -1,4 +1,7 @@ +use std::any::type_name; + use tokio::sync::mpsc::{channel, Sender}; +use tracing::info; use crate::component_definitions::ComponentRequestAndResponseSender; @@ -78,6 +81,16 @@ where } } +impl Drop for LocalComponentClient +where + Request: Send + Sync, + Response: Send + Sync, +{ + fn drop(&mut self) { + info!("Dropping LocalComponentClient {}.", type_name::()); + } +} + // Can't derive because derive forces the generics to also be `Clone`, which we prefer not to do // since it'll require transactions to be cloneable. impl Clone for LocalComponentClient diff --git a/crates/mempool_infra/src/component_client/remote_component_client.rs b/crates/mempool_infra/src/component_client/remote_component_client.rs index 0e86bd237dd..2537170871c 100644 --- a/crates/mempool_infra/src/component_client/remote_component_client.rs +++ b/crates/mempool_infra/src/component_client/remote_component_client.rs @@ -2,6 +2,7 @@ use std::fmt::Debug; use std::marker::PhantomData; use std::net::IpAddr; use std::sync::Arc; +use std::time::Duration; use hyper::body::to_bytes; use hyper::header::CONTENT_TYPE; @@ -10,7 +11,7 @@ use serde::de::DeserializeOwned; use serde::Serialize; use super::definitions::{ClientError, ClientResult}; -use crate::component_definitions::APPLICATION_OCTET_STREAM; +use crate::component_definitions::{RemoteComponentCommunicationConfig, APPLICATION_OCTET_STREAM}; use crate::serde_utils::BincodeSerdeWrapper; /// The `RemoteComponentClient` struct is a generic client for sending component requests and @@ -24,8 +25,7 @@ use crate::serde_utils::BincodeSerdeWrapper; /// # Fields /// - `uri`: URI address of the server. /// - `client`: The inner HTTP client that initiates the connection to the server and manages it. -/// - `max_retries`: Configurable number of extra attempts to send a request to server in case of a -/// failure. +/// - `config`: Client configuration. /// /// # Example /// ```rust @@ -34,6 +34,7 @@ use crate::serde_utils::BincodeSerdeWrapper; /// use serde::{Deserialize, Serialize}; /// /// use crate::starknet_mempool_infra::component_client::RemoteComponentClient; +/// use crate::starknet_mempool_infra::component_definitions::RemoteComponentCommunicationConfig; /// /// // Define your request and response types /// #[derive(Serialize, Deserialize, Debug, Clone)] @@ -52,7 +53,14 @@ use crate::serde_utils::BincodeSerdeWrapper; /// // Instantiate the client. /// let ip_address = std::net::IpAddr::V6(std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); /// let port: u16 = 8080; -/// let client = RemoteComponentClient::::new(ip_address, port, 2); +/// let socket = std::net::SocketAddr::new(ip_address, port); +/// let config = RemoteComponentCommunicationConfig { +/// socket, +/// retries: 3, +/// idle_connections: usize::MAX, +/// idle_timeout: 90, +/// }; +/// let client = RemoteComponentClient::::new(config); /// /// // Instantiate a request. /// let request = MyRequest { content: "Hello, world!".to_string() }; @@ -73,7 +81,7 @@ where { uri: Uri, client: Client, - max_retries: usize, + config: RemoteComponentCommunicationConfig, _req: PhantomData, _res: PhantomData, } @@ -83,22 +91,27 @@ where Request: Serialize + DeserializeOwned + Debug + Clone, Response: Serialize + DeserializeOwned + Debug, { - pub fn new(ip_address: IpAddr, port: u16, max_retries: usize) -> Self { + pub fn new(config: RemoteComponentCommunicationConfig) -> Self { + let ip_address = config.socket.ip(); + let port = config.socket.port(); let uri = match ip_address { IpAddr::V4(ip_address) => format!("http://{}:{}/", ip_address, port).parse().unwrap(), IpAddr::V6(ip_address) => format!("http://[{}]:{}/", ip_address, port).parse().unwrap(), }; // TODO(Tsabary): Add a configuration for the maximum number of idle connections. // TODO(Tsabary): Add a configuration for "keep-alive" time of idle connections. - let client = - Client::builder().http2_only(true).pool_max_idle_per_host(usize::MAX).build_http(); - Self { uri, client, max_retries, _req: PhantomData, _res: PhantomData } + let client = Client::builder() + .http2_only(true) + .pool_max_idle_per_host(config.idle_connections) + .pool_idle_timeout(Duration::from_secs(config.idle_timeout)) + .build_http(); + Self { uri, client, config, _req: PhantomData, _res: PhantomData } } pub async fn send(&self, component_request: Request) -> ClientResult { // Construct and request, and send it up to 'max_retries' times. Return if received a // successful response. - for _ in 0..self.max_retries { + for _ in 0..self.config.retries { let http_request = self.construct_http_request(component_request.clone()); let res = self.try_send(http_request).await; if res.is_ok() { @@ -162,7 +175,7 @@ where Self { uri: self.uri.clone(), client: self.client.clone(), - max_retries: self.max_retries, + config: self.config.clone(), _req: PhantomData, _res: PhantomData, } diff --git a/crates/mempool_infra/src/component_definitions.rs b/crates/mempool_infra/src/component_definitions.rs index 6f0d0ee0313..c4b63598659 100644 --- a/crates/mempool_infra/src/component_definitions.rs +++ b/crates/mempool_infra/src/component_definitions.rs @@ -1,6 +1,7 @@ +use std::any::type_name; use std::collections::BTreeMap; use std::fmt::Debug; -use std::net::IpAddr; +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use async_trait::async_trait; use papyrus_config::dumping::{ser_param, SerializeConfig}; @@ -8,18 +9,30 @@ use papyrus_config::{ParamPath, ParamPrivacyInput, SerializedParam}; use serde::{Deserialize, Serialize}; use thiserror::Error; use tokio::sync::mpsc::{Receiver, Sender}; -use tracing::error; +use tracing::{error, info}; use validator::Validate; +use crate::errors::ComponentError; + pub const APPLICATION_OCTET_STREAM: &str = "application/octet-stream"; const DEFAULT_CHANNEL_BUFFER_SIZE: usize = 32; const DEFAULT_RETRIES: usize = 3; +const DEFAULT_IDLE_CONNECTIONS: usize = usize::MAX; +const DEFAULT_IDLE_TIMEOUT: u64 = 90; #[async_trait] pub trait ComponentRequestHandler { async fn handle_request(&mut self, request: Request) -> Response; } +#[async_trait] +pub trait ComponentStarter { + async fn start(&mut self) -> Result<(), ComponentError> { + info!("Starting component {} with the default starter.", type_name::()); + Ok(()) + } +} + pub struct ComponentCommunication { tx: Option>, rx: Option>, @@ -80,24 +93,19 @@ impl Default for LocalComponentCommunicationConfig { // The communication configuration of the remote component. #[derive(Clone, Debug, Serialize, Deserialize, Validate, PartialEq)] pub struct RemoteComponentCommunicationConfig { - pub ip: IpAddr, - pub port: u16, + pub socket: SocketAddr, pub retries: usize, + pub idle_connections: usize, + pub idle_timeout: u64, } impl SerializeConfig for RemoteComponentCommunicationConfig { fn dump(&self) -> BTreeMap { BTreeMap::from_iter([ ser_param( - "ip", - &self.ip.to_string(), - "The remote component server ip.", - ParamPrivacyInput::Public, - ), - ser_param( - "port", - &self.port, - "The remote component server port.", + "socket", + &self.socket.to_string(), + "The remote component server socket.", ParamPrivacyInput::Public, ), ser_param( @@ -106,12 +114,30 @@ impl SerializeConfig for RemoteComponentCommunicationConfig { "The max number of retries for sending a message.", ParamPrivacyInput::Public, ), + ser_param( + "idle_connections", + &self.idle_connections, + "The maximum number of idle connections to keep alive.", + ParamPrivacyInput::Public, + ), + ser_param( + "idle_timeout", + &self.idle_timeout, + "The duration in seconds to keep an idle connection open before closing.", + ParamPrivacyInput::Public, + ), ]) } } impl Default for RemoteComponentCommunicationConfig { fn default() -> Self { - Self { ip: "0.0.0.0".parse().unwrap(), port: 8080, retries: DEFAULT_RETRIES } + let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8080); + Self { + socket, + retries: DEFAULT_RETRIES, + idle_connections: DEFAULT_IDLE_CONNECTIONS, + idle_timeout: DEFAULT_IDLE_TIMEOUT, + } } } diff --git a/crates/mempool_infra/src/component_runner.rs b/crates/mempool_infra/src/component_runner.rs deleted file mode 100644 index 8757a2f13a2..00000000000 --- a/crates/mempool_infra/src/component_runner.rs +++ /dev/null @@ -1,12 +0,0 @@ -use async_trait::async_trait; - -use crate::errors::ComponentError; - -/// Interface to start components. -#[async_trait] -pub trait ComponentStarter { - /// Start the component. By default do nothing. - async fn start(&mut self) -> Result<(), ComponentError> { - Ok(()) - } -} diff --git a/crates/mempool_infra/src/component_server/definitions.rs b/crates/mempool_infra/src/component_server/definitions.rs index 73b5de9b3a1..803d6e1ff0b 100644 --- a/crates/mempool_infra/src/component_server/definitions.rs +++ b/crates/mempool_infra/src/component_server/definitions.rs @@ -1,6 +1,7 @@ use std::any::type_name; use async_trait::async_trait; +use thiserror::Error; use tokio::sync::mpsc::Receiver; use tracing::info; @@ -8,7 +9,7 @@ use crate::component_definitions::{ComponentRequestAndResponseSender, ComponentR use crate::errors::ComponentServerError; #[async_trait] -pub trait ComponentServerStarter: Send + Sync { +pub trait ComponentServerStarter { async fn start(&mut self) -> Result<(), ComponentServerError>; } @@ -20,8 +21,11 @@ pub async fn request_response_loop( Request: Send + Sync, Response: Send + Sync, { + info!("Starting server loop for component {}", type_name::()); + // TODO(Tsabary): Make requests and responses implement `std::fmt::Display`, and add the request // to the log. + // TODO(Tsabary): Move this function to be part of the `local_server` module. while let Some(request_and_res_tx) = rx.recv().await { info!("Component {} received request", type_name::()); @@ -32,4 +36,17 @@ pub async fn request_response_loop( tx.send(res).await.expect("Response connection should be open."); } + + info!("Finished server loop for component {}", type_name::()); +} + +// TODO(Tsabary): Create an error module and move this error there. +#[derive(Clone, Debug, Error)] +pub enum ReplaceComponentError { + #[error("Internal error.")] + InternalError, +} + +pub trait ComponentReplacer { + fn replace(&mut self, component: Component) -> Result<(), ReplaceComponentError>; } diff --git a/crates/mempool_infra/src/component_server/empty_component_server.rs b/crates/mempool_infra/src/component_server/empty_component_server.rs index ccdf9929470..951f942809b 100644 --- a/crates/mempool_infra/src/component_server/empty_component_server.rs +++ b/crates/mempool_infra/src/component_server/empty_component_server.rs @@ -1,26 +1,43 @@ +use std::any::type_name; + use async_trait::async_trait; +use tracing::info; -use super::definitions::ComponentServerStarter; -use crate::component_runner::ComponentStarter; +use crate::component_definitions::ComponentStarter; +use crate::component_server::{ComponentReplacer, ComponentServerStarter, ReplaceComponentError}; use crate::errors::ComponentServerError; -pub struct EmptyServer { - component: T, +pub struct WrapperServer { + component: Component, } -impl EmptyServer { - pub fn new(component: T) -> Self { +impl WrapperServer { + pub fn new(component: Component) -> Self { Self { component } } } #[async_trait] -impl ComponentServerStarter for EmptyServer { +impl ComponentServerStarter + for WrapperServer +{ async fn start(&mut self) -> Result<(), ComponentServerError> { - self.component.start().await.map_err(ComponentServerError::ComponentError) + info!("Starting WrapperServer for {}.", type_name::()); + let res = self.component.start().await.map_err(ComponentServerError::ComponentError); + info!("Finished running WrapperServer for {}.", type_name::()); + res } } -pub fn create_empty_server(component: T) -> EmptyServer { - EmptyServer::new(component) +pub fn create_empty_server( + component: Component, +) -> WrapperServer { + WrapperServer::new(component) +} + +impl ComponentReplacer for WrapperServer { + fn replace(&mut self, component: Component) -> Result<(), ReplaceComponentError> { + self.component = component; + Ok(()) + } } diff --git a/crates/mempool_infra/src/component_server/local_component_server.rs b/crates/mempool_infra/src/component_server/local_component_server.rs index 6e27603030f..b05e13d7fd8 100644 --- a/crates/mempool_infra/src/component_server/local_component_server.rs +++ b/crates/mempool_infra/src/component_server/local_component_server.rs @@ -1,12 +1,17 @@ +use std::any::type_name; use std::marker::PhantomData; use async_trait::async_trait; use tokio::sync::mpsc::Receiver; -use tracing::error; +use tracing::{error, info}; -use super::definitions::{request_response_loop, ComponentServerStarter}; -use crate::component_definitions::{ComponentRequestAndResponseSender, ComponentRequestHandler}; -use crate::component_runner::ComponentStarter; +use super::definitions::request_response_loop; +use crate::component_definitions::{ + ComponentRequestAndResponseSender, + ComponentRequestHandler, + ComponentStarter, +}; +use crate::component_server::{ComponentReplacer, ComponentServerStarter, ReplaceComponentError}; use crate::errors::ComponentServerError; /// The `LocalComponentServer` struct is a generic server that handles requests and responses for a @@ -37,28 +42,23 @@ use crate::errors::ComponentServerError; /// use std::sync::mpsc::{channel, Receiver}; /// /// use async_trait::async_trait; -/// use starknet_mempool_infra::component_runner::ComponentStarter; -/// use starknet_mempool_infra::errors::ComponentError; /// use tokio::task; /// /// use crate::starknet_mempool_infra::component_definitions::{ /// ComponentRequestAndResponseSender, /// ComponentRequestHandler, +/// ComponentStarter, /// }; /// use crate::starknet_mempool_infra::component_server::{ /// ComponentServerStarter, /// LocalComponentServer, /// }; +/// use crate::starknet_mempool_infra::errors::ComponentServerError; /// /// // Define your component /// struct MyComponent {} /// -/// #[async_trait] -/// impl ComponentStarter for MyComponent { -/// async fn start(&mut self) -> Result<(), ComponentError> { -/// Ok(()) -/// } -/// } +/// impl ComponentStarter for MyComponent {} /// /// // Define your request and response types /// struct MyRequest { @@ -119,13 +119,15 @@ pub struct BlockingLocalServerType {} impl ComponentServerStarter for LocalComponentServer where - Component: ComponentRequestHandler + ComponentStarter + Send + Sync, + Component: ComponentRequestHandler + Send + Sync + ComponentStarter, Request: Send + Sync, Response: Send + Sync, { async fn start(&mut self) -> Result<(), ComponentServerError> { + info!("Starting LocalComponentServer for {}.", type_name::()); self.component.start().await?; request_response_loop(&mut self.rx, &mut self.component).await; + info!("Finished LocalComponentServer for {}.", type_name::()); Ok(()) } } @@ -162,7 +164,7 @@ where pub struct BaseLocalComponentServer where - Component: ComponentRequestHandler + ComponentStarter, + Component: ComponentRequestHandler, Request: Send + Sync, Response: Send + Sync, { @@ -174,7 +176,7 @@ where impl BaseLocalComponentServer where - Component: ComponentRequestHandler + ComponentStarter, + Component: ComponentRequestHandler, Request: Send + Sync, Response: Send + Sync, { @@ -185,3 +187,16 @@ where Self { component, rx, _local_server_type: PhantomData } } } + +impl ComponentReplacer + for BaseLocalComponentServer +where + Component: ComponentRequestHandler, + Request: Send + Sync, + Response: Send + Sync, +{ + fn replace(&mut self, component: Component) -> Result<(), ReplaceComponentError> { + self.component = component; + Ok(()) + } +} diff --git a/crates/mempool_infra/src/component_server/remote_component_server.rs b/crates/mempool_infra/src/component_server/remote_component_server.rs index b3f84b7a446..cee15022c8e 100644 --- a/crates/mempool_infra/src/component_server/remote_component_server.rs +++ b/crates/mempool_infra/src/component_server/remote_component_server.rs @@ -10,9 +10,9 @@ use hyper::{Body, Request as HyperRequest, Response as HyperResponse, Server, St use serde::de::DeserializeOwned; use serde::Serialize; -use super::definitions::ComponentServerStarter; use crate::component_client::{ClientError, LocalComponentClient}; use crate::component_definitions::{ServerError, APPLICATION_OCTET_STREAM}; +use crate::component_server::ComponentServerStarter; use crate::errors::ComponentServerError; use crate::serde_utils::BincodeSerdeWrapper; @@ -41,26 +41,23 @@ use crate::serde_utils::BincodeSerdeWrapper; /// // Example usage of the RemoteComponentServer /// use async_trait::async_trait; /// use serde::{Deserialize, Serialize}; -/// use starknet_mempool_infra::component_runner::ComponentStarter; -/// use starknet_mempool_infra::errors::ComponentError; /// use tokio::task; /// /// use crate::starknet_mempool_infra::component_client::LocalComponentClient; -/// use crate::starknet_mempool_infra::component_definitions::ComponentRequestHandler; +/// use crate::starknet_mempool_infra::component_definitions::{ +/// ComponentRequestHandler, +/// ComponentStarter, +/// }; /// use crate::starknet_mempool_infra::component_server::{ /// ComponentServerStarter, /// RemoteComponentServer, /// }; +/// use crate::starknet_mempool_infra::errors::ComponentError; /// /// // Define your component /// struct MyComponent {} /// -/// #[async_trait] -/// impl ComponentStarter for MyComponent { -/// async fn start(&mut self) -> Result<(), ComponentError> { -/// Ok(()) -/// } -/// } +/// impl ComponentStarter for MyComponent {} /// /// // Define your request and response types /// #[derive(Serialize, Deserialize, Debug)] diff --git a/crates/mempool_infra/src/errors.rs b/crates/mempool_infra/src/errors.rs index 23891e33665..aa3bd49956e 100644 --- a/crates/mempool_infra/src/errors.rs +++ b/crates/mempool_infra/src/errors.rs @@ -10,12 +10,10 @@ pub enum ComponentError { #[derive(Error, Debug, PartialEq, Clone)] pub enum ComponentServerError { - #[error("Server has already been started.")] - ServerAlreadyStarted, - #[error("Http server has failed: {0}.")] - HttpServerStartError(String), #[error(transparent)] ComponentError(#[from] ComponentError), + #[error("Http server has failed: {0}.")] + HttpServerStartError(String), #[error("Server unexpectedly stopped.")] ServerUnexpectedlyStopped, } diff --git a/crates/mempool_infra/src/lib.rs b/crates/mempool_infra/src/lib.rs index ea1d6c2f033..70db5ee6a2f 100644 --- a/crates/mempool_infra/src/lib.rs +++ b/crates/mempool_infra/src/lib.rs @@ -1,7 +1,10 @@ pub mod component_client; pub mod component_definitions; -pub mod component_runner; pub mod component_server; pub mod errors; pub mod serde_utils; +#[cfg(test)] +pub mod test_utils; +#[cfg(test)] +pub mod tests; pub mod trace_util; diff --git a/crates/mempool_infra/src/test_utils.rs b/crates/mempool_infra/src/test_utils.rs new file mode 100644 index 00000000000..58603bc63a9 --- /dev/null +++ b/crates/mempool_infra/src/test_utils.rs @@ -0,0 +1,19 @@ +use std::net::SocketAddr; + +use tokio::net::TcpListener; + +// TODO(Nadin): Merge this get_available_socket function with the one in the test_integration crate +// and remove the duplicate there. +/// Returns a unique IP address and port for testing purposes. +/// Tests run in parallel, so servers (like RPC or web) running on separate tests must have +/// different ports, otherwise the server will fail with "address already in use". +pub async fn get_available_socket() -> SocketAddr { + // Dynamically select port. + // First, set the port to 0 (dynamic port). + TcpListener::bind("127.0.0.1:0") + .await + .expect("Failed to bind to address") + // Then, resolve to the actual selected port. + .local_addr() + .expect("Failed to get local address") +} diff --git a/crates/mempool_infra/tests/active_local_component_client_server_test.rs b/crates/mempool_infra/src/tests/active_local_component_client_server_test.rs similarity index 97% rename from crates/mempool_infra/tests/active_local_component_client_server_test.rs rename to crates/mempool_infra/src/tests/active_local_component_client_server_test.rs index 4426011fd73..340f9804987 100644 --- a/crates/mempool_infra/tests/active_local_component_client_server_test.rs +++ b/crates/mempool_infra/src/tests/active_local_component_client_server_test.rs @@ -7,12 +7,12 @@ use starknet_mempool_infra::component_client::{ClientError, ClientResult, LocalC use starknet_mempool_infra::component_definitions::{ ComponentRequestAndResponseSender, ComponentRequestHandler, + ComponentStarter, }; -use starknet_mempool_infra::component_runner::ComponentStarter; use starknet_mempool_infra::component_server::{ ComponentServerStarter, - EmptyServer, LocalActiveComponentServer, + WrapperServer, }; use starknet_mempool_infra::errors::ComponentError; use tokio::sync::mpsc::{channel, Sender}; @@ -175,7 +175,7 @@ async fn test_setup_c_d() { let component_d = ComponentD::new(Box::new(c_client), max_iterations, barrier.clone()); let mut component_c_server = LocalActiveComponentServer::new(component_c, rx_c); - let mut component_d_server = EmptyServer::new(component_d); + let mut component_d_server = WrapperServer::new(component_d); task::spawn(async move { let _ = component_c_server.start().await; diff --git a/crates/mempool_infra/tests/local_component_client_server_test.rs b/crates/mempool_infra/src/tests/local_component_client_server_test.rs similarity index 67% rename from crates/mempool_infra/tests/local_component_client_server_test.rs rename to crates/mempool_infra/src/tests/local_component_client_server_test.rs index 14226fd556a..b23b74a925f 100644 --- a/crates/mempool_infra/tests/local_component_client_server_test.rs +++ b/crates/mempool_infra/src/tests/local_component_client_server_test.rs @@ -1,27 +1,26 @@ -mod common; - use async_trait::async_trait; -use common::{ +use starknet_types_core::felt::Felt; +use tokio::sync::mpsc::channel; +use tokio::task; + +use crate::component_client::{ClientError, ClientResult, LocalComponentClient}; +use crate::component_definitions::ComponentRequestAndResponseSender; +use crate::component_server::{ComponentServerStarter, LocalComponentServer}; +use crate::tests::{ + test_a_b_functionality, + ComponentA, ComponentAClientTrait, ComponentARequest, ComponentAResponse, + ComponentB, ComponentBClientTrait, ComponentBRequest, ComponentBResponse, ResultA, ResultB, + ValueA, + ValueB, }; -use starknet_mempool_infra::component_client::{ClientError, ClientResult, LocalComponentClient}; -use starknet_mempool_infra::component_definitions::{ - ComponentRequestAndResponseSender, - ComponentRequestHandler, -}; -use starknet_mempool_infra::component_server::{ComponentServerStarter, LocalComponentServer}; -use starknet_types_core::felt::Felt; -use tokio::sync::mpsc::channel; -use tokio::task; - -use crate::common::{test_a_b_functionality, ComponentA, ComponentB, ValueA, ValueB}; type ComponentAClient = LocalComponentClient; type ComponentBClient = LocalComponentClient; @@ -36,15 +35,6 @@ impl ComponentAClientTrait for LocalComponentClient for ComponentA { - async fn handle_request(&mut self, request: ComponentARequest) -> ComponentAResponse { - match request { - ComponentARequest::AGetValue => ComponentAResponse::AGetValue(self.a_get_value().await), - } - } -} - #[async_trait] impl ComponentBClientTrait for LocalComponentClient { async fn b_get_value(&self) -> ResultB { @@ -67,19 +57,6 @@ impl ComponentBClientTrait for LocalComponentClient for ComponentB { - async fn handle_request(&mut self, request: ComponentBRequest) -> ComponentBResponse { - match request { - ComponentBRequest::BGetValue => ComponentBResponse::BGetValue(self.b_get_value()), - ComponentBRequest::BSetValue(value) => { - self.b_set_value(value); - ComponentBResponse::BSetValue - } - } - } -} - #[tokio::test] async fn test_setup() { let setup_value: ValueB = Felt::from(30); diff --git a/crates/mempool_infra/tests/common/mod.rs b/crates/mempool_infra/src/tests/mod.rs similarity index 70% rename from crates/mempool_infra/tests/common/mod.rs rename to crates/mempool_infra/src/tests/mod.rs index a21ed7df20e..dba31e2ce1c 100644 --- a/crates/mempool_infra/tests/common/mod.rs +++ b/crates/mempool_infra/src/tests/mod.rs @@ -1,12 +1,13 @@ +mod local_component_client_server_test; +mod remote_component_client_server_test; use async_trait::async_trait; use serde::{Deserialize, Serialize}; -use starknet_mempool_infra::component_client::ClientResult; -use starknet_mempool_infra::component_runner::ComponentStarter; use starknet_types_core::felt::Felt; +use crate::component_client::ClientResult; +use crate::component_definitions::{ComponentRequestHandler, ComponentStarter}; pub(crate) type ValueA = Felt; pub(crate) type ValueB = Felt; - pub(crate) type ResultA = ClientResult; pub(crate) type ResultB = ClientResult; @@ -57,7 +58,6 @@ impl ComponentA { } } -#[async_trait] impl ComponentStarter for ComponentA {} pub(crate) struct ComponentB { @@ -79,7 +79,6 @@ impl ComponentB { } } -#[async_trait] impl ComponentStarter for ComponentB {} pub(crate) async fn test_a_b_functionality( @@ -96,3 +95,25 @@ pub(crate) async fn test_a_b_functionality( // Check the new value in component B through client A. assert_eq!(a_client.a_get_value().await.unwrap(), new_expected_value); } + +#[async_trait] +impl ComponentRequestHandler for ComponentA { + async fn handle_request(&mut self, request: ComponentARequest) -> ComponentAResponse { + match request { + ComponentARequest::AGetValue => ComponentAResponse::AGetValue(self.a_get_value().await), + } + } +} + +#[async_trait] +impl ComponentRequestHandler for ComponentB { + async fn handle_request(&mut self, request: ComponentBRequest) -> ComponentBResponse { + match request { + ComponentBRequest::BGetValue => ComponentBResponse::BGetValue(self.b_get_value()), + ComponentBRequest::BSetValue(value) => { + self.b_set_value(value); + ComponentBResponse::BSetValue + } + } + } +} diff --git a/crates/mempool_infra/tests/remote_component_client_server_test.rs b/crates/mempool_infra/src/tests/remote_component_client_server_test.rs similarity index 75% rename from crates/mempool_infra/tests/remote_component_client_server_test.rs rename to crates/mempool_infra/src/tests/remote_component_client_server_test.rs index 6602d942167..f7b58c8a5de 100644 --- a/crates/mempool_infra/tests/remote_component_client_server_test.rs +++ b/crates/mempool_infra/src/tests/remote_component_client_server_test.rs @@ -1,21 +1,8 @@ -mod common; - use std::fmt::Debug; -use std::net::{IpAddr, Ipv6Addr, SocketAddr}; +use std::net::SocketAddr; use std::sync::Arc; use async_trait::async_trait; -use common::{ - ComponentAClientTrait, - ComponentARequest, - ComponentAResponse, - ComponentBClientTrait, - ComponentBRequest, - ComponentBResponse, - ResultA, - ResultB, - ValueA, -}; use hyper::body::to_bytes; use hyper::header::CONTENT_TYPE; use hyper::service::{make_service_fn, service_fn}; @@ -23,44 +10,51 @@ use hyper::{Body, Client, Request, Response, Server, StatusCode, Uri}; use rstest::rstest; use serde::de::DeserializeOwned; use serde::Serialize; -use starknet_mempool_infra::component_client::{ +use starknet_types_core::felt::Felt; +use tokio::sync::mpsc::channel; +use tokio::sync::Mutex; +use tokio::task; + +use crate::component_client::{ ClientError, ClientResult, LocalComponentClient, RemoteComponentClient, }; -use starknet_mempool_infra::component_definitions::{ +use crate::component_definitions::{ ComponentRequestAndResponseSender, - ComponentRequestHandler, + RemoteComponentCommunicationConfig, ServerError, APPLICATION_OCTET_STREAM, }; -use starknet_mempool_infra::component_server::{ +use crate::component_server::{ ComponentServerStarter, LocalComponentServer, RemoteComponentServer, }; -use starknet_mempool_infra::serde_utils::BincodeSerdeWrapper; -use starknet_types_core::felt::Felt; -use tokio::sync::mpsc::channel; -use tokio::sync::Mutex; -use tokio::task; +use crate::serde_utils::BincodeSerdeWrapper; +use crate::test_utils::get_available_socket; +use crate::tests::{ + test_a_b_functionality, + ComponentA, + ComponentAClientTrait, + ComponentARequest, + ComponentAResponse, + ComponentB, + ComponentBClientTrait, + ComponentBRequest, + ComponentBResponse, + ResultA, + ResultB, + ValueA, + ValueB, +}; type ComponentAClient = RemoteComponentClient; type ComponentBClient = RemoteComponentClient; -use crate::common::{test_a_b_functionality, ComponentA, ComponentB, ValueB}; - -const LOCAL_IP: IpAddr = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)); -const MAX_RETRIES: usize = 0; -const A_PORT_TEST_SETUP: u16 = 10000; -const B_PORT_TEST_SETUP: u16 = 10001; -const A_PORT_FAULTY_CLIENT: u16 = 10010; -const B_PORT_FAULTY_CLIENT: u16 = 10011; -const UNCONNECTED_SERVER_PORT: u16 = 10002; -const FAULTY_SERVER_REQ_DESER_PORT: u16 = 10003; -const FAULTY_SERVER_RES_DESER_PORT: u16 = 10004; -const RETRY_REQ_PORT: u16 = 10005; +const MAX_IDLE_CONNECTION: usize = usize::MAX; +const IDLE_TIMEOUT: u64 = 90; const MOCK_SERVER_ERROR: &str = "mock server error"; const ARBITRARY_DATA: &str = "arbitrary data"; // ServerError::RequestDeserializationFailure error message. @@ -78,15 +72,6 @@ impl ComponentAClientTrait for RemoteComponentClient for ComponentA { - async fn handle_request(&mut self, request: ComponentARequest) -> ComponentAResponse { - match request { - ComponentARequest::AGetValue => ComponentAResponse::AGetValue(self.a_get_value().await), - } - } -} - #[async_trait] impl ComponentBClientTrait for RemoteComponentClient { async fn b_get_value(&self) -> ResultB { @@ -108,19 +93,6 @@ impl ComponentBClientTrait for RemoteComponentClient for ComponentB { - async fn handle_request(&mut self, request: ComponentBRequest) -> ComponentBResponse { - match request { - ComponentBRequest::BGetValue => ComponentBResponse::BGetValue(self.b_get_value()), - ComponentBRequest::BSetValue(value) => { - self.b_set_value(value); - ComponentBResponse::BSetValue - } - } - } -} - async fn verify_error( a_remote_client: impl ComponentAClientTrait, expected_error_contained_keywords: &[&str], @@ -139,10 +111,11 @@ fn assert_error_contains_keywords(error: String, expected_error_contained_keywor } } -async fn create_client_and_faulty_server(port: u16, body: T) -> ComponentAClient +async fn create_client_and_faulty_server(body: T) -> ComponentAClient where T: Serialize + DeserializeOwned + Debug + Send + Sync + 'static + Clone, { + let socket = get_available_socket().await; task::spawn(async move { async fn handler( _http_request: Request, @@ -157,7 +130,6 @@ where .unwrap()) } - let socket = SocketAddr::new(LOCAL_IP, port); let make_svc = make_service_fn(|_conn| { let body = body.clone(); async move { Ok::<_, hyper::Error>(service_fn(move |req| handler(req, body.clone()))) } @@ -169,12 +141,16 @@ where // Ensure the server starts running. task::yield_now().await; - ComponentAClient::new(LOCAL_IP, port, MAX_RETRIES) + let config = RemoteComponentCommunicationConfig { socket, ..Default::default() }; + ComponentAClient::new(config) } -async fn setup_for_tests(setup_value: ValueB, a_port: u16, b_port: u16) { - let a_remote_client = ComponentAClient::new(LOCAL_IP, a_port, MAX_RETRIES); - let b_remote_client = ComponentBClient::new(LOCAL_IP, b_port, MAX_RETRIES); +async fn setup_for_tests(setup_value: ValueB, a_socket: SocketAddr, b_socket: SocketAddr) { + let a_config = RemoteComponentCommunicationConfig { socket: a_socket, ..Default::default() }; + let b_config = RemoteComponentCommunicationConfig { socket: b_socket, ..Default::default() }; + + let a_remote_client = ComponentAClient::new(a_config); + let b_remote_client = ComponentBClient::new(b_config); let component_a = ComponentA::new(Box::new(b_remote_client)); let component_b = ComponentB::new(setup_value, Box::new(a_remote_client.clone())); @@ -191,9 +167,9 @@ async fn setup_for_tests(setup_value: ValueB, a_port: u16, b_port: u16) { let mut component_b_local_server = LocalComponentServer::new(component_b, rx_b); let mut component_a_remote_server = - RemoteComponentServer::new(a_local_client, LOCAL_IP, a_port); + RemoteComponentServer::new(a_local_client, a_socket.ip(), a_socket.port()); let mut component_b_remote_server = - RemoteComponentServer::new(b_local_client, LOCAL_IP, b_port); + RemoteComponentServer::new(b_local_client, b_socket.ip(), b_socket.port()); task::spawn(async move { let _ = component_a_local_server.start().await; @@ -217,26 +193,36 @@ async fn setup_for_tests(setup_value: ValueB, a_port: u16, b_port: u16) { #[tokio::test] async fn test_proper_setup() { let setup_value: ValueB = Felt::from(90); - setup_for_tests(setup_value, A_PORT_TEST_SETUP, B_PORT_TEST_SETUP).await; - let a_remote_client = ComponentAClient::new(LOCAL_IP, A_PORT_TEST_SETUP, MAX_RETRIES); - let b_remote_client = ComponentBClient::new(LOCAL_IP, B_PORT_TEST_SETUP, MAX_RETRIES); + let a_socket = get_available_socket().await; + let b_socket = get_available_socket().await; + + setup_for_tests(setup_value, a_socket, b_socket).await; + let a_config = RemoteComponentCommunicationConfig { socket: a_socket, ..Default::default() }; + let b_config = RemoteComponentCommunicationConfig { socket: b_socket, ..Default::default() }; + + let a_remote_client = ComponentAClient::new(a_config); + let b_remote_client = ComponentBClient::new(b_config); test_a_b_functionality(a_remote_client, b_remote_client, setup_value).await; } #[tokio::test] async fn test_faulty_client_setup() { + let a_socket = get_available_socket().await; + let b_socket = get_available_socket().await; // Todo(uriel): Find a better way to pass expected value to the setup // 123 is some arbitrary value, we don't check it anyway. - setup_for_tests(Felt::from(123), A_PORT_FAULTY_CLIENT, B_PORT_FAULTY_CLIENT).await; + setup_for_tests(Felt::from(123), a_socket, b_socket).await; - struct FaultyAClient; + struct FaultyAClient { + socket: SocketAddr, + } #[async_trait] impl ComponentAClientTrait for FaultyAClient { async fn a_get_value(&self) -> ResultA { let component_request = ARBITRARY_DATA.to_string(); let uri: Uri = - format!("http://[{}]:{}/", LOCAL_IP, A_PORT_FAULTY_CLIENT).parse().unwrap(); + format!("http://[{}]:{}/", self.socket.ip(), self.socket.port()).parse().unwrap(); let http_request = Request::post(uri) .header(CONTENT_TYPE, APPLICATION_OCTET_STREAM) .body(Body::from(BincodeSerdeWrapper::new(component_request).to_bincode().unwrap())) @@ -248,7 +234,7 @@ async fn test_faulty_client_setup() { Err(ClientError::ResponseError(status_code, response)) } } - let faulty_a_client = FaultyAClient; + let faulty_a_client = FaultyAClient { socket: a_socket }; let expected_error_contained_keywords = [StatusCode::BAD_REQUEST.as_str(), DESERIALIZE_REQ_ERROR_MESSAGE]; verify_error(faulty_a_client, &expected_error_contained_keywords).await; @@ -256,8 +242,9 @@ async fn test_faulty_client_setup() { #[tokio::test] async fn test_unconnected_server() { - let client = ComponentAClient::new(LOCAL_IP, UNCONNECTED_SERVER_PORT, MAX_RETRIES); - + let socket = get_available_socket().await; + let config = RemoteComponentCommunicationConfig { socket, ..Default::default() }; + let client = ComponentAClient::new(config); let expected_error_contained_keywords = ["Connection refused"]; verify_error(client, &expected_error_contained_keywords).await; } @@ -265,13 +252,12 @@ async fn test_unconnected_server() { #[rstest] #[case::request_deserialization_failure( create_client_and_faulty_server( - FAULTY_SERVER_REQ_DESER_PORT, ServerError::RequestDeserializationFailure(MOCK_SERVER_ERROR.to_string()) ).await, &[StatusCode::BAD_REQUEST.as_str(),DESERIALIZE_REQ_ERROR_MESSAGE, MOCK_SERVER_ERROR], )] #[case::response_deserialization_failure( - create_client_and_faulty_server(FAULTY_SERVER_RES_DESER_PORT,ARBITRARY_DATA.to_string()).await, + create_client_and_faulty_server(ARBITRARY_DATA.to_string()).await, &[DESERIALIZE_RES_ERROR_MESSAGE], )] #[tokio::test] @@ -284,6 +270,7 @@ async fn test_faulty_server( #[tokio::test] async fn test_retry_request() { + let socket = get_available_socket().await; // Spawn a server that responses with OK every other request. task::spawn(async move { let should_send_ok = Arc::new(Mutex::new(false)); @@ -309,7 +296,6 @@ async fn test_retry_request() { Ok(ret) } - let socket = SocketAddr::new(LOCAL_IP, RETRY_REQ_PORT); let make_svc = make_service_fn(|_conn| { let should_send_ok = should_send_ok.clone(); async move { @@ -326,11 +312,23 @@ async fn test_retry_request() { // The initial server state is 'false', hence the first attempt returns an error and // sets the server state to 'true'. The second attempt (first retry) therefore returns a // 'success', while setting the server state to 'false' yet again. - let a_client_retry = ComponentAClient::new(LOCAL_IP, RETRY_REQ_PORT, 1); + let retry_config = RemoteComponentCommunicationConfig { + socket, + retries: 1, + idle_connections: MAX_IDLE_CONNECTION, + idle_timeout: IDLE_TIMEOUT, + }; + let a_client_retry = ComponentAClient::new(retry_config); assert_eq!(a_client_retry.a_get_value().await.unwrap(), VALID_VALUE_A); // The current server state is 'false', hence the first and only attempt returns an error. - let a_client_no_retry = ComponentAClient::new(LOCAL_IP, RETRY_REQ_PORT, 0); + let no_retry_config = RemoteComponentCommunicationConfig { + socket, + retries: 0, + idle_connections: MAX_IDLE_CONNECTION, + idle_timeout: IDLE_TIMEOUT, + }; + let a_client_no_retry = ComponentAClient::new(no_retry_config); let expected_error_contained_keywords = [StatusCode::IM_A_TEAPOT.as_str()]; verify_error(a_client_no_retry.clone(), &expected_error_contained_keywords).await; } diff --git a/crates/mempool_node/build.rs b/crates/mempool_node/build.rs new file mode 100644 index 00000000000..2c3d009e87c --- /dev/null +++ b/crates/mempool_node/build.rs @@ -0,0 +1,3 @@ +// Sets up the environment variable OUT_DIR, which holds the cairo compiler binary. +// The binary is dowloaded to OUT_DIR by the starknet_sierra_compile crate. +fn main() {} diff --git a/crates/mempool_node/src/bin/mempool_dump_config.rs b/crates/mempool_node/src/bin/sequencer_dump_config.rs similarity index 53% rename from crates/mempool_node/src/bin/mempool_dump_config.rs rename to crates/mempool_node/src/bin/sequencer_dump_config.rs index 8a6c0f3c867..d522b9dceeb 100644 --- a/crates/mempool_node/src/bin/mempool_dump_config.rs +++ b/crates/mempool_node/src/bin/sequencer_dump_config.rs @@ -1,10 +1,10 @@ use papyrus_config::dumping::SerializeConfig; -use starknet_mempool_node::config::{MempoolNodeConfig, CONFIG_POINTERS, DEFAULT_CONFIG_PATH}; +use starknet_mempool_node::config::{SequencerNodeConfig, CONFIG_POINTERS, DEFAULT_CONFIG_PATH}; /// Updates the default config file by: -/// cargo run --bin mempool_dump_config -q +/// cargo run --bin sequencer_dump_config -q fn main() { - MempoolNodeConfig::default() + SequencerNodeConfig::default() .dump_to_file(&CONFIG_POINTERS, DEFAULT_CONFIG_PATH) .expect("dump to file error"); } diff --git a/crates/mempool_node/src/communication.rs b/crates/mempool_node/src/communication.rs index 7f197caa8e2..d1ebdad0a0a 100644 --- a/crates/mempool_node/src/communication.rs +++ b/crates/mempool_node/src/communication.rs @@ -23,16 +23,17 @@ use starknet_mempool_types::communication::{ }; use tokio::sync::mpsc::{channel, Receiver, Sender}; -use crate::config::MempoolNodeConfig; +use crate::config::SequencerNodeConfig; -pub struct MempoolNodeCommunication { +pub struct SequencerNodeCommunication { batcher_channel: ComponentCommunication, + /// TODO(Tsabary): remove the redundant consensus_manager_channel. consensus_manager_channel: ComponentCommunication, mempool_channel: ComponentCommunication, gateway_channel: ComponentCommunication, } -impl MempoolNodeCommunication { +impl SequencerNodeCommunication { pub fn take_batcher_tx(&mut self) -> Sender { self.batcher_channel.take_tx() } @@ -70,7 +71,7 @@ impl MempoolNodeCommunication { } } -pub fn create_node_channels() -> MempoolNodeCommunication { +pub fn create_node_channels() -> SequencerNodeCommunication { const DEFAULT_INVOCATIONS_QUEUE_SIZE: usize = 32; let (tx_mempool, rx_mempool) = channel::(DEFAULT_INVOCATIONS_QUEUE_SIZE); @@ -84,7 +85,7 @@ pub fn create_node_channels() -> MempoolNodeCommunication { let (tx_gateway, rx_gateway) = channel::(DEFAULT_INVOCATIONS_QUEUE_SIZE); - MempoolNodeCommunication { + SequencerNodeCommunication { mempool_channel: ComponentCommunication::new(Some(tx_mempool), Some(rx_mempool)), consensus_manager_channel: ComponentCommunication::new( Some(tx_consensus_manager), @@ -95,7 +96,7 @@ pub fn create_node_channels() -> MempoolNodeCommunication { } } -pub struct MempoolNodeClients { +pub struct SequencerNodeClients { batcher_client: Option, consensus_manager_client: Option, mempool_client: Option, @@ -103,7 +104,7 @@ pub struct MempoolNodeClients { // TODO (Lev): Change to Option>. } -impl MempoolNodeClients { +impl SequencerNodeClients { pub fn get_batcher_client(&self) -> Option { self.batcher_client.clone() } @@ -122,9 +123,9 @@ impl MempoolNodeClients { } pub fn create_node_clients( - config: &MempoolNodeConfig, - channels: &mut MempoolNodeCommunication, -) -> MempoolNodeClients { + config: &SequencerNodeConfig, + channels: &mut SequencerNodeCommunication, +) -> SequencerNodeClients { let batcher_client: Option = match config.components.batcher.execute { true => Some(Arc::new(LocalBatcherClientImpl::new(channels.take_batcher_tx()))), false => None, @@ -144,5 +145,10 @@ pub fn create_node_clients( true => Some(Arc::new(LocalGatewayClientImpl::new(channels.take_gateway_tx()))), false => None, }; - MempoolNodeClients { batcher_client, consensus_manager_client, mempool_client, gateway_client } + SequencerNodeClients { + batcher_client, + consensus_manager_client, + mempool_client, + gateway_client, + } } diff --git a/crates/mempool_node/src/components.rs b/crates/mempool_node/src/components.rs index 1aaafbe2bf4..12959788d3b 100644 --- a/crates/mempool_node/src/components.rs +++ b/crates/mempool_node/src/components.rs @@ -4,10 +4,10 @@ use starknet_gateway::gateway::{create_gateway, Gateway}; use starknet_http_server::http_server::{create_http_server, HttpServer}; use starknet_mempool::mempool::Mempool; -use crate::communication::MempoolNodeClients; -use crate::config::MempoolNodeConfig; +use crate::communication::SequencerNodeClients; +use crate::config::SequencerNodeConfig; -pub struct Components { +pub struct SequencerNodeComponents { pub batcher: Option, pub consensus_manager: Option, pub gateway: Option, @@ -15,7 +15,10 @@ pub struct Components { pub mempool: Option, } -pub fn create_components(config: &MempoolNodeConfig, clients: &MempoolNodeClients) -> Components { +pub fn create_node_components( + config: &SequencerNodeConfig, + clients: &SequencerNodeClients, +) -> SequencerNodeComponents { let batcher = if config.components.batcher.execute { let mempool_client = clients.get_mempool_client().expect("Mempool Client should be available"); @@ -57,5 +60,5 @@ pub fn create_components(config: &MempoolNodeConfig, clients: &MempoolNodeClient let mempool = if config.components.mempool.execute { Some(Mempool::empty()) } else { None }; - Components { batcher, consensus_manager, gateway, http_server, mempool } + SequencerNodeComponents { batcher, consensus_manager, gateway, http_server, mempool } } diff --git a/crates/mempool_node/src/config/config_test.rs b/crates/mempool_node/src/config/config_test.rs index f94fe1c6d31..a8f72484a87 100644 --- a/crates/mempool_node/src/config/config_test.rs +++ b/crates/mempool_node/src/config/config_test.rs @@ -17,8 +17,8 @@ use validator::{Validate, ValidationErrors}; use crate::config::{ ComponentConfig, ComponentExecutionConfig, - LocationType, - MempoolNodeConfig, + ComponentExecutionMode, + SequencerNodeConfig, CONFIG_POINTERS, DEFAULT_CONFIG_PATH, }; @@ -46,43 +46,43 @@ fn check_validation_error( } /// Test the validation of the struct ComponentExecutionConfig. -/// The validation validates that location of the component and the local/remote config are at sync. +/// Validates that execution mode of the component and the local/remote config are at sync. #[rstest] #[case( - LocationType::Local, + ComponentExecutionMode::Local, Some(LocalComponentCommunicationConfig::default()), Some(RemoteComponentCommunicationConfig::default()), "Local config and Remote config are mutually exclusive, can't be both active." )] #[case( - LocationType::Local, + ComponentExecutionMode::Local, None, Some(RemoteComponentCommunicationConfig::default()), "Local communication config is missing." )] -#[case(LocationType::Local, None, None, "Local communication config is missing.")] +#[case(ComponentExecutionMode::Local, None, None, "Local communication config is missing.")] #[case( - LocationType::Remote, + ComponentExecutionMode::Remote, Some(LocalComponentCommunicationConfig::default()), Some(RemoteComponentCommunicationConfig::default()), "Local config and Remote config are mutually exclusive, can't be both active." )] #[case( - LocationType::Remote, + ComponentExecutionMode::Remote, Some(LocalComponentCommunicationConfig::default()), None, "Remote communication config is missing." )] -#[case(LocationType::Remote, None, None, "Remote communication config is missing.")] +#[case(ComponentExecutionMode::Remote, None, None, "Remote communication config is missing.")] fn test_invalid_component_execution_config( - #[case] location: LocationType, + #[case] execution_mode: ComponentExecutionMode, #[case] local_config: Option, #[case] remote_config: Option, #[case] expected_error_message: &str, ) { // Initialize an invalid config and check that the validator finds an error. let component_exe_config = ComponentExecutionConfig { - location, + execution_mode, local_config, remote_config, ..ComponentExecutionConfig::default() @@ -95,24 +95,24 @@ fn test_invalid_component_execution_config( } /// Test the validation of the struct ComponentExecutionConfig. -/// The validation validates that location of the component and the local/remote config are at sync. +/// Validates that execution mode of the component and the local/remote config are at sync. #[rstest] -#[case::local(LocationType::Local)] -#[case::remote(LocationType::Remote)] -fn test_valid_component_execution_config(#[case] location: LocationType) { +#[case::local(ComponentExecutionMode::Local)] +#[case::remote(ComponentExecutionMode::Remote)] +fn test_valid_component_execution_config(#[case] execution_mode: ComponentExecutionMode) { // Initialize a valid config and check that the validator returns Ok. - let local_config = if location == LocationType::Local { + let local_config = if execution_mode == ComponentExecutionMode::Local { Some(LocalComponentCommunicationConfig::default()) } else { None }; - let remote_config = if location == LocationType::Remote { + let remote_config = if execution_mode == ComponentExecutionMode::Remote { Some(RemoteComponentCommunicationConfig::default()) } else { None }; let component_exe_config = ComponentExecutionConfig { - location, + execution_mode, local_config, remote_config, ..ComponentExecutionConfig::default() @@ -186,16 +186,16 @@ fn test_valid_components_config( assert_matches!(component_config.validate(), Ok(())); } -/// Test the validation of the struct MempoolNodeConfig and that the default config file is up to +/// Test the validation of the struct SequencerNodeConfig and that the default config file is up to /// date. To update the default config file, run: -/// cargo run --bin mempool_dump_config -q +/// cargo run --bin sequencer_dump_config -q #[test] fn default_config_file_is_up_to_date() { env::set_current_dir(get_absolute_path("")).expect("Couldn't set working dir."); let from_default_config_file: serde_json::Value = serde_json::from_reader(File::open(DEFAULT_CONFIG_PATH).unwrap()).unwrap(); - let default_config = MempoolNodeConfig::default(); + let default_config = SequencerNodeConfig::default(); assert_matches!(default_config.validate(), Ok(())); // Create a temporary file and dump the default config to it. @@ -210,10 +210,12 @@ fn default_config_file_is_up_to_date() { println!( "{}", "Default config file doesn't match the default NodeConfig implementation. Please update \ - it using the mempool_dump_config binary." + it using the sequencer_dump_config binary." .purple() .bold() ); - println!("Diffs shown below (default config file <<>> dump of MempoolNodeConfig::default())."); + println!( + "Diffs shown below (default config file <<>> dump of SequencerNodeConfig::default())." + ); assert_json_eq!(from_default_config_file, from_code) } diff --git a/crates/mempool_node/src/config/mod.rs b/crates/mempool_node/src/config/mod.rs index 8325004bb36..55f86800a0b 100644 --- a/crates/mempool_node/src/config/mod.rs +++ b/crates/mempool_node/src/config/mod.rs @@ -46,30 +46,19 @@ pub static CONFIG_POINTERS: LazyLock = LazyLock::new(|| { // The configuration of the components. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub enum LocationType { +pub enum ComponentExecutionMode { Local, Remote, } // TODO(Lev/Tsabary): When papyrus_config will support it, change to include communication config in // the enum. -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub enum ComponentType { - // A component that perpetually runs upon start up, and does not receive requests from other - // components. Example: an http server that listens to external requests. - Autonomous, - // A component that runs upon receiving a request from another component. It cannot invoke - // itself. Example: a mempool that receives transactions from the gateway. - Reactive, -} - /// The single component configuration. #[derive(Clone, Debug, Serialize, Deserialize, Validate, PartialEq)] #[validate(schema(function = "validate_single_component_config"))] pub struct ComponentExecutionConfig { pub execute: bool, - pub component_type: ComponentType, - pub location: LocationType, + pub execution_mode: ComponentExecutionMode, pub local_config: Option, pub remote_config: Option, } @@ -84,15 +73,9 @@ impl SerializeConfig for ComponentExecutionConfig { ParamPrivacyInput::Public, ), ser_param( - "location", - &self.location, - "The component location.", - ParamPrivacyInput::Public, - ), - ser_param( - "component_type", - &self.component_type, - "The component type.", + "execution_mode", + &self.execution_mode, + "The component execution mode.", ParamPrivacyInput::Public, ), ]); @@ -111,8 +94,7 @@ impl Default for ComponentExecutionConfig { fn default() -> Self { Self { execute: true, - location: LocationType::Local, - component_type: ComponentType::Reactive, + execution_mode: ComponentExecutionMode::Local, local_config: Some(LocalComponentCommunicationConfig::default()), remote_config: None, } @@ -124,8 +106,7 @@ impl ComponentExecutionConfig { pub fn gateway_default_config() -> Self { Self { execute: true, - location: LocationType::Local, - component_type: ComponentType::Autonomous, + execution_mode: ComponentExecutionMode::Local, local_config: Some(LocalComponentCommunicationConfig::default()), remote_config: None, } @@ -137,18 +118,16 @@ impl ComponentExecutionConfig { pub fn http_server_default_config() -> Self { Self { execute: true, - location: LocationType::Local, - component_type: ComponentType::Autonomous, - local_config: Some(LocalComponentCommunicationConfig::default()), - remote_config: None, + execution_mode: ComponentExecutionMode::Remote, + local_config: None, + remote_config: Some(RemoteComponentCommunicationConfig::default()), } } pub fn mempool_default_config() -> Self { Self { execute: true, - location: LocationType::Local, - component_type: ComponentType::Reactive, + execution_mode: ComponentExecutionMode::Local, local_config: Some(LocalComponentCommunicationConfig::default()), remote_config: None, } @@ -157,8 +136,7 @@ impl ComponentExecutionConfig { pub fn batcher_default_config() -> Self { Self { execute: true, - location: LocationType::Local, - component_type: ComponentType::Reactive, + execution_mode: ComponentExecutionMode::Local, local_config: Some(LocalComponentCommunicationConfig::default()), remote_config: None, } @@ -167,8 +145,7 @@ impl ComponentExecutionConfig { pub fn consensus_manager_default_config() -> Self { Self { execute: true, - location: LocationType::Local, - component_type: ComponentType::Reactive, + execution_mode: ComponentExecutionMode::Local, local_config: Some(LocalComponentCommunicationConfig::default()), remote_config: None, } @@ -181,11 +158,11 @@ pub fn validate_single_component_config( let error_message = if component_config.local_config.is_some() && component_config.remote_config.is_some() { "Local config and Remote config are mutually exclusive, can't be both active." - } else if component_config.location == LocationType::Local + } else if component_config.execution_mode == ComponentExecutionMode::Local && component_config.local_config.is_none() { "Local communication config is missing." - } else if component_config.location == LocationType::Remote + } else if component_config.execution_mode == ComponentExecutionMode::Remote && component_config.remote_config.is_none() { "Remote communication config is missing." @@ -233,6 +210,7 @@ impl SerializeConfig for ComponentConfig { append_sub_config_name(self.batcher.dump(), "batcher"), append_sub_config_name(self.consensus_manager.dump(), "consensus_manager"), append_sub_config_name(self.gateway.dump(), "gateway"), + append_sub_config_name(self.http_server.dump(), "http_server"), append_sub_config_name(self.mempool.dump(), "mempool"), ]; @@ -260,7 +238,7 @@ pub fn validate_components_config(components: &ComponentConfig) -> Result<(), Va /// The configurations of the various components of the node. #[derive(Debug, Deserialize, Default, Serialize, Clone, PartialEq, Validate)] -pub struct MempoolNodeConfig { +pub struct SequencerNodeConfig { #[validate] pub components: ComponentConfig, #[validate] @@ -277,7 +255,7 @@ pub struct MempoolNodeConfig { pub compiler_config: SierraToCasmCompilationConfig, } -impl SerializeConfig for MempoolNodeConfig { +impl SerializeConfig for SequencerNodeConfig { fn dump(&self) -> BTreeMap { #[allow(unused_mut)] let mut sub_configs = vec![ @@ -297,7 +275,7 @@ impl SerializeConfig for MempoolNodeConfig { } } -impl MempoolNodeConfig { +impl SequencerNodeConfig { /// Creates a config object. Selects the values from the default file and from resources with /// higher priority. fn load_and_process_config_file( @@ -324,6 +302,8 @@ impl MempoolNodeConfig { } } +// TODO(Tsabary): Rename the cli function. + /// The command line interface of this node. pub fn node_command() -> Command { Command::new("Mempool") diff --git a/crates/mempool_node/src/main.rs b/crates/mempool_node/src/main.rs index 7ab874fd801..8df25d4a70f 100644 --- a/crates/mempool_node/src/main.rs +++ b/crates/mempool_node/src/main.rs @@ -4,16 +4,16 @@ use std::process::exit; use papyrus_config::validators::config_validate; use papyrus_config::ConfigError; use starknet_mempool_infra::trace_util::configure_tracing; -use starknet_mempool_node::config::MempoolNodeConfig; +use starknet_mempool_node::config::SequencerNodeConfig; use starknet_mempool_node::servers::run_component_servers; -use starknet_mempool_node::utils::create_clients_servers_from_config; +use starknet_mempool_node::utils::create_node_modules; use tracing::{error, info}; #[tokio::main] async fn main() -> anyhow::Result<()> { configure_tracing(); - let config = MempoolNodeConfig::load_and_process(args().collect()); + let config = SequencerNodeConfig::load_and_process(args().collect()); if let Err(ConfigError::CommandInput(clap_err)) = config { clap_err.exit(); } @@ -24,7 +24,8 @@ async fn main() -> anyhow::Result<()> { exit(1); } - let (_, servers) = create_clients_servers_from_config(&config); + // Clients are currently unused, but should not be dropped. + let (_clients, servers) = create_node_modules(&config); info!("Starting components!"); run_component_servers(&config, servers).await?; diff --git a/crates/mempool_node/src/servers.rs b/crates/mempool_node/src/servers.rs index 6ce28845079..a5d7faff0a3 100644 --- a/crates/mempool_node/src/servers.rs +++ b/crates/mempool_node/src/servers.rs @@ -4,33 +4,44 @@ use std::pin::Pin; use futures::{Future, FutureExt}; use starknet_batcher::communication::{create_local_batcher_server, LocalBatcherServer}; use starknet_consensus_manager::communication::{ - create_local_consensus_manager_server, - LocalConsensusManagerServer, + create_consensus_manager_server, + ConsensusManagerServer, }; -use starknet_gateway::communication::{create_gateway_server, GatewayServer}; +use starknet_gateway::communication::{create_gateway_server, LocalGatewayServer}; use starknet_http_server::communication::{create_http_server, HttpServer}; -use starknet_mempool::communication::{create_mempool_server, MempoolServer}; +use starknet_mempool::communication::{create_mempool_server, LocalMempoolServer}; use starknet_mempool_infra::component_server::ComponentServerStarter; use starknet_mempool_infra::errors::ComponentServerError; use tracing::error; -use crate::communication::MempoolNodeCommunication; -use crate::components::Components; -use crate::config::MempoolNodeConfig; +use crate::communication::SequencerNodeCommunication; +use crate::components::SequencerNodeComponents; +use crate::config::SequencerNodeConfig; -pub struct Servers { +// Component servers that can run locally. +pub struct LocalServers { pub batcher: Option>, - pub consensus_manager: Option>, - pub gateway: Option>, + pub gateway: Option>, + pub mempool: Option>, +} + +// Component servers that wrap a component without a server. +pub struct WrapperServers { + pub consensus_manager: Option>, pub http_server: Option>, - pub mempool: Option>, } -pub fn create_servers( - config: &MempoolNodeConfig, - communication: &mut MempoolNodeCommunication, - components: Components, -) -> Servers { +/// TODO(Tsabary): make these fields private, currently public to support the outdated e2e test. +pub struct SequencerNodeServers { + pub local_servers: LocalServers, + pub wrapper_servers: WrapperServers, +} + +pub fn create_node_servers( + config: &SequencerNodeConfig, + communication: &mut SequencerNodeCommunication, + components: SequencerNodeComponents, +) -> SequencerNodeServers { let batcher_server = if config.components.batcher.execute { Some(Box::new(create_local_batcher_server( components.batcher.expect("Batcher is not initialized."), @@ -40,9 +51,8 @@ pub fn create_servers( None }; let consensus_manager_server = if config.components.consensus_manager.execute { - Some(Box::new(create_local_consensus_manager_server( + Some(Box::new(create_consensus_manager_server( components.consensus_manager.expect("Consensus Manager is not initialized."), - communication.take_consensus_manager_rx(), ))) } else { None @@ -71,41 +81,53 @@ pub fn create_servers( None }; - Servers { - batcher: batcher_server, - consensus_manager: consensus_manager_server, - gateway: gateway_server, - http_server, - mempool: mempool_server, - } + let local_servers = + LocalServers { batcher: batcher_server, gateway: gateway_server, mempool: mempool_server }; + + let wrapper_servers = + WrapperServers { consensus_manager: consensus_manager_server, http_server }; + + SequencerNodeServers { local_servers, wrapper_servers } } pub async fn run_component_servers( - config: &MempoolNodeConfig, - servers: Servers, + config: &SequencerNodeConfig, + servers: SequencerNodeServers, ) -> anyhow::Result<()> { // Batcher server. - let batcher_future = - get_server_future("Batcher", config.components.batcher.execute, servers.batcher); + let batcher_future = get_server_future( + "Batcher", + config.components.batcher.execute, + servers.local_servers.batcher, + ); // Consensus Manager server. let consensus_manager_future = get_server_future( "Consensus Manager", config.components.consensus_manager.execute, - servers.consensus_manager, + servers.wrapper_servers.consensus_manager, ); // Gateway server. - let gateway_future = - get_server_future("Gateway", config.components.gateway.execute, servers.gateway); + let gateway_future = get_server_future( + "Gateway", + config.components.gateway.execute, + servers.local_servers.gateway, + ); // HttpServer server. - let http_server_future = - get_server_future("HttpServer", config.components.http_server.execute, servers.http_server); + let http_server_future = get_server_future( + "HttpServer", + config.components.http_server.execute, + servers.wrapper_servers.http_server, + ); // Mempool server. - let mempool_future = - get_server_future("Mempool", config.components.mempool.execute, servers.mempool); + let mempool_future = get_server_future( + "Mempool", + config.components.mempool.execute, + servers.local_servers.mempool, + ); // Start servers. let batcher_handle = tokio::spawn(batcher_future); @@ -144,17 +166,11 @@ pub async fn run_component_servers( pub fn get_server_future( name: &str, execute_flag: bool, - server: Option>, + server: Option>, ) -> Pin> + Send>> { - let server_future = match execute_flag { - true => { - let mut server = match server { - Some(server) => server, - _ => panic!("{} component is not initialized.", name), - }; - async move { server.start().await }.boxed() - } - false => pending().boxed(), - }; - server_future + if !execute_flag { + return pending().boxed(); + } + let mut server = server.unwrap_or_else(|| panic!("{} component is not initialized.", name)); + async move { server.start().await }.boxed() } diff --git a/crates/mempool_node/src/utils.rs b/crates/mempool_node/src/utils.rs index 6cece8bae59..91dc13e1225 100644 --- a/crates/mempool_node/src/utils.rs +++ b/crates/mempool_node/src/utils.rs @@ -1,15 +1,15 @@ -use crate::communication::{create_node_channels, create_node_clients, MempoolNodeClients}; -use crate::components::create_components; -use crate::config::MempoolNodeConfig; -use crate::servers::{create_servers, Servers}; +use crate::communication::{create_node_channels, create_node_clients, SequencerNodeClients}; +use crate::components::create_node_components; +use crate::config::SequencerNodeConfig; +use crate::servers::{create_node_servers, SequencerNodeServers}; -pub fn create_clients_servers_from_config( - config: &MempoolNodeConfig, -) -> (MempoolNodeClients, Servers) { +pub fn create_node_modules( + config: &SequencerNodeConfig, +) -> (SequencerNodeClients, SequencerNodeServers) { let mut channels = create_node_channels(); let clients = create_node_clients(config, &mut channels); - let components = create_components(config, &clients); - let servers = create_servers(config, &mut channels, components); + let components = create_node_components(config, &clients); + let servers = create_node_servers(config, &mut channels, components); (clients, servers) } diff --git a/crates/mempool_p2p/src/receiver/mod.rs b/crates/mempool_p2p/src/receiver/mod.rs index ef32b3c1b8e..7b646e2cf20 100644 --- a/crates/mempool_p2p/src/receiver/mod.rs +++ b/crates/mempool_p2p/src/receiver/mod.rs @@ -1,12 +1,5 @@ -use async_trait::async_trait; -use starknet_mempool_infra::component_runner::ComponentStarter; -use starknet_mempool_infra::errors::ComponentError; +use starknet_mempool_infra::component_definitions::ComponentStarter; pub struct MempoolP2pReceiver; -#[async_trait] -impl ComponentStarter for MempoolP2pReceiver { - async fn start(&mut self) -> Result<(), ComponentError> { - unimplemented!() - } -} +impl ComponentStarter for MempoolP2pReceiver {} diff --git a/crates/mempool_p2p_types/Cargo.toml b/crates/mempool_p2p_types/Cargo.toml index 98135132be0..3bb97ae73a8 100644 --- a/crates/mempool_p2p_types/Cargo.toml +++ b/crates/mempool_p2p_types/Cargo.toml @@ -10,7 +10,7 @@ workspace = true [dependencies] async-trait.workspace = true -papyrus_network.workspace = true +papyrus_network_types.workspace = true papyrus_proc_macros.workspace = true serde = { workspace = true, features = ["derive"] } starknet_api.workspace = true diff --git a/crates/mempool_p2p_types/src/communication.rs b/crates/mempool_p2p_types/src/communication.rs index b39cb6c0d16..1e1aac230a7 100644 --- a/crates/mempool_p2p_types/src/communication.rs +++ b/crates/mempool_p2p_types/src/communication.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use async_trait::async_trait; // TODO(Shahak): Create a papyrus_network_types crate and move BroadcastedMessageManager to // there. -use papyrus_network::network_manager::BroadcastedMessageManager; +use papyrus_network_types::network_types::BroadcastedMessageManager; use papyrus_proc_macros::handle_response_variants; use serde::{Deserialize, Serialize}; use starknet_api::rpc_transaction::RpcTransaction; diff --git a/crates/mempool_test_utils/Cargo.toml b/crates/mempool_test_utils/Cargo.toml index 5213e0529f5..1996b75f368 100644 --- a/crates/mempool_test_utils/Cargo.toml +++ b/crates/mempool_test_utils/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true [dependencies] assert_matches.workspace = true blockifier = { workspace = true, features = ["testing"] } +pretty_assertions.workspace = true serde_json.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true diff --git a/crates/mempool_test_utils/src/starknet_api_test_utils.rs b/crates/mempool_test_utils/src/starknet_api_test_utils.rs index e76082f97a2..7eb79c069c4 100644 --- a/crates/mempool_test_utils/src/starknet_api_test_utils.rs +++ b/crates/mempool_test_utils/src/starknet_api_test_utils.rs @@ -1,5 +1,4 @@ use std::cell::RefCell; -use std::collections::HashMap; use std::env; use std::fs::File; use std::path::Path; @@ -9,6 +8,7 @@ use std::sync::OnceLock; use assert_matches::assert_matches; use blockifier::test_utils::contracts::FeatureContract; use blockifier::test_utils::{create_trivial_calldata, CairoVersion}; +use pretty_assertions::assert_ne; use serde_json::to_string_pretty; use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce}; use starknet_api::data_availability::DataAvailabilityMode; @@ -33,7 +33,7 @@ use starknet_api::transaction::{ TransactionVersion, ValidResourceBounds, }; -use starknet_api::{calldata, felt}; +use starknet_api::{calldata, felt, nonce}; use starknet_types_core::felt::Felt; use crate::{ @@ -190,24 +190,27 @@ pub fn invoke_tx(cairo_version: CairoVersion) -> RpcTransaction { pub fn executable_invoke_tx(cairo_version: CairoVersion) -> Transaction { let default_account = FeatureContract::AccountWithoutValidations(cairo_version); - MultiAccountTransactionGenerator::new_for_account_contracts([default_account]) - .account_with_id(0) - .generate_default_executable_invoke() + let mut tx_generator = MultiAccountTransactionGenerator::new(); + tx_generator.register_account(default_account); + tx_generator.account_with_id(0).generate_executable_invoke() } -// TODO(Yael 18/6/2024): Get a final decision from product whether to support Cairo0. -pub fn deploy_account_tx() -> RpcTransaction { - let default_account = FeatureContract::AccountWithoutValidations(CairoVersion::Cairo1); +pub fn generate_deploy_account_with_salt( + account: &FeatureContract, + contract_address_salt: ContractAddressSalt, +) -> RpcTransaction { + let deploy_account_args = deploy_account_tx_args!( + class_hash: account.get_class_hash(), + resource_bounds: test_resource_bounds_mapping(), + contract_address_salt + ); - MultiAccountTransactionGenerator::new_for_account_contracts([default_account]) - .account_with_id(0) - .generate_default_deploy_account() + rpc_deploy_account_tx(deploy_account_args) } // TODO: when moving this to Starknet API crate, move this const into a module alongside // MultiAcconutTransactionGenerator. type AccountId = usize; -type ContractInstanceId = u16; type SharedNonceManager = Rc>; @@ -221,48 +224,46 @@ type SharedNonceManager = Rc>; /// # Example /// /// ``` +/// use blockifier::test_utils::contracts::FeatureContract; +/// use blockifier::test_utils::CairoVersion; /// use mempool_test_utils::starknet_api_test_utils::MultiAccountTransactionGenerator; /// -/// let mut tx_generator = MultiAccountTransactionGenerator::new(2); // Initialize with 2 accounts. -/// let account_0_tx_with_nonce_0 = tx_generator.account_with_id(0).generate_default_invoke(); -/// let account_1_tx_with_nonce_0 = tx_generator.account_with_id(1).generate_default_invoke(); -/// let account_0_tx_with_nonce_1 = tx_generator.account_with_id(0).generate_default_invoke(); +/// let mut tx_generator = MultiAccountTransactionGenerator::new(); +/// let some_account_type = FeatureContract::AccountWithoutValidations(CairoVersion::Cairo1); +/// // Initialize multiple accounts, these can be any account type in `FeatureContract`. +/// tx_generator.register_account_for_flow_test(some_account_type.clone()); +/// tx_generator.register_account_for_flow_test(some_account_type); +/// +/// let account_0_tx_with_nonce_0 = tx_generator.account_with_id(0).generate_invoke_with_tip(1); +/// let account_1_tx_with_nonce_0 = tx_generator.account_with_id(1).generate_invoke_with_tip(3); +/// let account_0_tx_with_nonce_1 = tx_generator.account_with_id(0).generate_invoke_with_tip(1); /// ``` // Note: when moving this to starknet api crate, see if blockifier's // [blockifier::transaction::test_utils::FaultyAccountTxCreatorArgs] can be made to use this. +#[derive(Default)] pub struct MultiAccountTransactionGenerator { // Invariant: coupled with the nonce manager. account_tx_generators: Vec, // Invariant: nonces managed internally thorugh `generate` API of the account transaction // generator. - // Only used by single account transaction generators, but owning it here is preferable over - // only distributing the ownership among the account generators. - _nonce_manager: SharedNonceManager, + nonce_manager: SharedNonceManager, } impl MultiAccountTransactionGenerator { - pub fn new(n_accounts: usize) -> Self { - let default_account_contract = - FeatureContract::AccountWithoutValidations(CairoVersion::Cairo1); - let accounts = std::iter::repeat(default_account_contract).take(n_accounts); - Self::new_for_account_contracts(accounts) + pub fn new() -> Self { + Self::default() } - pub fn new_for_account_contracts(accounts: impl IntoIterator) -> Self { - let mut account_tx_generators = vec![]; - let mut account_type_to_n_instances = HashMap::new(); - let nonce_manager = SharedNonceManager::default(); - for account in accounts { - let n_current_contract = account_type_to_n_instances.entry(account).or_insert(0); - account_tx_generators.push(AccountTransactionGenerator { - account, - contract_instance_id: *n_current_contract, - nonce_manager: nonce_manager.clone(), - }); - *n_current_contract += 1; - } + pub fn register_account(&mut self, account_contract: FeatureContract) -> RpcTransaction { + let new_account_id = self.account_tx_generators.len(); + let (account_tx_generator, default_deploy_account_tx) = AccountTransactionGenerator::new( + new_account_id, + account_contract, + self.nonce_manager.clone(), + ); + self.account_tx_generators.push(account_tx_generator); - Self { account_tx_generators, _nonce_manager: nonce_manager } + default_deploy_account_tx } pub fn account_with_id(&mut self, account_id: AccountId) -> &mut AccountTransactionGenerator { @@ -273,6 +274,17 @@ impl MultiAccountTransactionGenerator { ) }) } + + // TODO(deploy_account_support): once we support deploy account in tests, remove this method and + // only use new_account_default in tests. In particular, deploy account txs must be then sent to + // the GW via the add tx endpoint just like other txs. + pub fn register_account_for_flow_test(&mut self, account_contract: FeatureContract) { + self.register_account(account_contract); + } + + pub fn accounts(&self) -> Vec { + self.account_tx_generators.iter().map(|tx_gen| &tx_gen.account).copied().collect() + } } /// Manages transaction generation for a single account. @@ -282,68 +294,66 @@ impl MultiAccountTransactionGenerator { /// with room for future extensions. /// /// TODO: add more transaction generation methods as needed. +#[derive(Debug)] pub struct AccountTransactionGenerator { - account: FeatureContract, - contract_instance_id: ContractInstanceId, + account: Contract, nonce_manager: SharedNonceManager, } impl AccountTransactionGenerator { /// Generate a valid `RpcTransaction` with default parameters. - pub fn generate_default_invoke(&mut self) -> RpcTransaction { + pub fn generate_invoke_with_tip(&mut self, tip: u64) -> RpcTransaction { + let nonce = self.next_nonce(); + assert_ne!( + nonce, + nonce!(0), + "Cannot invoke on behalf of an undeployed account: the first transaction of every \ + account must be a deploy account transaction." + ); let invoke_args = invoke_tx_args!( + nonce, + tip : Tip(tip), sender_address: self.sender_address(), resource_bounds: test_resource_bounds_mapping(), - nonce: self.next_nonce(), - calldata: create_trivial_calldata(self.test_contract_address()), + calldata: create_trivial_calldata(self.sender_address()), ); rpc_invoke_tx(invoke_args) } - pub fn generate_default_executable_invoke(&mut self) -> Transaction { + pub fn generate_executable_invoke(&mut self) -> Transaction { + let nonce = self.next_nonce(); + assert_ne!( + nonce, + nonce!(0), + "Cannot invoke on behalf of an undeployed account: the first transaction of every \ + account must be a deploy account transaction." + ); + let invoke_args = starknet_api::invoke_tx_args!( sender_address: self.sender_address(), resource_bounds: test_valid_resource_bounds(), - nonce: self.next_nonce(), - calldata: create_trivial_calldata(self.test_contract_address()), - ); - - Transaction::Invoke(starknet_api::test_utils::invoke::executable_invoke_tx(invoke_args)) - } - - pub fn generate_default_deploy_account(&mut self) -> RpcTransaction { - let nonce = self.next_nonce(); - assert_eq!(nonce, Nonce(Felt::ZERO)); - - let deploy_account_args = deploy_account_tx_args!( nonce, - class_hash: self.account.get_class_hash(), - resource_bounds: test_resource_bounds_mapping() + calldata: create_trivial_calldata(self.sender_address()), ); - rpc_deploy_account_tx(deploy_account_args) - } - // TODO: support more contracts, instead of this hardcoded type. - pub fn test_contract_address(&mut self) -> ContractAddress { - let cairo_version = self.account.cairo_version(); - FeatureContract::TestContract(cairo_version).get_instance_address(0) + Transaction::Invoke(starknet_api::test_utils::invoke::executable_invoke_tx(invoke_args)) } /// Generates an `RpcTransaction` with fully custom parameters. /// /// Caller must manually handle bumping nonce and fetching the correct sender address via /// [AccountTransactionGenerator::next_nonce] and [AccountTransactionGenerator::sender_address]. - /// See [AccountTransactionGenerator::generate_default_invoke] to have these filled up by + /// See [AccountTransactionGenerator::generate_invoke_with_tip] to have these filled up by /// default. /// /// Note: This is a best effort attempt to make the API more useful; amend or add new methods /// as needed. - pub fn generate_raw(&mut self, invoke_tx_args: InvokeTxArgs) -> RpcTransaction { + pub fn generate_raw_invoke(&mut self, invoke_tx_args: InvokeTxArgs) -> RpcTransaction { rpc_invoke_tx(invoke_tx_args) } pub fn sender_address(&mut self) -> ContractAddress { - self.account.get_instance_address(self.contract_instance_id) + self.account.sender_address } /// Retrieves the nonce for the current account, and __increments__ it internally. @@ -351,74 +361,78 @@ impl AccountTransactionGenerator { let sender_address = self.sender_address(); self.nonce_manager.borrow_mut().next(sender_address) } + + /// Private constructor, since only the multi-account transaction generator should create this + /// struct. + // TODO: add a version that doesn't rely on the default deploy account constructor, but takes + // deploy account args. + fn new( + account_id: usize, + account: FeatureContract, + nonce_manager: SharedNonceManager, + ) -> (Self, RpcTransaction) { + let contract_address_salt = ContractAddressSalt(account_id.into()); + // A deploy account transaction must be created now in order to affix an address to it. + // If this doesn't happen now it'll be difficult to fund the account during test setup. + let default_deploy_account_tx = + generate_deploy_account_with_salt(&account, contract_address_salt); + + let mut account_tx_generator = Self { + account: Contract::new_for_account(account, &default_deploy_account_tx), + nonce_manager, + }; + // Bump the account nonce after transaction creation. + account_tx_generator.next_nonce(); + + (account_tx_generator, default_deploy_account_tx) + } } -/// Adds state to the feature contract struct, so that its _account_ variants can generate a single -/// address, thus allowing future transactions generated for the account to share the same address. -#[derive(Debug, Clone)] -pub struct FeatureAccount { - pub class_hash: ClassHash, - pub account: FeatureContract, - deployment_state: InitializationState, +/// Extends (account) feature contracts with a fixed sender address. +/// The address is calculated from a deploy account transaction and cached. +// Note: feature contracts have their own address generating method, but it a mocked address and is +// not related to an actual deploy account transaction, which is the way real account addresses are +// calculated. +#[derive(Clone, Copy, Debug)] +pub struct Contract { + pub contract: FeatureContract, + pub sender_address: ContractAddress, } -impl FeatureAccount { - pub fn new(account: FeatureContract) -> Self { - assert_matches!( - account, - FeatureContract::AccountWithLongValidate(_) - | FeatureContract::AccountWithoutValidations(_) - | FeatureContract::FaultyAccount(_), - "{account:?} is not an account" - ); +impl Contract { + pub fn class_hash(&self) -> ClassHash { + self.contract.get_class_hash() + } - Self { - class_hash: account.get_class_hash(), - account, - deployment_state: InitializationState::default(), - } + pub fn cairo_version(&self) -> CairoVersion { + self.contract.cairo_version() } - pub fn build(&mut self, deploy_account_tx: &RpcTransaction) { + pub fn raw_class(&self) -> String { + self.contract.get_raw_class() + } + + fn new_for_account(account: FeatureContract, deploy_account_tx: &RpcTransaction) -> Self { assert_matches!( deploy_account_tx, RpcTransaction::DeployAccount(_), "An account must be initialized with a deploy account transaction" ); + assert_matches!( + account, + FeatureContract::AccountWithLongValidate(_) + | FeatureContract::AccountWithoutValidations(_) + | FeatureContract::FaultyAccount(_), + "{account:?} is not an account" + ); - match self.deployment_state { - InitializationState::Uninitialized => { - let address = deploy_account_tx.calculate_sender_address().unwrap(); - self.deployment_state = InitializationState::Initialized(address); - } - InitializationState::Initialized(_) => panic!("Account is already initialized"), - } - } - - pub fn sender_address(&self) -> ContractAddress { - match self.deployment_state { - InitializationState::Initialized(address) => address, - InitializationState::Uninitialized => panic!("Uninitialized account"), - } - } - - // Use for special case testing accounts that don't have an explicit deploy account transaction. - pub fn new_with_custom_address(account: FeatureContract, address: ContractAddress) -> Self { Self { - account, - deployment_state: InitializationState::Initialized(address), - class_hash: account.get_class_hash(), + contract: account, + sender_address: deploy_account_tx.calculate_sender_address().unwrap(), } } } -#[derive(Clone, Debug, Default)] -enum InitializationState { - #[default] - Uninitialized, - Initialized(ContractAddress), -} - // TODO(Ayelet, 28/5/2025): Try unifying the macros. // TODO(Ayelet, 28/5/2025): Consider moving the macros StarkNet API. #[macro_export] diff --git a/crates/mempool_types/Cargo.toml b/crates/mempool_types/Cargo.toml index eb8c0d1beba..b30c4f1da73 100644 --- a/crates/mempool_types/Cargo.toml +++ b/crates/mempool_types/Cargo.toml @@ -11,7 +11,7 @@ workspace = true [dependencies] async-trait.workspace = true mockall.workspace = true -papyrus_network.workspace = true +papyrus_network_types.workspace = true papyrus_proc_macros.workspace = true serde = { workspace = true, features = ["derive"] } starknet_api.workspace = true diff --git a/crates/mempool_types/src/communication.rs b/crates/mempool_types/src/communication.rs index 07b23d45d39..56d89c2169e 100644 --- a/crates/mempool_types/src/communication.rs +++ b/crates/mempool_types/src/communication.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use async_trait::async_trait; use mockall::predicate::*; use mockall::*; -use papyrus_network::network_manager::BroadcastedMessageManager; +use papyrus_network_types::network_types::BroadcastedMessageManager; use papyrus_proc_macros::handle_response_variants; use serde::{Deserialize, Serialize}; use starknet_api::executable_transaction::Transaction; @@ -16,7 +16,7 @@ use starknet_mempool_infra::component_definitions::ComponentRequestAndResponseSe use thiserror::Error; use crate::errors::MempoolError; -use crate::mempool_types::MempoolInput; +use crate::mempool_types::{AddTransactionArgs, CommitBlockArgs}; pub type LocalMempoolClientImpl = LocalComponentClient; pub type RemoteMempoolClientImpl = RemoteComponentClient; @@ -26,10 +26,10 @@ pub type MempoolRequestAndResponseSender = ComponentRequestAndResponseSender; pub type SharedMempoolClient = Arc; -#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)] -pub struct MempoolWrapperInput { - pub mempool_input: MempoolInput, - pub message_metadata: Option, +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct AddTransactionArgsWrapper { + pub args: AddTransactionArgs, + pub p2p_message_metadata: Option, } /// Serves as the mempool's shared interface. Requires `Send + Sync` to allow transferring and @@ -39,19 +39,22 @@ pub struct MempoolWrapperInput { pub trait MempoolClient: Send + Sync { // TODO: Add Option as an argument for add_transaction // TODO: Rename tx to transaction - async fn add_tx(&self, mempool_input: MempoolWrapperInput) -> MempoolClientResult<()>; + async fn add_tx(&self, args: AddTransactionArgsWrapper) -> MempoolClientResult<()>; + async fn commit_block(&self, args: CommitBlockArgs) -> MempoolClientResult<()>; async fn get_txs(&self, n_txs: usize) -> MempoolClientResult>; } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub enum MempoolRequest { - AddTransaction(MempoolWrapperInput), + AddTransaction(AddTransactionArgsWrapper), + CommitBlock(CommitBlockArgs), GetTransactions(usize), } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub enum MempoolResponse { AddTransaction(MempoolResult<()>), + CommitBlock(MempoolResult<()>), GetTransactions(MempoolResult>), } @@ -65,12 +68,18 @@ pub enum MempoolClientError { #[async_trait] impl MempoolClient for LocalMempoolClientImpl { - async fn add_tx(&self, mempool_wrapper_input: MempoolWrapperInput) -> MempoolClientResult<()> { - let request = MempoolRequest::AddTransaction(mempool_wrapper_input); + async fn add_tx(&self, args: AddTransactionArgsWrapper) -> MempoolClientResult<()> { + let request = MempoolRequest::AddTransaction(args); let response = self.send(request).await; handle_response_variants!(MempoolResponse, AddTransaction, MempoolClientError, MempoolError) } + async fn commit_block(&self, args: CommitBlockArgs) -> MempoolClientResult<()> { + let request = MempoolRequest::CommitBlock(args); + let response = self.send(request).await; + handle_response_variants!(MempoolResponse, CommitBlock, MempoolClientError, MempoolError) + } + async fn get_txs(&self, n_txs: usize) -> MempoolClientResult> { let request = MempoolRequest::GetTransactions(n_txs); let response = self.send(request).await; @@ -85,12 +94,18 @@ impl MempoolClient for LocalMempoolClientImpl { #[async_trait] impl MempoolClient for RemoteMempoolClientImpl { - async fn add_tx(&self, mempool_wrapper_input: MempoolWrapperInput) -> MempoolClientResult<()> { - let request = MempoolRequest::AddTransaction(mempool_wrapper_input); + async fn add_tx(&self, args: AddTransactionArgsWrapper) -> MempoolClientResult<()> { + let request = MempoolRequest::AddTransaction(args); let response = self.send(request).await?; handle_response_variants!(MempoolResponse, AddTransaction, MempoolClientError, MempoolError) } + async fn commit_block(&self, args: CommitBlockArgs) -> MempoolClientResult<()> { + let request = MempoolRequest::CommitBlock(args); + let response = self.send(request).await?; + handle_response_variants!(MempoolResponse, CommitBlock, MempoolClientError, MempoolError) + } + async fn get_txs(&self, n_txs: usize) -> MempoolClientResult> { let request = MempoolRequest::GetTransactions(n_txs); let response = self.send(request).await?; diff --git a/crates/mempool_types/src/mempool_types.rs b/crates/mempool_types/src/mempool_types.rs index 2ba58df8ddc..49d67790051 100644 --- a/crates/mempool_types/src/mempool_types.rs +++ b/crates/mempool_types/src/mempool_types.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use serde::{Deserialize, Serialize}; use starknet_api::core::{ContractAddress, Nonce}; use starknet_api::executable_transaction::Transaction; @@ -6,15 +8,20 @@ use crate::errors::MempoolError; #[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)] pub struct AccountState { - // TODO(Ayelet): Consider removing this field as it is duplicated in ThinTransaction. - pub sender_address: ContractAddress, + // TODO(Ayelet): Consider removing this field as it is duplicated in Transaction. + pub address: ContractAddress, pub nonce: Nonce, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct MempoolInput { +pub struct AddTransactionArgs { pub tx: Transaction, pub account_state: AccountState, } +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct CommitBlockArgs { + pub nonces: HashMap, +} + pub type MempoolResult = Result; diff --git a/crates/native_blockifier/src/lib.rs b/crates/native_blockifier/src/lib.rs index 8ce52197ed3..082826e7459 100644 --- a/crates/native_blockifier/src/lib.rs +++ b/crates/native_blockifier/src/lib.rs @@ -20,6 +20,7 @@ pub mod state_readers; pub mod storage; pub mod test_utils; +use blockifier::versioned_constants::StarknetVersion; use errors::{add_py_exceptions, UndeclaredClassHashError}; use py_block_executor::PyBlockExecutor; use py_objects::PyExecutionResources; @@ -51,6 +52,7 @@ fn native_blockifier(py: Python<'_>, py_module: &PyModule) -> PyResult<()> { add_py_exceptions(py, py_module)?; py_module.add_function(wrap_pyfunction!(blockifier_version, py)?)?; + py_module.add_function(wrap_pyfunction!(starknet_version, py)?)?; // TODO(Dori, 1/4/2023): If and when supported in the Python build environment, gate this code // with #[cfg(test)]. @@ -74,3 +76,9 @@ fn native_blockifier(py: Python<'_>, py_module: &PyModule) -> PyResult<()> { pub fn blockifier_version() -> PyResult { Ok(env!("CARGO_PKG_VERSION").to_string()) } + +/// Returns the latest Starknet version for versioned constants. +#[pyfunction] +pub fn starknet_version() -> PyResult { + Ok(StarknetVersion::latest().into()) +} diff --git a/crates/native_blockifier/src/py_block_executor.rs b/crates/native_blockifier/src/py_block_executor.rs index bd6eb608f18..0b9a96c5520 100644 --- a/crates/native_blockifier/src/py_block_executor.rs +++ b/crates/native_blockifier/src/py_block_executor.rs @@ -93,17 +93,17 @@ impl ThinTransactionExecutionInfo { resources.extend(HashMap::from([ ( abi_constants::L1_GAS_USAGE.to_string(), - usize_from_u128(l1_gas) + usize_from_u128(l1_gas.0) .expect("This conversion should not fail as the value is a converted usize."), ), ( abi_constants::BLOB_GAS_USAGE.to_string(), - usize_from_u128(l1_data_gas) + usize_from_u128(l1_data_gas.0) .expect("This conversion should not fail as the value is a converted usize."), ), ( abi_constants::L2_GAS_USAGE.to_string(), - usize_from_u128(l2_gas) + usize_from_u128(l2_gas.0) .expect("This conversion should not fail as the value is a converted usize."), ), ])); diff --git a/crates/native_blockifier/src/py_transaction.rs b/crates/native_blockifier/src/py_transaction.rs index 708c8499b88..e6886685e8f 100644 --- a/crates/native_blockifier/src/py_transaction.rs +++ b/crates/native_blockifier/src/py_transaction.rs @@ -121,7 +121,7 @@ pub fn py_account_tx( tx: &PyAny, optional_py_class_info: Option, ) -> NativeBlockifierResult { - let Transaction::AccountTransaction(account_tx) = py_tx(tx, optional_py_class_info)? else { + let Transaction::Account(account_tx) = py_tx(tx, optional_py_class_info)? else { panic!("Not an account transaction."); }; diff --git a/crates/papyrus_execution/src/execution_test.rs b/crates/papyrus_execution/src/execution_test.rs index 9dde083e0c2..f3d2605bc9d 100644 --- a/crates/papyrus_execution/src/execution_test.rs +++ b/crates/papyrus_execution/src/execution_test.rs @@ -6,7 +6,7 @@ use assert_matches::assert_matches; use blockifier::abi::abi_utils::get_storage_var_address; use blockifier::execution::call_info::Retdata; use blockifier::execution::errors::ConstructorEntryPointExecutionError; -use blockifier::execution::stack_trace::gen_transaction_execution_error_trace; +use blockifier::execution::stack_trace::gen_tx_execution_error_trace; use blockifier::transaction::errors::TransactionExecutionError as BlockifierTransactionExecutionError; use indexmap::indexmap; use papyrus_storage::test_utils::get_test_storage; @@ -180,7 +180,7 @@ fn estimate_fee_invoke() { let fees = estimate_fees(tx).expect("Fee estimation should succeed."); for fee in fees { assert_ne!(fee.overall_fee, Fee(0)); - assert_eq!(fee.gas_price, GAS_PRICE.price_in_wei); + assert_eq!(fee.l1_gas_price, GAS_PRICE.price_in_wei); } } @@ -191,7 +191,7 @@ fn estimate_fee_declare_deprecated_class() { let fees = estimate_fees(tx).expect("Fee estimation should succeed."); for fee in fees { assert_ne!(fee.overall_fee, Fee(0)); - assert_eq!(fee.gas_price, GAS_PRICE.price_in_wei); + assert_eq!(fee.l1_gas_price, GAS_PRICE.price_in_wei); } } @@ -202,7 +202,7 @@ fn estimate_fee_declare_class() { let fees = estimate_fees(tx).expect("Fee estimation should succeed."); for fee in fees { assert_ne!(fee.overall_fee, Fee(0)); - assert_eq!(fee.gas_price, GAS_PRICE.price_in_wei); + assert_eq!(fee.l1_gas_price, GAS_PRICE.price_in_wei); } } @@ -213,7 +213,7 @@ fn estimate_fee_deploy_account() { let fees = estimate_fees(tx).expect("Fee estimation should succeed."); for fee in fees { assert_ne!(fee.overall_fee, Fee(0)); - assert_eq!(fee.gas_price, GAS_PRICE.price_in_wei); + assert_eq!(fee.l1_gas_price, GAS_PRICE.price_in_wei); } } @@ -229,7 +229,7 @@ fn estimate_fee_combination() { let fees = estimate_fees(txs).expect("Fee estimation should succeed."); for fee in fees { assert_ne!(fee.overall_fee, Fee(0)); - assert_eq!(fee.gas_price, GAS_PRICE.price_in_wei); + assert_eq!(fee.l1_gas_price, GAS_PRICE.price_in_wei); } } @@ -330,7 +330,7 @@ fn simulate_invoke() { fee_transfer_invocation: Some(_), } ); - assert_eq!(charge_fee.fee_estimation.gas_price, GAS_PRICE.price_in_wei); + assert_eq!(charge_fee.fee_estimation.l1_gas_price, GAS_PRICE.price_in_wei); assert_eq!(exec_only_trace.execute_invocation, charge_fee_trace.execute_invocation); @@ -817,7 +817,7 @@ fn blockifier_error_mapping() { let expected = format!( "Transaction execution has failed:\n{}", // TODO: consider adding ErrorStack display instead. - String::from(gen_transaction_execution_error_trace(&blockifier_err)) + String::from(gen_tx_execution_error_trace(&blockifier_err)) ); let err = ExecutionError::from((0, blockifier_err)); let ExecutionError::TransactionExecutionError { transaction_index, execution_error } = err @@ -836,7 +836,7 @@ fn blockifier_error_mapping() { }; let expected = format!( "Transaction validation has failed:\n{}", - String::from(gen_transaction_execution_error_trace(&blockifier_err)) + String::from(gen_tx_execution_error_trace(&blockifier_err)) ); let err = ExecutionError::from((0, blockifier_err)); let ExecutionError::TransactionExecutionError { transaction_index, execution_error } = err diff --git a/crates/papyrus_execution/src/lib.rs b/crates/papyrus_execution/src/lib.rs index 64722d0d3bc..b4244d3d2e4 100644 --- a/crates/papyrus_execution/src/lib.rs +++ b/crates/papyrus_execution/src/lib.rs @@ -91,9 +91,11 @@ use crate::objects::{tx_execution_output_to_fee_estimation, FeeEstimation, Pendi const STARKNET_VERSION_O_13_0: &str = "0.13.0"; const STARKNET_VERSION_O_13_1: &str = "0.13.1"; const STARKNET_VERSION_O_13_2: &str = "0.13.2"; -const STRK_FEE_CONTRACT_ADDRESS: &str = +/// The address of the STRK fee contract on Starknet. +pub const STRK_FEE_CONTRACT_ADDRESS: &str = "0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d"; -const ETH_FEE_CONTRACT_ADDRESS: &str = +/// The address of the ETH fee contract on Starknet. +pub const ETH_FEE_CONTRACT_ADDRESS: &str = "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"; const DEFAULT_INITIAL_GAS_COST: u64 = 10000000000; @@ -872,7 +874,7 @@ fn get_versioned_constants( } else if version == STARKNET_VERSION_O_13_2 { BlockifierStarknetVersion::V0_13_2 } else { - BlockifierStarknetVersion::Latest + BlockifierStarknetVersion::V0_13_3 }; VersionedConstants::get(blockifier_starknet_version) } diff --git a/crates/papyrus_execution/src/objects.rs b/crates/papyrus_execution/src/objects.rs index 86251b60c71..25caedbd1a0 100644 --- a/crates/papyrus_execution/src/objects.rs +++ b/crates/papyrus_execution/src/objects.rs @@ -93,11 +93,11 @@ pub struct FeeEstimation { /// Gas consumed by this transaction. This includes gas for DA in calldata mode. pub gas_consumed: Felt, /// The gas price for execution and calldata DA. - pub gas_price: GasPrice, + pub l1_gas_price: GasPrice, /// Gas consumed by DA in blob mode. pub data_gas_consumed: Felt, /// The gas price for DA blob. - pub data_gas_price: GasPrice, + pub l1_data_gas_price: GasPrice, // TODO(Tzahi): Add l2_gas_consumed. Verify overall_fee estimation of l1_gas_price only is // close enough (as there are roundings) to the fee of both l1_gas_price and l2_gas_price. /// The L2 gas price for execution. @@ -175,10 +175,10 @@ pub(crate) fn tx_execution_output_to_fee_estimation( let gas_vector = tx_execution_output.execution_info.receipt.gas; Ok(FeeEstimation { - gas_consumed: gas_vector.l1_gas.into(), - gas_price: l1_gas_price, - data_gas_consumed: gas_vector.l1_data_gas.into(), - data_gas_price: l1_data_gas_price, + gas_consumed: gas_vector.l1_gas.0.into(), + l1_gas_price, + data_gas_consumed: gas_vector.l1_data_gas.0.into(), + l1_data_gas_price, l2_gas_price, overall_fee: tx_execution_output.execution_info.receipt.fee, unit: tx_execution_output.price_unit, @@ -397,9 +397,10 @@ fn vm_resources_to_execution_resources( builtin_instance_counter, memory_holes: vm_resources.n_memory_holes as u64, da_gas_consumed: StarknetApiGasVector { - l1_gas: l1_gas.try_into().map_err(|_| ExecutionError::GasConsumedOutOfRange)?, - l2_gas: l2_gas.try_into().map_err(|_| ExecutionError::GasConsumedOutOfRange)?, + l1_gas: l1_gas.0.try_into().map_err(|_| ExecutionError::GasConsumedOutOfRange)?, + l2_gas: l2_gas.0.try_into().map_err(|_| ExecutionError::GasConsumedOutOfRange)?, l1_data_gas: l1_data_gas + .0 .try_into() .map_err(|_| ExecutionError::GasConsumedOutOfRange)?, }, diff --git a/crates/papyrus_execution/src/testing_instances.rs b/crates/papyrus_execution/src/testing_instances.rs index 667bccd9709..218eb1185ad 100644 --- a/crates/papyrus_execution/src/testing_instances.rs +++ b/crates/papyrus_execution/src/testing_instances.rs @@ -66,9 +66,9 @@ auto_impl_get_test_instance! { } pub struct FeeEstimation { pub gas_consumed: Felt, - pub gas_price: GasPrice, + pub l1_gas_price: GasPrice, pub data_gas_consumed: Felt, - pub data_gas_price: GasPrice, + pub l1_data_gas_price: GasPrice, pub l2_gas_price: GasPrice, pub overall_fee: Fee, pub unit: PriceUnit, diff --git a/crates/papyrus_network/Cargo.toml b/crates/papyrus_network/Cargo.toml index 072972e7bd4..3b791b29b15 100644 --- a/crates/papyrus_network/Cargo.toml +++ b/crates/papyrus_network/Cargo.toml @@ -10,6 +10,7 @@ testing = [] [dependencies] async-stream.workspace = true +async-trait.workspace = true bytes.workspace = true derive_more.workspace = true futures.workspace = true @@ -30,6 +31,7 @@ libp2p = { workspace = true, features = [ metrics.workspace = true papyrus_common.workspace = true papyrus_config.workspace = true +papyrus_network_types.workspace = true replace_with.workspace = true serde = { workspace = true, features = ["derive"] } starknet_api.workspace = true diff --git a/crates/papyrus_network/src/e2e_broadcast_test.rs b/crates/papyrus_network/src/e2e_broadcast_test.rs index 98ef58dca8b..85c8276c465 100644 --- a/crates/papyrus_network/src/e2e_broadcast_test.rs +++ b/crates/papyrus_network/src/e2e_broadcast_test.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use futures::{FutureExt, SinkExt, StreamExt}; +use futures::{FutureExt, StreamExt}; use libp2p::core::multiaddr::Protocol; use libp2p::swarm::SwarmEvent; use libp2p::{Multiaddr, Swarm}; @@ -9,7 +9,7 @@ use starknet_api::core::ChainId; use crate::gossipsub_impl::Topic; use crate::mixed_behaviour::MixedBehaviour; -use crate::network_manager::GenericNetworkManager; +use crate::network_manager::{BroadcastTopicClientTrait, GenericNetworkManager}; use crate::sqmr; use crate::sqmr::Bytes; @@ -109,8 +109,8 @@ async fn broadcast_subscriber_end_to_end_test() { subscriber_channels2_1.broadcasted_messages_receiver; let mut broadcast_client2_2 = subscriber_channels2_2.broadcasted_messages_receiver; - subscriber_channels1_1.messages_to_broadcast_sender.send(number1).await.unwrap(); - subscriber_channels1_2.messages_to_broadcast_sender.send(number2).await.unwrap(); + subscriber_channels1_1.broadcast_topic_client.broadcast_message(number1).await.unwrap(); + subscriber_channels1_2.broadcast_topic_client.broadcast_message(number2).await.unwrap(); let (received_number1, _report_callback) = broadcast_client2_1.next().await.unwrap(); let (received_number2, _report_callback) = diff --git a/crates/papyrus_network/src/lib.rs b/crates/papyrus_network/src/lib.rs index 5c0b33a7899..ae72a9612ae 100644 --- a/crates/papyrus_network/src/lib.rs +++ b/crates/papyrus_network/src/lib.rs @@ -13,7 +13,7 @@ mod peer_manager; mod sqmr; #[cfg(test)] mod test_utils; -mod utils; +pub mod utils; use std::collections::BTreeMap; use std::time::Duration; diff --git a/crates/papyrus_network/src/network_manager/mod.rs b/crates/papyrus_network/src/network_manager/mod.rs index dea30fe9ad5..d6c537e6414 100644 --- a/crates/papyrus_network/src/network_manager/mod.rs +++ b/crates/papyrus_network/src/network_manager/mod.rs @@ -9,6 +9,7 @@ use std::collections::HashMap; use std::pin::Pin; use std::task::{Context, Poll}; +use async_trait::async_trait; use futures::channel::mpsc::{Receiver, SendError, Sender}; use futures::channel::oneshot; use futures::future::{ready, BoxFuture, Ready}; @@ -20,7 +21,7 @@ use libp2p::swarm::SwarmEvent; use libp2p::{Multiaddr, PeerId, StreamProtocol, Swarm}; use metrics::gauge; use papyrus_common::metrics as papyrus_metrics; -use serde::{Deserialize, Serialize}; +use papyrus_network_types::network_types::{BroadcastedMessageManager, OpaquePeerId}; use sqmr::Bytes; use tracing::{debug, error, info, trace, warn}; @@ -221,26 +222,33 @@ impl GenericNetworkManager { panic!("Topic '{}' has already been registered.", topic); } + let broadcasted_messages_fn: BroadcastReceivedMessagesConverterFn = + |(x, broadcasted_message_manager)| (T::try_from(x), broadcasted_message_manager); + let broadcasted_messages_receiver = + broadcasted_messages_receiver.map(broadcasted_messages_fn); + let messages_to_broadcast_fn: fn(T) -> Ready> = |x| ready(Ok(Bytes::from(x))); let messages_to_broadcast_sender = messages_to_broadcast_sender.with(messages_to_broadcast_fn); - let broadcasted_messages_fn: BroadcastReceivedMessagesConverterFn = - |(x, broadcasted_message_manager)| (T::try_from(x), broadcasted_message_manager); - let broadcasted_messages_receiver = - broadcasted_messages_receiver.map(broadcasted_messages_fn); + let reported_messages_fn: fn( + BroadcastedMessageManager, + ) -> Ready> = |broadcasted_message_manager| { + ready(Ok(broadcasted_message_manager.originator_id.private_get_peer_id())) + }; + let reported_messages_sender = + self.reported_peers_sender.clone().with(reported_messages_fn); - let reported_messages_sender = self - .reported_peers_sender - .clone() - .with(|manager: BroadcastedMessageManager| ready(Ok(manager.peer_id))); let continue_propagation_sender = self.continue_propagation_sender.clone(); + Ok(BroadcastTopicChannels { - messages_to_broadcast_sender, - broadcasted_messages_receiver: Box::new(broadcasted_messages_receiver), - reported_messages_sender: Box::new(reported_messages_sender), - continue_propagation_sender: Box::new(continue_propagation_sender), + broadcasted_messages_receiver, + broadcast_topic_client: BroadcastTopicClient { + messages_to_broadcast_sender, + reported_messages_sender, + continue_propagation_sender, + }, }) } @@ -476,7 +484,9 @@ impl GenericNetworkManager { fn handle_gossipsub_behaviour_event(&mut self, event: gossipsub_impl::ExternalEvent) { let gossipsub_impl::ExternalEvent::Received { originated_peer_id, message, topic_hash } = event; - let broadcasted_message_manager = BroadcastedMessageManager { peer_id: originated_peer_id }; + let broadcasted_message_manager = BroadcastedMessageManager { + originator_id: OpaquePeerId::private_new(originated_peer_id), + }; let Some(sender) = self.broadcasted_messages_senders.get_mut(&topic_hash) else { error!( "Received a message from a topic we're not subscribed to with hash {topic_hash:?}" @@ -829,21 +839,15 @@ struct SqmrServerPayload { responses_sender: ResponsesSender, } -pub type BroadcastTopicSender = With< - Sender, - Bytes, +pub type BroadcastTopicSender = With< + Sender, + Message, T, - Ready>, - fn(T) -> Ready>, + Ready>, + fn(T) -> Ready>, >; -// TODO(alonl): remove clone -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct BroadcastedMessageManager { - peer_id: PeerId, -} - -pub type BroadcastTopicReceiver = +pub type BroadcastTopicServer = Map, BroadcastReceivedMessagesConverterFn>; type ReceivedBroadcastedMessage = @@ -853,8 +857,47 @@ type BroadcastReceivedMessagesConverterFn = fn((Bytes, BroadcastedMessageManager)) -> ReceivedBroadcastedMessage; pub struct BroadcastTopicChannels> { - pub messages_to_broadcast_sender: BroadcastTopicSender, - pub broadcasted_messages_receiver: GenericReceiver>, - pub reported_messages_sender: GenericSender, - pub continue_propagation_sender: GenericSender, + pub broadcasted_messages_receiver: BroadcastTopicServer, + pub broadcast_topic_client: BroadcastTopicClient, +} + +#[async_trait] +pub trait BroadcastTopicClientTrait { + async fn broadcast_message(&mut self, message: T) -> Result<(), SendError>; + async fn report_peer( + &mut self, + broadcasted_message_manager: BroadcastedMessageManager, + ) -> Result<(), SendError>; + async fn continue_propagation( + &mut self, + broadcasted_message_manager: &BroadcastedMessageManager, + ) -> Result<(), SendError>; +} + +#[derive(Clone)] +pub struct BroadcastTopicClient> { + messages_to_broadcast_sender: BroadcastTopicSender, + reported_messages_sender: BroadcastTopicSender, + continue_propagation_sender: Sender, +} + +#[async_trait] +impl + Send> BroadcastTopicClientTrait for BroadcastTopicClient { + async fn broadcast_message(&mut self, message: T) -> Result<(), SendError> { + self.messages_to_broadcast_sender.send(message).await + } + + async fn report_peer( + &mut self, + broadcasted_message_manager: BroadcastedMessageManager, + ) -> Result<(), SendError> { + self.reported_messages_sender.send(broadcasted_message_manager).await + } + + async fn continue_propagation( + &mut self, + broadcasted_message_manager: &BroadcastedMessageManager, + ) -> Result<(), SendError> { + self.continue_propagation_sender.send(broadcasted_message_manager.clone()).await + } } diff --git a/crates/papyrus_network/src/network_manager/test.rs b/crates/papyrus_network/src/network_manager/test.rs index 7ff5b498b5d..22b749b1891 100644 --- a/crates/papyrus_network/src/network_manager/test.rs +++ b/crates/papyrus_network/src/network_manager/test.rs @@ -24,7 +24,7 @@ use super::swarm_trait::{Event, SwarmTrait}; use super::{BroadcastTopicChannels, GenericNetworkManager}; use crate::gossipsub_impl::{self, Topic}; use crate::mixed_behaviour; -use crate::network_manager::ServerQueryManager; +use crate::network_manager::{BroadcastTopicClientTrait, ServerQueryManager}; use crate::sqmr::behaviour::{PeerNotConnected, SessionIdNotFoundError}; use crate::sqmr::{Bytes, GenericEvent, InboundSessionId, OutboundSessionId}; @@ -322,11 +322,11 @@ async fn broadcast_message() { let mut network_manager = GenericNetworkManager::generic_new(mock_swarm, None); - let mut messages_to_broadcast_sender = network_manager + let mut broadcast_topic_client = network_manager .register_broadcast_topic(topic.clone(), BUFFER_SIZE) .unwrap() - .messages_to_broadcast_sender; - messages_to_broadcast_sender.send(message.clone()).await.unwrap(); + .broadcast_topic_client; + broadcast_topic_client.broadcast_message(message.clone()).await.unwrap(); tokio::select! { _ = network_manager.run() => panic!("network manager ended"), @@ -359,7 +359,7 @@ async fn receive_broadcasted_message_and_report_it() { let mut network_manager = GenericNetworkManager::generic_new(mock_swarm, None); let BroadcastTopicChannels { - mut reported_messages_sender, + mut broadcast_topic_client, mut broadcasted_messages_receiver, .. } = network_manager.register_broadcast_topic::(topic.clone(), BUFFER_SIZE).unwrap(); @@ -372,7 +372,7 @@ async fn receive_broadcasted_message_and_report_it() { let result = broadcasted_messages_receiver.next().await; let (message_result, broadcasted_message_manager) = result.unwrap(); assert_eq!(message, message_result.unwrap()); - reported_messages_sender.send(broadcasted_message_manager).await.unwrap(); + broadcast_topic_client.report_peer(broadcasted_message_manager).await.unwrap(); reported_peer_receiver.next().await }) => { assert_eq!(originated_peer_id, reported_peer_result.unwrap().unwrap()); diff --git a/crates/papyrus_network/src/network_manager/test_utils.rs b/crates/papyrus_network/src/network_manager/test_utils.rs index da16b696e8d..69bb63f30af 100644 --- a/crates/papyrus_network/src/network_manager/test_utils.rs +++ b/crates/papyrus_network/src/network_manager/test_utils.rs @@ -8,6 +8,7 @@ use libp2p::gossipsub::SubscriptionError; use libp2p::PeerId; use super::{ + BroadcastTopicClient, BroadcastedMessageManager, GenericReceiver, ReportReceiver, @@ -74,10 +75,6 @@ where ) } -pub fn create_test_broadcasted_message_manager() -> BroadcastedMessageManager { - BroadcastedMessageManager { peer_id: PeerId::random() } -} - const CHANNEL_BUFFER_SIZE: usize = 10000; pub fn mock_register_broadcast_topic() -> Result, SubscriptionError> @@ -100,15 +97,22 @@ where let (reported_messages_sender, _mock_reported_messages_receiver) = futures::channel::mpsc::channel(CHANNEL_BUFFER_SIZE); + let reported_messages_fn: fn(BroadcastedMessageManager) -> Ready> = + |broadcasted_message_manager| { + ready(Ok(broadcasted_message_manager.originator_id.private_get_peer_id())) + }; + let reported_messages_sender = reported_messages_sender.with(reported_messages_fn); let (continue_propagation_sender, _mock_continue_propagation_receiver) = futures::channel::mpsc::channel(CHANNEL_BUFFER_SIZE); let subscriber_channels = BroadcastTopicChannels { - messages_to_broadcast_sender, - broadcasted_messages_receiver: Box::new(broadcasted_messages_receiver), - reported_messages_sender: Box::new(reported_messages_sender), - continue_propagation_sender: Box::new(continue_propagation_sender), + broadcasted_messages_receiver, + broadcast_topic_client: BroadcastTopicClient { + messages_to_broadcast_sender, + reported_messages_sender, + continue_propagation_sender, + }, }; let mock_broadcasted_messages_fn: MockBroadcastedMessagesFn = diff --git a/crates/papyrus_network/src/utils.rs b/crates/papyrus_network/src/utils.rs index bc0a3ab9717..015b0d701e9 100644 --- a/crates/papyrus_network/src/utils.rs +++ b/crates/papyrus_network/src/utils.rs @@ -66,7 +66,7 @@ impl Stream for StreamHashMap Self { + Self(peer_id) + } + + /// This function shouldn't be used by anyone except for the papyrus_network crate + pub fn private_get_peer_id(&self) -> PeerId { + self.0 + } +} diff --git a/crates/papyrus_network_types/src/test_utils.rs b/crates/papyrus_network_types/src/test_utils.rs new file mode 100644 index 00000000000..2f36023c935 --- /dev/null +++ b/crates/papyrus_network_types/src/test_utils.rs @@ -0,0 +1,18 @@ +use libp2p::PeerId; +use papyrus_test_utils::{auto_impl_get_test_instance, GetTestInstance}; +use rand_chacha::ChaCha8Rng; + +use crate::network_types::{BroadcastedMessageManager, OpaquePeerId}; + +impl GetTestInstance for OpaquePeerId { + // TODO: use the given rng by copying the libp2p implementation. + fn get_test_instance(_rng: &mut ChaCha8Rng) -> Self { + Self::private_new(PeerId::random()) + } +} + +auto_impl_get_test_instance! { + pub struct BroadcastedMessageManager { + pub originator_id: OpaquePeerId, + } +} diff --git a/crates/papyrus_node/src/bin/run_consensus.rs b/crates/papyrus_node/src/bin/run_consensus.rs index 39bf8224ff7..e7bd28ccbc6 100644 --- a/crates/papyrus_node/src/bin/run_consensus.rs +++ b/crates/papyrus_node/src/bin/run_consensus.rs @@ -1,12 +1,17 @@ //! Run a papyrus node with consensus enabled and the ability to simulate network issues for //! consensus. +//! +//! Expects to receive 2 groupings of arguments: +//! 1. TestConfig - these are prefixed with `--test.` in the command. +//! 2. NodeConfig - any argument lacking the above prefix is assumed to be in NodeConfig. use clap::Parser; use futures::stream::StreamExt; use papyrus_consensus::config::ConsensusConfig; use papyrus_consensus::simulation_network_receiver::NetworkReceiver; +use papyrus_consensus::types::BroadcastConsensusMessageChannel; use papyrus_consensus_orchestrator::papyrus_consensus_context::PapyrusConsensusContext; use papyrus_network::gossipsub_impl::Topic; -use papyrus_network::network_manager::{BroadcastTopicChannels, NetworkManager}; +use papyrus_network::network_manager::NetworkManager; use papyrus_node::bin_utils::build_configs; use papyrus_node::run::{run, PapyrusResources, PapyrusTaskHandles}; use papyrus_p2p_sync::BUFFER_SIZE; @@ -59,9 +64,9 @@ fn build_consensus( .register_broadcast_topic(Topic::new(test_config.sync_topic.clone()), BUFFER_SIZE)?; let context = PapyrusConsensusContext::new( storage_reader.clone(), - network_channels.messages_to_broadcast_sender.clone(), + network_channels.broadcast_topic_client.clone(), consensus_config.num_validators, - Some(sync_channels.messages_to_broadcast_sender), + Some(sync_channels.broadcast_topic_client), ); let sync_receiver = sync_channels.broadcasted_messages_receiver.map(|(vote, _report_sender)| { @@ -74,11 +79,9 @@ fn build_consensus( test_config.drop_probability, test_config.invalid_probability, ); - let broadcast_channels = BroadcastTopicChannels { - messages_to_broadcast_sender: network_channels.messages_to_broadcast_sender, + let broadcast_channels = BroadcastConsensusMessageChannel { broadcasted_messages_receiver: Box::new(network_receiver), - reported_messages_sender: network_channels.reported_messages_sender, - continue_propagation_sender: network_channels.continue_propagation_sender, + broadcast_topic_client: network_channels.broadcast_topic_client, }; Ok(Some(tokio::spawn(async move { @@ -97,7 +100,7 @@ fn build_consensus( #[tokio::main] async fn main() -> anyhow::Result<()> { - let (test_config, node_config) = build_configs::()?; + let (test_config, node_config) = build_configs::("--test.")?; let mut resources = PapyrusResources::new(&node_config)?; diff --git a/crates/papyrus_node/src/bin_utils.rs b/crates/papyrus_node/src/bin_utils.rs index 02016079bc6..e84d23b9ed2 100644 --- a/crates/papyrus_node/src/bin_utils.rs +++ b/crates/papyrus_node/src/bin_utils.rs @@ -5,22 +5,19 @@ use papyrus_config::ConfigError; use crate::config::NodeConfig; -// Test arguments passed on the command line are prefixed with `test.`. -const TEST_ARG_PREFIX: &str = "--test."; - /// Split the elements of `input_args` into 2 groups: -/// 1. Those prefixed with "--test." +/// 1. Those prefixed with `split_args_prefix` /// 2. Other. /// /// Presumes input is: program_name (--flag_name value)* -pub fn split_args(input_args: Vec) -> (Vec, Vec) { +pub fn split_args(input_args: Vec, split_args_prefix: &str) -> (Vec, Vec) { input_args[1..].chunks(2).fold( (vec![input_args[0].clone()], vec![input_args[0].clone()]), |(mut matching_args, mut mismatched_args), input_arg| { let (name, value) = (&input_arg[0], &input_arg[1]); // String leading `--` for comparison. - if &name[..TEST_ARG_PREFIX.len()] == TEST_ARG_PREFIX { - matching_args.push(format!("--{}", &name[TEST_ARG_PREFIX.len()..])); + if &name[..split_args_prefix.len()] == split_args_prefix { + matching_args.push(format!("--{}", &name[split_args_prefix.len()..])); matching_args.push(value.clone()); } else { mismatched_args.push(name.clone()); @@ -32,9 +29,11 @@ pub fn split_args(input_args: Vec) -> (Vec, Vec) { } /// Build both the node and test configs from the command line arguments. -pub fn build_configs() -> Result<(T, NodeConfig), ConfigError> { +pub fn build_configs( + split_args_prefix: &str, +) -> Result<(T, NodeConfig), ConfigError> { let input_args = args().collect::>(); - let (test_input_args, node_input_args) = split_args(input_args); + let (test_input_args, node_input_args) = split_args(input_args, split_args_prefix); let mut test_config = T::default(); test_config.update_from(test_input_args.iter()); diff --git a/crates/papyrus_node/src/run.rs b/crates/papyrus_node/src/run.rs index 2d9ef95dfb0..f2e77d528b9 100644 --- a/crates/papyrus_node/src/run.rs +++ b/crates/papyrus_node/src/run.rs @@ -188,7 +188,7 @@ fn spawn_consensus( .register_broadcast_topic(Topic::new(config.network_topic.clone()), BUFFER_SIZE)?; let context = PapyrusConsensusContext::new( storage_reader.clone(), - network_channels.messages_to_broadcast_sender.clone(), + network_channels.broadcast_topic_client.clone(), config.num_validators, None, ); @@ -199,7 +199,7 @@ fn spawn_consensus( config.validator_id, config.consensus_delay, config.timeouts.clone(), - network_channels, + network_channels.into(), futures::stream::pending(), ) .await?) diff --git a/crates/papyrus_protobuf/src/consensus.rs b/crates/papyrus_protobuf/src/consensus.rs index 8b925cf7f2a..50afaed1bd8 100644 --- a/crates/papyrus_protobuf/src/consensus.rs +++ b/crates/papyrus_protobuf/src/consensus.rs @@ -45,12 +45,18 @@ impl ConsensusMessage { } } } -#[derive(Debug, Default, Clone, Hash, Eq, PartialEq)] + +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub enum StreamMessageBody { + Content(T), + Fin, +} + +#[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct StreamMessage> + TryFrom, Error = ProtobufConversionError>> { - pub message: T, + pub message: StreamMessageBody, pub stream_id: u64, pub message_id: u64, - pub fin: bool, } // TODO(Guy): Remove after implementing broadcast streams. diff --git a/crates/papyrus_protobuf/src/converters/consensus.rs b/crates/papyrus_protobuf/src/converters/consensus.rs index 6c37847c76b..83ab95fb061 100644 --- a/crates/papyrus_protobuf/src/converters/consensus.rs +++ b/crates/papyrus_protobuf/src/converters/consensus.rs @@ -8,7 +8,14 @@ use starknet_api::block::BlockHash; use starknet_api::hash::StarkHash; use starknet_api::transaction::Transaction; -use crate::consensus::{ConsensusMessage, Proposal, StreamMessage, Vote, VoteType}; +use crate::consensus::{ + ConsensusMessage, + Proposal, + StreamMessage, + StreamMessageBody, + Vote, + VoteType, +}; use crate::converters::ProtobufConversionError; use crate::{auto_impl_into_and_try_from_vec_u8, protobuf}; @@ -121,10 +128,23 @@ impl> + TryFrom, Error = ProtobufConversionError>> fn try_from(value: protobuf::StreamMessage) -> Result { Ok(Self { - message: T::try_from(value.message)?, + message: match value { + protobuf::StreamMessage { + message: Some(protobuf::stream_message::Message::Content(message)), + stream_id: _, + message_id: _, + } => StreamMessageBody::Content(message.try_into()?), + protobuf::StreamMessage { + message: Some(protobuf::stream_message::Message::Fin(protobuf::Fin {})), + stream_id: _, + message_id: _, + } => StreamMessageBody::Fin, + protobuf::StreamMessage { message: None, stream_id: _, message_id: _ } => { + StreamMessageBody::Fin + } + }, stream_id: value.stream_id, message_id: value.message_id, - fin: value.fin, }) } } @@ -134,10 +154,18 @@ impl> + TryFrom, Error = ProtobufConversionError>> From< { fn from(value: StreamMessage) -> Self { Self { - message: value.message.into(), + message: match value { + StreamMessage { + message: StreamMessageBody::Content(message), + stream_id: _, + message_id: _, + } => Some(protobuf::stream_message::Message::Content(message.into())), + StreamMessage { message: StreamMessageBody::Fin, stream_id: _, message_id: _ } => { + Some(protobuf::stream_message::Message::Fin(protobuf::Fin {})) + } + }, stream_id: value.stream_id, message_id: value.message_id, - fin: value.fin, } } } diff --git a/crates/papyrus_protobuf/src/converters/consensus_test.rs b/crates/papyrus_protobuf/src/converters/consensus_test.rs index ca3f81ecb90..5397d0ff53c 100644 --- a/crates/papyrus_protobuf/src/converters/consensus_test.rs +++ b/crates/papyrus_protobuf/src/converters/consensus_test.rs @@ -18,7 +18,14 @@ use starknet_api::transaction::{ ValidResourceBounds, }; -use crate::consensus::{ConsensusMessage, Proposal, StreamMessage, Vote, VoteType}; +use crate::consensus::{ + ConsensusMessage, + Proposal, + StreamMessage, + StreamMessageBody, + Vote, + VoteType, +}; auto_impl_get_test_instance! { pub enum ConsensusMessage { @@ -55,14 +62,16 @@ auto_impl_get_test_instance! { } } +// The auto_impl_get_test_instance macro does not work for StreamMessage because it has +// a generic type. TODO(guyn): try to make the macro work with generic types. impl GetTestInstance for StreamMessage { fn get_test_instance(rng: &mut rand_chacha::ChaCha8Rng) -> Self { - Self { - message: ConsensusMessage::Proposal(Proposal::default()), - stream_id: rng.gen_range(0..100), - message_id: rng.gen_range(0..1000), - fin: rng.gen_bool(0.5), - } + let message = if rng.gen_bool(0.5) { + StreamMessageBody::Content(ConsensusMessage::Proposal(Proposal::get_test_instance(rng))) + } else { + StreamMessageBody::Fin + }; + Self { message, stream_id: rng.gen_range(0..100), message_id: rng.gen_range(0..1000) } } } diff --git a/crates/papyrus_protobuf/src/proto/p2p/proto/common.proto b/crates/papyrus_protobuf/src/proto/p2p/proto/common.proto index 63376061f2a..0964096d8ba 100644 --- a/crates/papyrus_protobuf/src/proto/p2p/proto/common.proto +++ b/crates/papyrus_protobuf/src/proto/p2p/proto/common.proto @@ -70,6 +70,4 @@ message Iteration { // bool interleave = 6; // return results in any order of blocks, per block the messages should still be in the order specified } -// mark the end of a stream of messages -// TBD: may not be required if we open a stream per request. message Fin {} diff --git a/crates/papyrus_protobuf/src/proto/p2p/proto/consensus.proto b/crates/papyrus_protobuf/src/proto/p2p/proto/consensus.proto index 29f3a47beed..cae7b1c0555 100644 --- a/crates/papyrus_protobuf/src/proto/p2p/proto/consensus.proto +++ b/crates/papyrus_protobuf/src/proto/p2p/proto/consensus.proto @@ -36,8 +36,10 @@ message ConsensusMessage { } message StreamMessage { - bytes message = 1; - uint64 stream_id = 2; - uint64 message_id = 3; - bool fin = 4; + oneof message { + bytes content = 1; + Fin fin = 2; + } + uint64 stream_id = 3; + uint64 message_id = 4; } diff --git a/crates/papyrus_rpc/resources/V0_8/starknet_api_openrpc.json b/crates/papyrus_rpc/resources/V0_8/starknet_api_openrpc.json index 6bc8657e591..b6a9d9aaf9b 100644 --- a/crates/papyrus_rpc/resources/V0_8/starknet_api_openrpc.json +++ b/crates/papyrus_rpc/resources/V0_8/starknet_api_openrpc.json @@ -1477,6 +1477,11 @@ "description": "The price of l1 data gas in the block", "$ref": "#/components/schemas/RESOURCE_PRICE" }, + "l2_gas_price": { + "title": "L2 gas price", + "description": "The price of l2 gas in the block", + "$ref": "#/components/schemas/RESOURCE_PRICE" + }, "l1_da_mode": { "title": "L1 da mode", "type": "string", @@ -3611,7 +3616,7 @@ "description": "The Ethereum gas consumption of the transaction", "$ref": "#/components/schemas/FELT" }, - "gas_price": { + "l1_gas_price": { "title": "Gas price", "description": "The gas price (in wei or fri, depending on the tx version) that was used in the cost estimation", "$ref": "#/components/schemas/FELT" @@ -3621,7 +3626,7 @@ "description": "The Ethereum data gas consumption of the transaction", "$ref": "#/components/schemas/FELT" }, - "data_gas_price": { + "l1_data_gas_price": { "title": "Data gas price", "description": "The data gas price (in wei or fri, depending on the tx version) that was used in the cost estimation", "$ref": "#/components/schemas/FELT" @@ -3639,9 +3644,9 @@ }, "required": [ "gas_consumed", - "gas_price", + "l1_gas_price", "data_gas_consumed", - "data_gas_price", + "l1_data_gas_price", "overall_fee", "unit" ] diff --git a/crates/papyrus_rpc/src/v0_8/api/api_impl.rs b/crates/papyrus_rpc/src/v0_8/api/api_impl.rs index f324705c416..eb9c6ffb34a 100644 --- a/crates/papyrus_rpc/src/v0_8/api/api_impl.rs +++ b/crates/papyrus_rpc/src/v0_8/api/api_impl.rs @@ -1539,6 +1539,10 @@ impl JsonRpcServerImpl { price_in_wei: block.l1_data_gas_price().price_in_wei, price_in_fri: block.l1_data_gas_price().price_in_fri, }, + l2_gas_price: ResourcePrice { + price_in_wei: block.l2_gas_price().price_in_wei, + price_in_fri: block.l2_gas_price().price_in_fri, + }, l1_da_mode: block.l1_da_mode(), starknet_version: block.starknet_version(), }; diff --git a/crates/papyrus_rpc/src/v0_8/api/test.rs b/crates/papyrus_rpc/src/v0_8/api/test.rs index 63c456328b7..3ba233c4a67 100644 --- a/crates/papyrus_rpc/src/v0_8/api/test.rs +++ b/crates/papyrus_rpc/src/v0_8/api/test.rs @@ -606,6 +606,8 @@ async fn get_block_w_full_transactions() { price_in_wei: GasPrice(random::()), price_in_fri: GasPrice(random::()), }; + let pending_l2_gas_price = + GasPricePerToken { price_in_wei: GasPrice(0), price_in_fri: GasPrice(0) }; let expected_pending_block = Block { header: GeneralBlockHeader::PendingBlockHeader(PendingBlockHeader { parent_hash: block_hash, @@ -616,6 +618,10 @@ async fn get_block_w_full_transactions() { price_in_fri: pending_l1_gas_price.price_in_fri, }, l1_data_gas_price: ResourcePrice::default(), + l2_gas_price: ResourcePrice { + price_in_wei: pending_l2_gas_price.price_in_wei, + price_in_fri: pending_l2_gas_price.price_in_fri, + }, l1_da_mode: L1DataAvailabilityMode::Calldata, starknet_version: starknet_version.to_string(), }), @@ -792,6 +798,8 @@ async fn get_block_w_full_transactions_and_receipts() { price_in_wei: GasPrice(rng.next_u64().into()), price_in_fri: GasPrice(rng.next_u64().into()), }; + let pending_l2_gas_price = + GasPricePerToken { price_in_wei: GasPrice(0), price_in_fri: GasPrice(0) }; let expected_pending_block = Block { header: GeneralBlockHeader::PendingBlockHeader(PendingBlockHeader { parent_hash: block_hash, @@ -802,6 +810,10 @@ async fn get_block_w_full_transactions_and_receipts() { price_in_fri: pending_l1_gas_price.price_in_fri, }, l1_data_gas_price: ResourcePrice::default(), + l2_gas_price: ResourcePrice { + price_in_wei: pending_l2_gas_price.price_in_wei, + price_in_fri: pending_l2_gas_price.price_in_fri, + }, l1_da_mode: L1DataAvailabilityMode::Calldata, starknet_version: starknet_version.to_string(), }), @@ -978,6 +990,8 @@ async fn get_block_w_transaction_hashes() { price_in_wei: GasPrice(random::()), price_in_fri: GasPrice(random::()), }; + let pending_l2_gas_price = + GasPricePerToken { price_in_wei: GasPrice(0), price_in_fri: GasPrice(0) }; let expected_pending_block = Block { header: GeneralBlockHeader::PendingBlockHeader(PendingBlockHeader { parent_hash: block_hash, @@ -987,6 +1001,10 @@ async fn get_block_w_transaction_hashes() { price_in_wei: pending_l1_gas_price.price_in_wei, price_in_fri: pending_l1_gas_price.price_in_fri, }, + l2_gas_price: ResourcePrice { + price_in_wei: pending_l2_gas_price.price_in_wei, + price_in_fri: pending_l2_gas_price.price_in_fri, + }, l1_data_gas_price: ResourcePrice::default(), l1_da_mode: L1DataAvailabilityMode::Calldata, starknet_version: starknet_version.to_string(), @@ -4077,6 +4095,7 @@ auto_impl_get_test_instance! { pub timestamp: BlockTimestamp, pub l1_gas_price: ResourcePrice, pub l1_data_gas_price: ResourcePrice, + pub l2_gas_price: ResourcePrice, pub l1_da_mode: L1DataAvailabilityMode, pub starknet_version: String, } diff --git a/crates/papyrus_rpc/src/v0_8/block.rs b/crates/papyrus_rpc/src/v0_8/block.rs index 0f657e30aac..2659535a3bf 100644 --- a/crates/papyrus_rpc/src/v0_8/block.rs +++ b/crates/papyrus_rpc/src/v0_8/block.rs @@ -22,6 +22,7 @@ pub struct BlockHeader { pub timestamp: BlockTimestamp, pub l1_gas_price: ResourcePrice, pub l1_data_gas_price: ResourcePrice, + pub l2_gas_price: ResourcePrice, pub l1_da_mode: L1DataAvailabilityMode, pub starknet_version: String, } @@ -34,6 +35,7 @@ pub struct PendingBlockHeader { pub timestamp: BlockTimestamp, pub l1_gas_price: ResourcePrice, pub l1_data_gas_price: ResourcePrice, + pub l2_gas_price: ResourcePrice, pub l1_da_mode: L1DataAvailabilityMode, pub starknet_version: String, } @@ -62,6 +64,10 @@ impl From for BlockHeader { price_in_wei: header.block_header_without_hash.l1_data_gas_price.price_in_wei, price_in_fri: header.block_header_without_hash.l1_data_gas_price.price_in_fri, }, + l2_gas_price: ResourcePrice { + price_in_wei: header.block_header_without_hash.l2_gas_price.price_in_wei, + price_in_fri: header.block_header_without_hash.l2_gas_price.price_in_fri, + }, l1_da_mode: header.block_header_without_hash.l1_da_mode, starknet_version: header.block_header_without_hash.starknet_version.to_string(), } diff --git a/crates/papyrus_rpc/src/v0_8/execution_test.rs b/crates/papyrus_rpc/src/v0_8/execution_test.rs index b1cf9624769..24d5e8e5dae 100644 --- a/crates/papyrus_rpc/src/v0_8/execution_test.rs +++ b/crates/papyrus_rpc/src/v0_8/execution_test.rs @@ -178,9 +178,9 @@ lazy_static! { // call. pub static ref EXPECTED_FEE_ESTIMATE: FeeEstimation = FeeEstimation { gas_consumed: felt!("0x681"), - gas_price: GAS_PRICE.price_in_wei, + l1_gas_price: GAS_PRICE.price_in_wei, data_gas_consumed: Felt::ZERO, - data_gas_price: DATA_GAS_PRICE.price_in_wei, + l1_data_gas_price: DATA_GAS_PRICE.price_in_wei, l2_gas_price: L2_GAS_PRICE.price_in_wei, overall_fee: Fee(166500000000000,), unit: PriceUnit::Wei, @@ -188,9 +188,9 @@ lazy_static! { pub static ref EXPECTED_FEE_ESTIMATE_SKIP_VALIDATE: FeeEstimation = FeeEstimation { gas_consumed: felt!("0x681"), - gas_price: GAS_PRICE.price_in_wei, + l1_gas_price: GAS_PRICE.price_in_wei, data_gas_consumed: Felt::ZERO, - data_gas_price: DATA_GAS_PRICE.price_in_wei, + l1_data_gas_price: DATA_GAS_PRICE.price_in_wei, l2_gas_price: L2_GAS_PRICE.price_in_wei, overall_fee: Fee(166500000000000,), unit: PriceUnit::Wei, @@ -1219,9 +1219,9 @@ async fn call_estimate_message_fee() { // is correct. let expected_fee_estimate = FeeEstimation { gas_consumed: felt!("0x3937"), - gas_price: GAS_PRICE.price_in_wei, + l1_gas_price: GAS_PRICE.price_in_wei, data_gas_consumed: Felt::ZERO, - data_gas_price: DATA_GAS_PRICE.price_in_wei, + l1_data_gas_price: DATA_GAS_PRICE.price_in_wei, l2_gas_price: L2_GAS_PRICE.price_in_wei, overall_fee: Fee(0), unit: PriceUnit::default(), diff --git a/crates/sequencing/papyrus_consensus/Cargo.toml b/crates/sequencing/papyrus_consensus/Cargo.toml index 8f5023021a4..25be426ad6b 100644 --- a/crates/sequencing/papyrus_consensus/Cargo.toml +++ b/crates/sequencing/papyrus_consensus/Cargo.toml @@ -9,14 +9,16 @@ description = "Reach consensus for Starknet" [dependencies] async-trait.workspace = true clap = { workspace = true, features = ["derive"] } -fs2 = "0.4" +fs2.workspace = true futures.workspace = true lazy_static.workspace = true lru.workspace = true metrics.workspace = true +nix.workspace = true papyrus_common.workspace = true papyrus_config.workspace = true papyrus_network.workspace = true +papyrus_network_types.workspace = true papyrus_protobuf.workspace = true serde = { workspace = true, features = ["derive"] } starknet-types-core.workspace = true @@ -28,6 +30,7 @@ tracing.workspace = true [dev-dependencies] mockall.workspace = true papyrus_network = { workspace = true, features = ["testing"] } +papyrus_network_types = { workspace = true, features = ["testing"] } papyrus_storage = { workspace = true, features = ["testing"] } papyrus_test_utils.workspace = true test-case.workspace = true diff --git a/crates/sequencing/papyrus_consensus/src/bin/run_simulation.rs b/crates/sequencing/papyrus_consensus/src/bin/run_simulation.rs index 0e1164784d9..6b425d72f2f 100644 --- a/crates/sequencing/papyrus_consensus/src/bin/run_simulation.rs +++ b/crates/sequencing/papyrus_consensus/src/bin/run_simulation.rs @@ -5,6 +5,7 @@ use std::collections::HashSet; use std::fs::{self, File}; use std::net::TcpListener; +use std::os::unix::process::CommandExt; use std::process::Command; use std::str::FromStr; use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; @@ -12,6 +13,7 @@ use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; use clap::Parser; use fs2::FileExt; use lazy_static::lazy_static; +use nix::unistd::Pid; use tokio::process::Command as TokioCommand; lazy_static! { @@ -48,19 +50,22 @@ impl Node { } fn start(&mut self) { - self.process = Some( - TokioCommand::new("sh") - .arg("-c") - .arg(&self.cmd) - .spawn() - .expect("Failed to start process"), - ); + let mut cmd = Command::new("sh"); + cmd.arg("-c").arg(&self.cmd) + // Sets the process group to ensure that when we terminate the process, + // all subprocesses (e.g., those spawned via pipes or redirections) are also terminated. + .process_group(0); + + let process = TokioCommand::from(cmd).spawn().expect("Failed to start process"); + self.process = Some(process); } async fn stop(&mut self) { - if let Some(process) = self.process.as_mut() { - process.kill().await.expect("Failed to kill process"); - } + let process = self.process.as_mut().expect("Process not found"); + let pid = process.id().unwrap(); + // Send SIGINT to the entire process group to terminate the process and its subprocesses + nix::sys::signal::killpg(Pid::from_raw(pid as i32), nix::sys::signal::Signal::SIGINT) + .expect("Failed to kill process group"); } async fn get_metric(&self, metric: &str) -> Option { diff --git a/crates/sequencing/papyrus_consensus/src/manager.rs b/crates/sequencing/papyrus_consensus/src/manager.rs index b1dbebd19cc..3a9f18bb7e4 100644 --- a/crates/sequencing/papyrus_consensus/src/manager.rs +++ b/crates/sequencing/papyrus_consensus/src/manager.rs @@ -9,9 +9,9 @@ use std::time::Duration; use futures::channel::{mpsc, oneshot}; use futures::stream::FuturesUnordered; -use futures::{SinkExt, Stream, StreamExt}; +use futures::{Stream, StreamExt}; use papyrus_common::metrics::{PAPYRUS_CONSENSUS_HEIGHT, PAPYRUS_CONSENSUS_SYNC_COUNT}; -use papyrus_network::network_manager::BroadcastTopicChannels; +use papyrus_network::network_manager::BroadcastTopicClientTrait; use papyrus_protobuf::consensus::{ConsensusMessage, ProposalWrapper}; use starknet_api::block::{BlockHash, BlockNumber}; use starknet_api::core::ContractAddress; @@ -19,7 +19,13 @@ use tracing::{debug, info, instrument}; use crate::config::TimeoutsConfig; use crate::single_height_consensus::{ShcReturn, ShcTask, SingleHeightConsensus}; -use crate::types::{ConsensusContext, ConsensusError, Decision, ValidatorId}; +use crate::types::{ + BroadcastConsensusMessageChannel, + ConsensusContext, + ConsensusError, + Decision, + ValidatorId, +}; // TODO(dvir): add test for this. #[instrument(skip_all, level = "info")] @@ -30,7 +36,7 @@ pub async fn run_consensus( validator_id: ValidatorId, consensus_delay: Duration, timeouts: TimeoutsConfig, - mut broadcast_channels: BroadcastTopicChannels, + mut broadcast_channels: BroadcastConsensusMessageChannel, mut sync_receiver: SyncReceiverT, ) -> Result<(), ConsensusError> where @@ -101,7 +107,7 @@ impl MultiHeightManager { &mut self, context: &mut ContextT, height: BlockNumber, - broadcast_channels: &mut BroadcastTopicChannels, + broadcast_channels: &mut BroadcastConsensusMessageChannel, ) -> Result where ContextT: ConsensusContext, @@ -219,16 +225,12 @@ impl MultiHeightManager { async fn next_message( cached_messages: &mut Vec, - broadcast_channels: &mut BroadcastTopicChannels, + broadcast_channels: &mut BroadcastConsensusMessageChannel, ) -> Result where { - let BroadcastTopicChannels { - continue_propagation_sender, - broadcasted_messages_receiver, - reported_messages_sender, - .. - } = broadcast_channels; + let BroadcastConsensusMessageChannel { broadcasted_messages_receiver, broadcast_topic_client } = + broadcast_channels; if let Some(msg) = cached_messages.pop() { return Ok(msg); } @@ -242,12 +244,12 @@ where match msg { // TODO(matan): Return report_sender for use in later errors by SHC. Ok(msg) => { - let _ = continue_propagation_sender.send(broadcasted_message_manager).await; + let _ = broadcast_topic_client.continue_propagation(&broadcasted_message_manager).await; Ok(msg) } Err(e) => { // Failed to parse consensus message - let _ = reported_messages_sender.send(broadcasted_message_manager).await; + let _ = broadcast_topic_client.report_peer(broadcasted_message_manager).await; Err(e.into()) } } diff --git a/crates/sequencing/papyrus_consensus/src/manager_test.rs b/crates/sequencing/papyrus_consensus/src/manager_test.rs index 1d1db83b256..f36292ff423 100644 --- a/crates/sequencing/papyrus_consensus/src/manager_test.rs +++ b/crates/sequencing/papyrus_consensus/src/manager_test.rs @@ -8,12 +8,13 @@ use lazy_static::lazy_static; use mockall::mock; use mockall::predicate::eq; use papyrus_network::network_manager::test_utils::{ - create_test_broadcasted_message_manager, mock_register_broadcast_topic, MockBroadcastedMessagesSender, TestSubscriberChannels, }; +use papyrus_network_types::network_types::BroadcastedMessageManager; use papyrus_protobuf::consensus::{ConsensusMessage, Vote}; +use papyrus_test_utils::{get_rng, GetTestInstance}; use starknet_api::block::{BlockHash, BlockNumber}; use starknet_api::transaction::Transaction; use starknet_types_core::felt::Felt; @@ -84,13 +85,13 @@ mock! { } async fn send(sender: &mut MockBroadcastedMessagesSender, msg: ConsensusMessage) { - let broadcasted_message_manager = create_test_broadcasted_message_manager(); + let broadcasted_message_manager = BroadcastedMessageManager::get_test_instance(&mut get_rng()); sender.send((msg, broadcasted_message_manager)).await.unwrap(); } #[tokio::test] async fn manager_multiple_heights_unordered() { - let TestSubscriberChannels { mock_network, mut subscriber_channels } = + let TestSubscriberChannels { mock_network, subscriber_channels } = mock_register_broadcast_topic().unwrap(); let mut sender = mock_network.broadcasted_messages_sender; // Send messages for height 2 followed by those for height 1. @@ -116,6 +117,7 @@ async fn manager_multiple_heights_unordered() { context.expect_broadcast().returning(move |_| Ok(())); let mut manager = MultiHeightManager::new(*VALIDATOR_ID, TIMEOUTS.clone()); + let mut subscriber_channels = subscriber_channels.into(); let decision = manager.run_height(&mut context, BlockNumber(1), &mut subscriber_channels).await.unwrap(); assert_eq!(decision.block, BlockHash(Felt::ONE)); @@ -172,7 +174,7 @@ async fn run_consensus_sync() { *VALIDATOR_ID, Duration::ZERO, TIMEOUTS.clone(), - subscriber_channels, + subscriber_channels.into(), &mut sync_receiver, ) .await @@ -231,7 +233,7 @@ async fn run_consensus_sync_cancellation_safety() { *VALIDATOR_ID, Duration::ZERO, TIMEOUTS.clone(), - subscriber_channels, + subscriber_channels.into(), &mut sync_receiver, ) .await @@ -259,7 +261,7 @@ async fn run_consensus_sync_cancellation_safety() { #[tokio::test] async fn test_timeouts() { - let TestSubscriberChannels { mock_network, mut subscriber_channels } = + let TestSubscriberChannels { mock_network, subscriber_channels } = mock_register_broadcast_topic().unwrap(); let mut sender = mock_network.broadcasted_messages_sender; send(&mut sender, proposal(Felt::ONE, 1, 0, *PROPOSER_ID)).await; @@ -294,7 +296,7 @@ async fn test_timeouts() { let mut manager = MultiHeightManager::new(*VALIDATOR_ID, TIMEOUTS.clone()); let manager_handle = tokio::spawn(async move { let decision = manager - .run_height(&mut context, BlockNumber(1), &mut subscriber_channels) + .run_height(&mut context, BlockNumber(1), &mut subscriber_channels.into()) .await .unwrap(); assert_eq!(decision.block, BlockHash(Felt::ONE)); diff --git a/crates/sequencing/papyrus_consensus/src/simulation_network_receiver.rs b/crates/sequencing/papyrus_consensus/src/simulation_network_receiver.rs index 4652f88889e..013f1bfdb80 100644 --- a/crates/sequencing/papyrus_consensus/src/simulation_network_receiver.rs +++ b/crates/sequencing/papyrus_consensus/src/simulation_network_receiver.rs @@ -9,7 +9,8 @@ use std::task::Poll; use futures::{Stream, StreamExt}; use lru::LruCache; -use papyrus_network::network_manager::{BroadcastedMessageManager, GenericReceiver}; +use papyrus_network::network_manager::BroadcastTopicServer; +use papyrus_network_types::network_types::BroadcastedMessageManager; use papyrus_protobuf::consensus::ConsensusMessage; use papyrus_protobuf::converters::ProtobufConversionError; use starknet_api::block::BlockHash; @@ -30,10 +31,7 @@ use tracing::{debug, instrument}; /// is as opposed to using a stateful RNG where the random number is a function of all the /// previous calls to the RNG. pub struct NetworkReceiver { - pub broadcasted_messages_receiver: GenericReceiver<( - Result, - BroadcastedMessageManager, - )>, + pub broadcasted_messages_receiver: BroadcastTopicServer, // Cache is used so that repeat sends of a message can be processed differently. For example, // if a message is dropped resending it should result in a new decision. pub cache: LruCache, @@ -46,10 +44,7 @@ pub struct NetworkReceiver { impl NetworkReceiver { pub fn new( - broadcasted_messages_receiver: GenericReceiver<( - Result, - BroadcastedMessageManager, - )>, + broadcasted_messages_receiver: BroadcastTopicServer, cache_size: usize, seed: u64, drop_probability: f64, diff --git a/crates/sequencing/papyrus_consensus/src/simulation_network_receiver_test.rs b/crates/sequencing/papyrus_consensus/src/simulation_network_receiver_test.rs index 6b92a4b9e77..6a538ff0513 100644 --- a/crates/sequencing/papyrus_consensus/src/simulation_network_receiver_test.rs +++ b/crates/sequencing/papyrus_consensus/src/simulation_network_receiver_test.rs @@ -1,10 +1,11 @@ use futures::{SinkExt, StreamExt}; use papyrus_network::network_manager::test_utils::{ - create_test_broadcasted_message_manager, mock_register_broadcast_topic, TestSubscriberChannels, }; +use papyrus_network_types::network_types::BroadcastedMessageManager; use papyrus_protobuf::consensus::ConsensusMessage; +use papyrus_test_utils::{get_rng, GetTestInstance}; use test_case::test_case; use super::NetworkReceiver; @@ -33,7 +34,8 @@ async fn test_invalid(distinct_messages: bool, is_vote: bool) { for height in 0..1000 { let msg = create_consensus_msg(if distinct_messages { height } else { 0 }, is_vote); - let broadcasted_message_manager = create_test_broadcasted_message_manager(); + let broadcasted_message_manager = + BroadcastedMessageManager::get_test_instance(&mut get_rng()); mock_network .broadcasted_messages_sender .send((msg.clone(), broadcasted_message_manager)) @@ -65,7 +67,8 @@ async fn test_drops(distinct_messages: bool, is_vote: bool) { for height in 0..1000 { let msg = create_consensus_msg(if distinct_messages { height } else { 0 }, is_vote); - let broadcasted_message_manager = create_test_broadcasted_message_manager(); + let broadcasted_message_manager = + BroadcastedMessageManager::get_test_instance(&mut get_rng()); mock_network .broadcasted_messages_sender .send((msg.clone(), broadcasted_message_manager)) diff --git a/crates/sequencing/papyrus_consensus/src/stream_handler.rs b/crates/sequencing/papyrus_consensus/src/stream_handler.rs index 2cf763b5c44..120182af0cc 100644 --- a/crates/sequencing/papyrus_consensus/src/stream_handler.rs +++ b/crates/sequencing/papyrus_consensus/src/stream_handler.rs @@ -1,10 +1,17 @@ //! Stream handler, see StreamManager struct. use std::collections::hash_map::Entry; -use std::collections::{BTreeMap, HashMap}; +use std::collections::{BTreeMap, HashMap, HashSet}; use futures::channel::mpsc; use futures::StreamExt; -use papyrus_protobuf::consensus::StreamMessage; +use papyrus_network::network_manager::{ + BroadcastTopicClient, + BroadcastTopicClientTrait, + BroadcastTopicServer, +}; +use papyrus_network::utils::StreamHashMap; +use papyrus_network_types::network_types::{BroadcastedMessageManager, OpaquePeerId}; +use papyrus_protobuf::consensus::{StreamMessage, StreamMessageBody}; use papyrus_protobuf::converters::ProtobufConversionError; use tracing::{instrument, warn}; @@ -12,19 +19,26 @@ use tracing::{instrument, warn}; #[path = "stream_handler_test.rs"] mod stream_handler_test; +type PeerId = OpaquePeerId; type StreamId = u64; type MessageId = u64; +type StreamKey = (PeerId, StreamId); + const CHANNEL_BUFFER_LENGTH: usize = 100; +fn get_metadata_peer_id(metadata: BroadcastedMessageManager) -> PeerId { + metadata.originator_id +} + #[derive(Debug, Clone)] struct StreamData> + TryFrom, Error = ProtobufConversionError>> { - // The next message_id that is expected. + peer_id: PeerId, + next_message_id: MessageId, // The message_id of the message that is marked as "fin" (the last message), // if None, it means we have not yet gotten to it. fin_message_id: Option, - // The highest message_id that was received. max_message_id: MessageId, // The sender that corresponds to the receiver that was sent out for this stream. @@ -35,8 +49,9 @@ struct StreamData> + TryFrom, Error = ProtobufCo } impl> + TryFrom, Error = ProtobufConversionError>> StreamData { - fn new(sender: mpsc::Sender) -> Self { + fn new(peer_id: PeerId, sender: mpsc::Sender) -> Self { StreamData { + peer_id, next_message_id: 0, fin_message_id: None, max_message_id: 0, @@ -51,60 +66,146 @@ pub struct StreamHandler< T: Clone + Into> + TryFrom, Error = ProtobufConversionError>, > { // An end of a channel used to send out receivers, one for each stream. - sender: mpsc::Sender>, + listen_channel_sender: mpsc::Sender>, // An end of a channel used to receive messages. - receiver: mpsc::Receiver>, + listen_receiver: BroadcastTopicServer>, + + // A network sender that allows sending StreamMessages to peers. + broadcast_sender: BroadcastTopicClient>, + + // A receiver of receivers, one for each stream_id. + broadcast_channel_receiver: mpsc::Receiver<(StreamId, mpsc::Receiver)>, // A map from stream_id to a struct that contains all the information about the stream. // This includes both the message buffer and some metadata (like the latest message_id). - stream_data: HashMap>, - // TODO(guyn): perhaps make input_stream_data and output_stream_data? + listen_stream_data: HashMap>, + + // For each stream that goes out to broadcast, there is a receiver. + broadcast_stream_receivers: StreamHashMap>, + broadcast_stream_number: HashMap, } -impl> + TryFrom, Error = ProtobufConversionError>> +impl> + TryFrom, Error = ProtobufConversionError>> StreamHandler { /// Create a new StreamHandler. pub fn new( - sender: mpsc::Sender>, - receiver: mpsc::Receiver>, + listen_channel_sender: mpsc::Sender>, + listen_receiver: BroadcastTopicServer>, + broadcast_sender: BroadcastTopicClient>, + broadcast_channel_receiver: mpsc::Receiver<(StreamId, mpsc::Receiver)>, ) -> Self { - StreamHandler { sender, receiver, stream_data: HashMap::new() } + StreamHandler { + listen_channel_sender, + listen_receiver, + broadcast_sender, + broadcast_channel_receiver, + listen_stream_data: HashMap::new(), + broadcast_stream_receivers: StreamHashMap::new(HashMap::new()), + broadcast_stream_number: HashMap::new(), + } } /// Listen for messages on the receiver channel, buffering them if necessary. /// Guarantees that messages are sent in order. - pub async fn listen(&mut self) { + pub async fn run(&mut self) { loop { - if let Some(message) = self.receiver.next().await { - self.handle_message(message); - } + // The StreamHashMap doesn't report back that some of the channels were closed, + // but the relevant keys are removed when that happens. So check before-after: + let before: HashSet<_> = self.broadcast_stream_receivers.keys().cloned().collect(); + + // Go over the broadcast_channel_receiver to see if there is a new receiver, + // and go over all existing broadcast_receivers to see if there are any messages to + // send. Finally, check if there is an input message from the network. + tokio::select!( + Some((stream_id, receiver)) = self.broadcast_channel_receiver.next() => { + self.broadcast_stream_receivers.insert(stream_id, receiver); + } + output = self.broadcast_stream_receivers.next() => { + match output { + Some((key, message)) => { + println!("Got message! "); + self.broadcast(key, message).await; + } + None => { + let after: HashSet<_> = self.broadcast_stream_receivers.keys().cloned().collect(); + println!("before: {:?} | after: {:?}", before, after); + let diff = before.difference(&after).collect::>(); + for key in diff { + println!("Removing key: {:?}", key); + self.broadcast_fin(*key).await; + } + } + } + } + Some(message) = self.listen_receiver.next() => { + self.handle_message(message); + } + ); } } - fn send(data: &mut StreamData, message: StreamMessage) { + fn internal_send(data: &mut StreamData, message: StreamMessage) { // TODO(guyn): reconsider the "expect" here. let sender = &mut data.sender; - sender.try_send(message.message).expect("Send should succeed"); - data.next_message_id += 1; + if let StreamMessageBody::Content(content) = message.message { + sender.try_send(content).expect("Send should succeed"); + data.next_message_id += 1; + } + } + + // Send the message to the network. + async fn broadcast(self: &mut Self, key: StreamId, message: T) { + let message = StreamMessage { + message: StreamMessageBody::Content(message), + stream_id: key, + message_id: *self.broadcast_stream_number.get(&key).unwrap_or(&0), + }; + // TODO(guyn): reconsider the "expect" here. + self.broadcast_sender.broadcast_message(message).await.expect("Send should succeed"); + self.broadcast_stream_number + .insert(key, self.broadcast_stream_number.get(&key).unwrap_or(&0) + 1); + } + + // Send a fin message to the network. + async fn broadcast_fin(self: &mut Self, key: StreamId) { + let message = StreamMessage { + message: StreamMessageBody::Fin, + stream_id: key, + message_id: *self.broadcast_stream_number.get(&key).unwrap_or(&0), + }; + self.broadcast_sender.broadcast_message(message).await.expect("Send should succeed"); + self.broadcast_stream_number.remove(&key); } - // Handle the message, return true if the channel is still open. #[instrument(skip_all, level = "warn")] - fn handle_message(&mut self, message: StreamMessage) { + fn handle_message( + &mut self, + message: (Result, ProtobufConversionError>, BroadcastedMessageManager), + ) { + let (message, metadata) = message; + let message = match message { + Ok(message) => message, + Err(e) => { + warn!("Error converting message: {:?}", e); + return; + } + }; + let peer_id = get_metadata_peer_id(metadata); // TODO(guyn): use peer_id let stream_id = message.stream_id; + let key = (peer_id.clone(), stream_id); let message_id = message.message_id; - let data = match self.stream_data.entry(stream_id) { + let data = match self.listen_stream_data.entry(key.clone()) { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(e) => { // If we received a message for a stream that we have not seen before, // we need to create a new receiver for it. let (sender, receiver) = mpsc::channel(CHANNEL_BUFFER_LENGTH); // TODO(guyn): reconsider the "expect" here. - self.sender.try_send(receiver).expect("Send should succeed"); + self.listen_channel_sender.try_send(receiver).expect("Send should succeed"); - let data = StreamData::new(sender); + let data = StreamData::new(peer_id.clone(), sender); e.insert(data) } }; @@ -113,25 +214,29 @@ impl> + TryFrom, Error = ProtobufConversionError data.max_message_id = message_id; } - if message.fin { - data.fin_message_id = Some(message_id); - if data.max_message_id > message_id { - // TODO(guyn): replace warnings with more graceful error handling - warn!( - "Received fin message with id that is smaller than a previous message! \ - stream_id: {}, fin_message_id: {}, max_message_id: {}", - stream_id, message_id, data.max_message_id - ); - return; + // Check for Fin type message + match message.message { + StreamMessageBody::Content(_) => {} + StreamMessageBody::Fin => { + data.fin_message_id = Some(message_id); + if data.max_message_id > message_id { + // TODO(guyn): replace warnings with more graceful error handling + warn!( + "Received fin message with id that is smaller than a previous message! \ + stream_id: {}, fin_message_id: {}, max_message_id: {}", + stream_id, message_id, data.max_message_id + ); + return; + } } } - // Check that message_id is not bigger than the fin_message_id. if message_id > data.fin_message_id.unwrap_or(u64::MAX) { // TODO(guyn): replace warnings with more graceful error handling warn!( - "Received message with id that is bigger than the id of the fin message! \ - stream_id: {}, message_id: {}, fin_message_id: {}", + "Received message with id that is bigger than the id of the fin message! peer_id: \ + {:?}, stream_id: {}, message_id: {}, fin_message_id: {}", + peer_id.clone(), stream_id, message_id, data.fin_message_id.unwrap_or(u64::MAX) @@ -141,25 +246,22 @@ impl> + TryFrom, Error = ProtobufConversionError // This means we can just send the message without buffering it. if message_id == data.next_message_id { - Self::send(data, message); + Self::internal_send(data, message); - // Try to drain the buffer. Self::process_buffer(data); - // If empty, remove this buffer and close the channel. if data.message_buffer.is_empty() && data.fin_message_id.is_some() { data.sender.close_channel(); - self.stream_data.remove(&stream_id); + self.listen_stream_data.remove(&key); } } else if message_id > data.next_message_id { - // Save the message in the buffer. Self::store(data, message); } else { // TODO(guyn): replace warnings with more graceful error handling warn!( "Received message with id that is smaller than the next message expected! \ - stream_id: {}, message_id: {}, next_message_id: {}", - stream_id, message_id, data.next_message_id + peer_id: {:?}, stream_id: {}, message_id: {}, next_message_id: {}", + peer_id, stream_id, message_id, data.next_message_id ); return; } @@ -172,8 +274,11 @@ impl> + TryFrom, Error = ProtobufConversionError if data.message_buffer.contains_key(&message_id) { // TODO(guyn): replace warnings with more graceful error handling warn!( - "Two messages with the same message_id in buffer! stream_id: {}, message_id: {}", - stream_id, message_id + "Two messages with the same message_id in buffer! peer_id: {:?}, stream_id: {}, \ + message_id: {}", + data.peer_id.clone(), + stream_id, + message_id ); } else { data.message_buffer.insert(message_id, message); @@ -184,7 +289,7 @@ impl> + TryFrom, Error = ProtobufConversionError // DOES NOT guarantee that the buffer will be empty after calling this function. fn process_buffer(data: &mut StreamData) { while let Some(message) = data.message_buffer.remove(&data.next_message_id) { - Self::send(data, message); + Self::internal_send(data, message); } } } diff --git a/crates/sequencing/papyrus_consensus/src/stream_handler_test.rs b/crates/sequencing/papyrus_consensus/src/stream_handler_test.rs index 26101c9647f..5e5d75953c8 100644 --- a/crates/sequencing/papyrus_consensus/src/stream_handler_test.rs +++ b/crates/sequencing/papyrus_consensus/src/stream_handler_test.rs @@ -1,26 +1,41 @@ +use std::time::Duration; + use futures::channel::mpsc; use futures::stream::StreamExt; -use papyrus_protobuf::consensus::{ConsensusMessage, Proposal, StreamMessage}; - -use super::StreamHandler; +use futures::SinkExt; +use papyrus_network::network_manager::test_utils::{ + mock_register_broadcast_topic, + MockBroadcastedMessagesSender, + TestSubscriberChannels, +}; +use papyrus_network::network_manager::BroadcastTopicChannels; +use papyrus_protobuf::consensus::{ConsensusMessage, Proposal, StreamMessage, StreamMessageBody}; +use papyrus_test_utils::{get_rng, GetTestInstance}; + +use super::{get_metadata_peer_id, StreamHandler}; #[cfg(test)] mod tests { - use std::time::Duration; + + use core::net; + + use papyrus_network::network_manager::test_utils::BroadcastNetworkMock; + use papyrus_network::network_manager::{BroadcastTopicClientTrait, BroadcastTopicServer}; + use papyrus_network_types::network_types::BroadcastedMessageManager; use super::*; + use crate::stream_handler; fn make_test_message( stream_id: u64, message_id: u64, fin: bool, ) -> StreamMessage { - StreamMessage { - message: ConsensusMessage::Proposal(Proposal::default()), - stream_id, - message_id, - fin, - } + let content = match fin { + true => StreamMessageBody::Fin, + false => StreamMessageBody::Content(ConsensusMessage::Proposal(Proposal::default())), + }; + StreamMessage { message: content, stream_id, message_id } } // Check if two vectors are the same: @@ -29,35 +44,101 @@ mod tests { matching == a.len() && matching == b.len() } + async fn send( + sender: &mut MockBroadcastedMessagesSender>, + metadata: &BroadcastedMessageManager, + msg: StreamMessage, + ) { + sender.send((msg, metadata.clone())).await.unwrap(); + } + fn setup_test() -> ( StreamHandler, - mpsc::Sender>, + MockBroadcastedMessagesSender>, mpsc::Receiver>, + BroadcastedMessageManager, + mpsc::Sender<(u64, mpsc::Receiver)>, + futures::stream::Map< + mpsc::Receiver>, + fn(Vec) -> StreamMessage, + >, /* BroadcastTopicServer>, + * BroadcastNetworkMock>, */ ) { - let (tx_input, rx_input) = mpsc::channel::>(100); - let (tx_output, rx_output) = mpsc::channel::>(100); - let handler = StreamHandler::new(tx_output, rx_input); - (handler, tx_input, rx_output) + // The network_broadcast_sender is the network connector for broadcasting messages. + // The broadcasted_messages_receiver is used to catch those messages in the test. + let TestSubscriberChannels { mock_network: mock_broadcast_network, subscriber_channels } = + mock_register_broadcast_topic().unwrap(); + let BroadcastTopicChannels { + broadcasted_messages_receiver: _, // network_broadcast_receiver, + broadcast_topic_client: network_broadcast_sender, + } = subscriber_channels; + + let network_broadcast_receiver = mock_broadcast_network.messages_to_broadcast_receiver; + + // This is used to feed receivers of messages to StreamHandler for broadcasting. + // The receiver goes into StreamHandler, sender is used by the test (as mock Consensus). + // Note that each new channel comes in a tuple with (stream_id, receiver). + let (broadcast_channel_sender, broadcast_channel_receiver) = + mpsc::channel::<(u64, mpsc::Receiver)>(100); + + // The network_sender_to_listen is the sender of the mock network, that is used to send + // messages into the StreamHandler (from the mock network). + let TestSubscriberChannels { mock_network, subscriber_channels } = + mock_register_broadcast_topic().unwrap(); + let network_sender_to_listen = mock_network.broadcasted_messages_sender; + + // The listen_receiver is given to StreamHandler to listen to mock network messages. + let BroadcastTopicChannels { + broadcasted_messages_receiver: listen_receiver, + broadcast_topic_client: _, + } = subscriber_channels; + + // The listen_channel_sender is given to StreamHandler so it can output new channels for + // each stream. The listen_channel_receiver is given to the "mock consensus" that + // gets new channels and listens to them. + let (listen_channel_sender, listen_channel_receiver) = + mpsc::channel::>(100); + + // TODO(guyn): We should also give the broadcast_topic_client to the StreamHandler + let handler = StreamHandler::new( + listen_channel_sender, + listen_receiver, + network_broadcast_sender, + broadcast_channel_receiver, + ); + + let listen_metadata = BroadcastedMessageManager::get_test_instance(&mut get_rng()); + + ( + handler, + network_sender_to_listen, + listen_channel_receiver, + listen_metadata, + broadcast_channel_sender, + network_broadcast_receiver, + ) } #[tokio::test] - async fn stream_handler_in_order() { - let (mut stream_handler, mut tx_input, mut rx_output) = setup_test(); + async fn stream_handler_listen_in_order() { + let (mut stream_handler, mut network_sender, mut listen_channel_receiver, metadata, _, _) = + setup_test(); let stream_id = 127; for i in 0..10 { let message = make_test_message(stream_id, i, i == 9); - tx_input.try_send(message).expect("Send should succeed"); + send(&mut network_sender, &metadata, message).await; } let join_handle = tokio::spawn(async move { - let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.listen()).await; + let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.run()).await; }); join_handle.await.expect("Task should succeed"); - let mut receiver = rx_output.next().await.unwrap(); - for _ in 0..10 { + let mut receiver = listen_channel_receiver.next().await.unwrap(); + for _ in 0..9 { + // message number 9 is Fin, so it will not be sent! let _ = receiver.next().await.unwrap(); } // Check that the receiver was closed: @@ -65,45 +146,58 @@ mod tests { } #[tokio::test] - async fn stream_handler_in_reverse() { - let (mut stream_handler, mut tx_input, mut rx_output) = setup_test(); - + async fn stream_handler_listen_in_reverse() { + let ( + mut stream_handler, + mut network_sender, + mut listen_channel_receiver, + listen_metadata, + _, + _, + ) = setup_test(); + let peer_id = get_metadata_peer_id(&listen_metadata); let stream_id = 127; + for i in 0..5 { let message = make_test_message(stream_id, 5 - i, i == 0); - tx_input.try_send(message).expect("Send should succeed"); + send(&mut network_sender, &listen_metadata, message).await; } - let join_handle = tokio::spawn(async move { - let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.listen()).await; + let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.run()).await; stream_handler }); let mut stream_handler = join_handle.await.expect("Task should succeed"); // Get the receiver for the stream. - let mut receiver = rx_output.next().await.unwrap(); + let mut receiver = listen_channel_receiver.next().await.unwrap(); // Check that the channel is empty (no messages were sent yet). assert!(receiver.try_next().is_err()); - assert_eq!(stream_handler.stream_data.len(), 1); - assert_eq!(stream_handler.stream_data[&stream_id].message_buffer.len(), 5); + assert_eq!(stream_handler.listen_stream_data.len(), 1); + assert_eq!( + stream_handler.listen_stream_data[&(peer_id.clone(), stream_id)].message_buffer.len(), + 5 + ); let range: Vec = (1..6).collect(); - let keys: Vec = - stream_handler.stream_data[&stream_id].message_buffer.clone().into_keys().collect(); + let keys: Vec = stream_handler.listen_stream_data[&(peer_id, stream_id)] + .clone() + .message_buffer + .into_keys() + .collect(); assert!(do_vecs_match(&keys, &range)); // Now send the last message: - tx_input.try_send(make_test_message(stream_id, 0, false)).expect("Send should succeed"); - + send(&mut network_sender, &listen_metadata, make_test_message(stream_id, 0, false)).await; let join_handle = tokio::spawn(async move { - let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.listen()).await; + let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.run()).await; stream_handler }); let stream_handler = join_handle.await.expect("Task should succeed"); - assert!(stream_handler.stream_data.is_empty()); + assert!(stream_handler.listen_stream_data.is_empty()); - for _ in 0..6 { + for _ in 0..5 { + // message number 5 is Fin, so it will not be sent! let _ = receiver.next().await.unwrap(); } // Check that the receiver was closed: @@ -111,44 +205,58 @@ mod tests { } #[tokio::test] - async fn stream_handler_multiple_streams() { - let (mut stream_handler, mut tx_input, mut rx_output) = setup_test(); + async fn stream_handler_listen_multiple_streams() { + let ( + mut stream_handler, + mut network_sender, + mut listen_channel_receiver, + listen_metadata, + _, + _, + ) = setup_test(); + let peer_id = get_metadata_peer_id(&listen_metadata); let stream_id1 = 127; // Send all messages in order (except the first one). let stream_id2 = 10; // Send in reverse order (except the first one). - let stream_id3 = 1; // Send in two batches of 5 messages, without the first one, don't send fin. + let stream_id3 = 1; // Send in two batches, without the first one, don't send fin. for i in 1..10 { let message = make_test_message(stream_id1, i, i == 9); - tx_input.try_send(message).expect("Send should succeed"); + send(&mut network_sender, &listen_metadata, message).await; } for i in 0..5 { let message = make_test_message(stream_id2, 5 - i, i == 0); - tx_input.try_send(message).expect("Send should succeed"); + send(&mut network_sender, &listen_metadata, message).await; } for i in 5..10 { let message = make_test_message(stream_id3, i, false); - tx_input.try_send(message).expect("Send should succeed"); + send(&mut network_sender, &listen_metadata, message).await; } for i in 1..5 { let message = make_test_message(stream_id3, i, false); - tx_input.try_send(message).expect("Send should succeed"); + send(&mut network_sender, &listen_metadata, message).await; } let join_handle = tokio::spawn(async move { - let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.listen()).await; + let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.run()).await; stream_handler }); let mut stream_handler = join_handle.await.expect("Task should succeed"); - let values = vec![1, 10, 127]; - assert!(stream_handler.stream_data.clone().into_keys().all(|item| values.contains(&item))); + let values = vec![(peer_id.clone(), 1), (peer_id.clone(), 10), (peer_id.clone(), 127)]; + assert!( + stream_handler + .listen_stream_data + .clone() + .into_keys() + .all(|item| values.contains(&item)) + ); // We have all message from 1 to 9 buffered. assert!(do_vecs_match( - &stream_handler.stream_data[&stream_id1] + &stream_handler.listen_stream_data[&(peer_id.clone(), stream_id1)] .message_buffer .clone() .into_keys() @@ -158,7 +266,7 @@ mod tests { // We have all message from 1 to 5 buffered. assert!(do_vecs_match( - &stream_handler.stream_data[&stream_id2] + &stream_handler.listen_stream_data[&(peer_id.clone(), stream_id2)] .message_buffer .clone() .into_keys() @@ -168,7 +276,7 @@ mod tests { // We have all message from 1 to 5 buffered. assert!(do_vecs_match( - &stream_handler.stream_data[&stream_id3] + &stream_handler.listen_stream_data[&(peer_id.clone(), stream_id3)] .message_buffer .clone() .into_keys() @@ -177,34 +285,35 @@ mod tests { )); // Get the receiver for the first stream. - let mut receiver1 = rx_output.next().await.unwrap(); + let mut receiver1 = listen_channel_receiver.next().await.unwrap(); // Check that the channel is empty (no messages were sent yet). assert!(receiver1.try_next().is_err()); // Get the receiver for the second stream. - let mut receiver2 = rx_output.next().await.unwrap(); + let mut receiver2 = listen_channel_receiver.next().await.unwrap(); // Check that the channel is empty (no messages were sent yet). assert!(receiver2.try_next().is_err()); // Get the receiver for the third stream. - let mut receiver3 = rx_output.next().await.unwrap(); + let mut receiver3 = listen_channel_receiver.next().await.unwrap(); // Check that the channel is empty (no messages were sent yet). assert!(receiver3.try_next().is_err()); // Send the last message on stream_id1: - tx_input.try_send(make_test_message(stream_id1, 0, false)).expect("Send should succeed"); + send(&mut network_sender, &listen_metadata, make_test_message(stream_id1, 0, false)).await; let join_handle = tokio::spawn(async move { - let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.listen()).await; + let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.run()).await; stream_handler }); let mut stream_handler = join_handle.await.expect("Task should succeed"); // Should be able to read all the messages for stream_id1. - for _ in 0..10 { + for _ in 0..9 { + // message number 9 is Fin, so it will not be sent! let _ = receiver1.next().await.unwrap(); } @@ -212,20 +321,27 @@ mod tests { assert!(matches!(receiver1.try_next(), Ok(None))); // stream_id1 should be gone - let values = vec![1, 10]; - assert!(stream_handler.stream_data.clone().into_keys().all(|item| values.contains(&item))); + let values = vec![(peer_id.clone(), 1), (peer_id.clone(), 10)]; + assert!( + stream_handler + .listen_stream_data + .clone() + .into_keys() + .all(|item| values.contains(&item)) + ); // Send the last message on stream_id2: - tx_input.try_send(make_test_message(stream_id2, 0, false)).expect("Send should succeed"); + send(&mut network_sender, &listen_metadata, make_test_message(stream_id2, 0, false)).await; let join_handle = tokio::spawn(async move { - let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.listen()).await; + let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.run()).await; stream_handler }); let mut stream_handler = join_handle.await.expect("Task should succeed"); // Should be able to read all the messages for stream_id2. - for _ in 0..6 { + for _ in 0..5 { + // message number 5 is Fin, so it will not be sent! let _ = receiver2.next().await.unwrap(); } @@ -233,19 +349,26 @@ mod tests { assert!(matches!(receiver2.try_next(), Ok(None))); // Stream_id2 should also be gone. - let values = vec![1]; - assert!(stream_handler.stream_data.clone().into_keys().all(|item| values.contains(&item))); + let values = vec![(peer_id.clone(), 1)]; + assert!( + stream_handler + .listen_stream_data + .clone() + .into_keys() + .all(|item| values.contains(&item)) + ); // Send the last message on stream_id3: - tx_input.try_send(make_test_message(stream_id3, 0, false)).expect("Send should succeed"); + send(&mut network_sender, &listen_metadata, make_test_message(stream_id3, 0, false)).await; let join_handle = tokio::spawn(async move { - let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.listen()).await; + let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.run()).await; stream_handler }); let stream_handler = join_handle.await.expect("Task should succeed"); for _ in 0..10 { + // All messages are received, including number 9 which is not Fin let _ = receiver3.next().await.unwrap(); } @@ -253,10 +376,131 @@ mod tests { assert!(matches!(receiver3.try_next(), Err(_))); // Stream_id3 should still be there, because we didn't send a fin. - let values = vec![1]; - assert!(stream_handler.stream_data.clone().into_keys().all(|item| values.contains(&item))); + let values = vec![(peer_id.clone(), 1)]; + assert!( + stream_handler + .listen_stream_data + .clone() + .into_keys() + .all(|item| values.contains(&item)) + ); // But the buffer should be empty, as we've successfully drained it all. - assert!(stream_handler.stream_data[&stream_id3].message_buffer.is_empty()); + assert!( + stream_handler.listen_stream_data[&(peer_id, stream_id3)].message_buffer.is_empty() + ); + } + + #[tokio::test] + async fn stream_handler_broadcast() { + let ( + mut stream_handler, + _, + _, + _, + mut broadcast_channel_sender, + mut broadcasted_messages_receiver, + ) = setup_test(); + + let stream_id1 = 42_u64; + let stream_id2 = 127_u64; + + let join_handle = tokio::spawn(async move { + let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.run()).await; + stream_handler + }); + + // Start a new stream by sending the (stream_id, receiver). + let (mut sender1, receiver1) = mpsc::channel(100); + broadcast_channel_sender.send((stream_id1, receiver1)).await.unwrap(); + + // Send a message on the stream. + let message1 = ConsensusMessage::Proposal(Proposal::default()); + sender1.send(message1.clone()).await.unwrap(); + + // Wait for an incoming message. + let broadcasted_message = broadcasted_messages_receiver.next().await.unwrap(); + let mut stream_handler = join_handle.await.expect("Task should succeed"); + + // Check that message was broadcasted. + assert_eq!(broadcasted_message.message, StreamMessageBody::Content(message1)); + assert_eq!(broadcasted_message.stream_id, stream_id1); + assert_eq!(broadcasted_message.message_id, 0); + + // Check that internally, stream_handler holds this receiver. + assert_eq!( + stream_handler.broadcast_stream_receivers.keys().collect::>(), + vec![&stream_id1] + ); + // Check that the number of messages sent on this stream is 1. + assert_eq!(stream_handler.broadcast_stream_number[&stream_id1], 1); + + // Send another message on the same stream. + let join_handle = tokio::spawn(async move { + let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.run()).await; + stream_handler + }); + + let message2 = ConsensusMessage::Proposal(Proposal::default()); + sender1.send(message2.clone()).await.unwrap(); + + // Wait for an incoming message. + let broadcasted_message = broadcasted_messages_receiver.next().await.unwrap(); + + let mut stream_handler = join_handle.await.expect("Task should succeed"); + + // Check that message was broadcasted. + assert_eq!(broadcasted_message.message, StreamMessageBody::Content(message2)); + assert_eq!(broadcasted_message.stream_id, stream_id1); + assert_eq!(broadcasted_message.message_id, 1); + assert_eq!(stream_handler.broadcast_stream_number[&stream_id1], 2); + + // Start a new stream by sending the (stream_id, receiver). + let (mut sender2, receiver2) = mpsc::channel(100); + broadcast_channel_sender.send((stream_id2, receiver2)).await.unwrap(); + + let join_handle = tokio::spawn(async move { + let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.run()).await; + stream_handler + }); + + // Send a message on the stream. + let message3 = ConsensusMessage::Proposal(Proposal::default()); + sender2.send(message3.clone()).await.unwrap(); + + // Wait for an incoming message. + let broadcasted_message = broadcasted_messages_receiver.next().await.unwrap(); + + let mut stream_handler = join_handle.await.expect("Task should succeed"); + + // Check that message was broadcasted. + assert_eq!(broadcasted_message.message, StreamMessageBody::Content(message3)); + assert_eq!(broadcasted_message.stream_id, stream_id2); + assert_eq!(broadcasted_message.message_id, 0); + assert_eq!( + stream_handler.broadcast_stream_receivers.keys().collect::>().sort(), + vec![&stream_id1, &stream_id2].sort() + ); + assert_eq!(stream_handler.broadcast_stream_number[&stream_id2], 1); + + // Close the first channel. + + let join_handle = tokio::spawn(async move { + let _ = tokio::time::timeout(Duration::from_millis(100), stream_handler.run()).await; + stream_handler + }); + + sender1.close_channel(); + + // Check that we got a fin message. + let broadcasted_message = broadcasted_messages_receiver.next().await.unwrap(); + // assert_eq!(broadcasted_message.message, StreamMessageBody::Fin); + + let mut stream_handler = join_handle.await.expect("Task should succeed"); + println!("{:?}", stream_handler.broadcast_stream_receivers.keys().collect::>()); + + // Check that the information about this stream is gone. + // assert_eq!(stream_handler.broadcast_stream_receivers.keys().collect::>(), + // vec![&stream_id2]); } } diff --git a/crates/sequencing/papyrus_consensus/src/types.rs b/crates/sequencing/papyrus_consensus/src/types.rs index 420b70c1e2c..3e29c1528e7 100644 --- a/crates/sequencing/papyrus_consensus/src/types.rs +++ b/crates/sequencing/papyrus_consensus/src/types.rs @@ -2,6 +2,12 @@ use std::fmt::Debug; use async_trait::async_trait; use futures::channel::{mpsc, oneshot}; +use papyrus_network::network_manager::{ + BroadcastTopicChannels, + BroadcastTopicClient, + GenericReceiver, +}; +use papyrus_network_types::network_types::BroadcastedMessageManager; use papyrus_protobuf::consensus::{ConsensusMessage, Vote}; use papyrus_protobuf::converters::ProtobufConversionError; use starknet_api::block::{BlockHash, BlockNumber}; @@ -142,6 +148,25 @@ impl From<(BlockNumber, u32, ContractAddress, Option)> for ProposalInit { } } +pub struct BroadcastConsensusMessageChannel { + pub broadcasted_messages_receiver: GenericReceiver<( + Result, + BroadcastedMessageManager, + )>, + pub broadcast_topic_client: BroadcastTopicClient, +} + +impl From> for BroadcastConsensusMessageChannel { + fn from(broadcast_topic_client: BroadcastTopicChannels) -> Self { + BroadcastConsensusMessageChannel { + broadcasted_messages_receiver: Box::new( + broadcast_topic_client.broadcasted_messages_receiver, + ), + broadcast_topic_client: broadcast_topic_client.broadcast_topic_client, + } + } +} + #[derive(thiserror::Error, PartialEq, Debug)] pub enum ConsensusError { #[error(transparent)] diff --git a/crates/sequencing/papyrus_consensus_orchestrator/src/papyrus_consensus_context.rs b/crates/sequencing/papyrus_consensus_orchestrator/src/papyrus_consensus_context.rs index 44cebff9ac1..63d858f4db4 100644 --- a/crates/sequencing/papyrus_consensus_orchestrator/src/papyrus_consensus_context.rs +++ b/crates/sequencing/papyrus_consensus_orchestrator/src/papyrus_consensus_context.rs @@ -9,7 +9,6 @@ use std::time::Duration; use async_trait::async_trait; use futures::channel::{mpsc, oneshot}; -use futures::sink::SinkExt; use futures::StreamExt; use papyrus_consensus::types::{ ConsensusContext, @@ -19,7 +18,7 @@ use papyrus_consensus::types::{ Round, ValidatorId, }; -use papyrus_network::network_manager::BroadcastTopicSender; +use papyrus_network::network_manager::{BroadcastTopicClient, BroadcastTopicClientTrait}; use papyrus_protobuf::consensus::{ConsensusMessage, Proposal, Vote}; use papyrus_storage::body::BodyStorageReader; use papyrus_storage::header::HeaderStorageReader; @@ -35,9 +34,9 @@ type HeightToIdToContent = BTreeMap, + network_broadcast_client: BroadcastTopicClient, validators: Vec, - sync_broadcast_sender: Option>, + sync_broadcast_sender: Option>, // Proposal building/validating returns immediately, leaving the actual processing to a spawned // task. The spawned task processes the proposal asynchronously and updates the // valid_proposals map upon completion, ensuring consistency across tasks. @@ -47,13 +46,13 @@ pub struct PapyrusConsensusContext { impl PapyrusConsensusContext { pub fn new( storage_reader: StorageReader, - network_broadcast_sender: BroadcastTopicSender, + network_broadcast_client: BroadcastTopicClient, num_validators: u64, - sync_broadcast_sender: Option>, + sync_broadcast_sender: Option>, ) -> Self { Self { storage_reader, - network_broadcast_sender, + network_broadcast_client, validators: (0..num_validators).map(ContractAddress::from).collect(), sync_broadcast_sender, valid_proposals: Arc::new(Mutex::new(BTreeMap::new())), @@ -223,7 +222,7 @@ impl ConsensusContext for PapyrusConsensusContext { async fn broadcast(&mut self, message: ConsensusMessage) -> Result<(), ConsensusError> { debug!("Broadcasting message: {message:?}"); - self.network_broadcast_sender.send(message).await?; + self.network_broadcast_client.broadcast_message(message).await?; Ok(()) } @@ -233,7 +232,7 @@ impl ConsensusContext for PapyrusConsensusContext { mut content_receiver: mpsc::Receiver, fin_receiver: oneshot::Receiver, ) -> Result<(), ConsensusError> { - let mut network_broadcast_sender = self.network_broadcast_sender.clone(); + let mut network_broadcast_sender = self.network_broadcast_client.clone(); tokio::spawn( async move { @@ -264,7 +263,7 @@ impl ConsensusContext for PapyrusConsensusContext { ); network_broadcast_sender - .send(ConsensusMessage::Proposal(proposal)) + .broadcast_message(ConsensusMessage::Proposal(proposal)) .await .expect("Failed to send proposal"); } @@ -281,7 +280,7 @@ impl ConsensusContext for PapyrusConsensusContext { let height = precommits[0].height; info!("Finished consensus for height: {height}. Agreed on block: {:}", block); if let Some(sender) = &mut self.sync_broadcast_sender { - sender.send(precommits[0].clone()).await?; + sender.broadcast_message(precommits[0].clone()).await?; } let mut proposals = self diff --git a/crates/sequencing/papyrus_consensus_orchestrator/src/papyrus_consensus_context_test.rs b/crates/sequencing/papyrus_consensus_orchestrator/src/papyrus_consensus_context_test.rs index 3a5bb4d97ca..f2852ef6299 100644 --- a/crates/sequencing/papyrus_consensus_orchestrator/src/papyrus_consensus_context_test.rs +++ b/crates/sequencing/papyrus_consensus_orchestrator/src/papyrus_consensus_context_test.rs @@ -136,9 +136,9 @@ fn test_setup() -> ( let sync_channels = mock_register_broadcast_topic().unwrap(); let papyrus_context = PapyrusConsensusContext::new( storage_reader.clone(), - network_channels.subscriber_channels.messages_to_broadcast_sender, + network_channels.subscriber_channels.broadcast_topic_client, 4, - Some(sync_channels.subscriber_channels.messages_to_broadcast_sender), + Some(sync_channels.subscriber_channels.broadcast_topic_client), ); (block, papyrus_context, network_channels.mock_network, sync_channels.mock_network) } diff --git a/crates/starknet_api/src/block_hash/block_hash_calculator.rs b/crates/starknet_api/src/block_hash/block_hash_calculator.rs index f2d15a61270..27dab67dbdf 100644 --- a/crates/starknet_api/src/block_hash/block_hash_calculator.rs +++ b/crates/starknet_api/src/block_hash/block_hash_calculator.rs @@ -9,7 +9,13 @@ use super::receipt_commitment::{calculate_receipt_commitment, ReceiptElement}; use super::state_diff_hash::calculate_state_diff_hash; use super::transaction_commitment::{calculate_transaction_commitment, TransactionLeafElement}; use crate::block::{BlockHash, BlockHeaderWithoutHash, GasPricePerToken, StarknetVersion}; -use crate::core::{EventCommitment, ReceiptCommitment, StateDiffCommitment, TransactionCommitment}; +use crate::core::{ + ascii_as_felt, + EventCommitment, + ReceiptCommitment, + StateDiffCommitment, + TransactionCommitment, +}; use crate::crypto::utils::HashChain; use crate::data_availability::L1DataAvailabilityMode; use crate::execution_resources::GasVector; @@ -22,7 +28,6 @@ use crate::transaction::{ TransactionHash, TransactionSignature, }; -use crate::transaction_hash::ascii_as_felt; #[cfg(test)] #[path = "block_hash_calculator_test.rs"] @@ -31,6 +36,9 @@ mod block_hash_calculator_test; static STARKNET_BLOCK_HASH0: LazyLock = LazyLock::new(|| { ascii_as_felt("STARKNET_BLOCK_HASH0").expect("ascii_as_felt failed for 'STARKNET_BLOCK_HASH0'") }); +static STARKNET_GAS_PRICES0: LazyLock = LazyLock::new(|| { + ascii_as_felt("STARKNET_GAS_PRICES0").expect("ascii_as_felt failed for 'STARKNET_GAS_PRICES0'") +}); #[allow(non_camel_case_types)] #[derive(Clone, Debug, PartialEq, Eq)] @@ -60,7 +68,7 @@ pub struct TransactionOutputForHash { #[derive(Clone, Debug, Deserialize, Eq, PartialEq)] pub struct TransactionHashingData { - pub transaction_signature: Option, + pub transaction_signature: TransactionSignature, pub transaction_output: TransactionOutputForHash, pub transaction_hash: TransactionHash, } @@ -187,7 +195,7 @@ fn to_64_bits(num: usize) -> [u8; 8] { // For starknet version >= 0.13.3, returns: // [Poseidon ( -// gas_price_wei, gas_price_fri, data_gas_price_wei, data_gas_price_fri, +// "STARKNET_GAS_PRICES0", gas_price_wei, gas_price_fri, data_gas_price_wei, data_gas_price_fri, // l2_gas_price_wei, l2_gas_price_fri // )]. // Otherwise, returns: @@ -201,6 +209,7 @@ fn gas_prices_to_hash( if *starknet_version >= BlockHashVersion::VO_13_3.into() { vec![ HashChain::new() + .chain(&STARKNET_GAS_PRICES0) .chain(&l1_gas_price.price_in_wei.0.into()) .chain(&l1_gas_price.price_in_fri.0.into()) .chain(&l1_data_gas_price.price_in_wei.0.into()) diff --git a/crates/starknet_api/src/block_hash/block_hash_calculator_test.rs b/crates/starknet_api/src/block_hash/block_hash_calculator_test.rs index 6d4e62d1359..e43387f12fc 100644 --- a/crates/starknet_api/src/block_hash/block_hash_calculator_test.rs +++ b/crates/starknet_api/src/block_hash/block_hash_calculator_test.rs @@ -92,7 +92,7 @@ fn test_block_hash_regression( parent_hash: BlockHash(Felt::from(11_u8)), }; let transactions_data = vec![TransactionHashingData { - transaction_signature: Some(TransactionSignature(vec![Felt::TWO, Felt::THREE])), + transaction_signature: TransactionSignature(vec![Felt::TWO, Felt::THREE]), transaction_output: get_transaction_output(), transaction_hash: TransactionHash(Felt::ONE), }]; @@ -106,7 +106,7 @@ fn test_block_hash_regression( felt!("0xe248d6ce583f8fa48d1d401d4beb9ceced3733e38d8eacb0d8d3669a7d901c") } BlockHashVersion::VO_13_3 => { - felt!("0x17c0dc0b67fa9bcf74197b758a6b48eb412cae14397a51a6393f1e0305fe585") + felt!("0x65b653f5bc0939cdc39f98230affc8fbd1a01ea801e025271a4cfba912ba59a") } }; diff --git a/crates/starknet_api/src/block_hash/state_diff_hash.rs b/crates/starknet_api/src/block_hash/state_diff_hash.rs index 9713446d6f3..147f56a2862 100644 --- a/crates/starknet_api/src/block_hash/state_diff_hash.rs +++ b/crates/starknet_api/src/block_hash/state_diff_hash.rs @@ -3,11 +3,17 @@ use std::sync::LazyLock; use indexmap::IndexMap; use starknet_types_core::felt::Felt; -use crate::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce, StateDiffCommitment}; +use crate::core::{ + ascii_as_felt, + ClassHash, + CompiledClassHash, + ContractAddress, + Nonce, + StateDiffCommitment, +}; use crate::crypto::utils::HashChain; use crate::hash::PoseidonHash; use crate::state::{StorageKey, ThinStateDiff}; -use crate::transaction_hash::ascii_as_felt; #[cfg(test)] #[path = "state_diff_hash_test.rs"] diff --git a/crates/starknet_api/src/block_hash/transaction_commitment.rs b/crates/starknet_api/src/block_hash/transaction_commitment.rs index 822b4cbdebb..93d23f31a82 100644 --- a/crates/starknet_api/src/block_hash/transaction_commitment.rs +++ b/crates/starknet_api/src/block_hash/transaction_commitment.rs @@ -15,7 +15,7 @@ mod transaction_commitment_test; #[derive(Clone)] pub struct TransactionLeafElement { pub(crate) transaction_hash: TransactionHash, - pub(crate) transaction_signature: Option, + pub(crate) transaction_signature: TransactionSignature, } impl From<&TransactionHashingData> for TransactionLeafElement { @@ -41,13 +41,6 @@ pub fn calculate_transaction_commitment( fn calculate_transaction_leaf(transaction_leaf_elements: &TransactionLeafElement) -> Felt { HashChain::new() .chain(&transaction_leaf_elements.transaction_hash.0) - .chain_iter( - transaction_leaf_elements - .transaction_signature - .as_ref() - .unwrap_or(&TransactionSignature(vec![])) - .0 - .iter(), - ) + .chain_iter(transaction_leaf_elements.transaction_signature.0.iter()) .get_poseidon_hash() } diff --git a/crates/starknet_api/src/block_hash/transaction_commitment_test.rs b/crates/starknet_api/src/block_hash/transaction_commitment_test.rs index a510e3a4935..bc0d51bb99e 100644 --- a/crates/starknet_api/src/block_hash/transaction_commitment_test.rs +++ b/crates/starknet_api/src/block_hash/transaction_commitment_test.rs @@ -22,7 +22,7 @@ fn test_transaction_leaf_regression() { fn test_transaction_leaf_without_signature_regression() { let transaction_leaf_elements = TransactionLeafElement { transaction_hash: TransactionHash(Felt::ONE), - transaction_signature: None, + transaction_signature: TransactionSignature(vec![]), }; let expected_leaf = felt!("0x579e8877c7755365d5ec1ec7d3a94a457eff5d1f40482bbe9729c064cdead2"); @@ -46,5 +46,5 @@ fn test_transaction_commitment_regression() { fn get_transaction_leaf_element() -> TransactionLeafElement { let transaction_hash = TransactionHash(Felt::ONE); let transaction_signature = TransactionSignature(vec![Felt::TWO, Felt::THREE]); - TransactionLeafElement { transaction_hash, transaction_signature: Some(transaction_signature) } + TransactionLeafElement { transaction_hash, transaction_signature } } diff --git a/crates/starknet_api/src/core.rs b/crates/starknet_api/src/core.rs index 0892aaebbe4..eabfe8f407f 100644 --- a/crates/starknet_api/src/core.rs +++ b/crates/starknet_api/src/core.rs @@ -18,6 +18,12 @@ use crate::serde_utils::{BytesAsHex, PrefixedBytesAsHex}; use crate::transaction::{Calldata, ContractAddressSalt}; use crate::{impl_from_through_intermediate, StarknetApiError}; +/// Felt. +pub fn ascii_as_felt(ascii_str: &str) -> Result { + Felt::from_hex(hex::encode(ascii_str).as_str()) + .map_err(|_| StarknetApiError::OutOfRange { string: ascii_str.to_string() }) +} + /// A chain id. #[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)] pub enum ChainId { diff --git a/crates/starknet_api/src/core_test.rs b/crates/starknet_api/src/core_test.rs index eed3b3900ae..79dee2ca1ca 100644 --- a/crates/starknet_api/src/core_test.rs +++ b/crates/starknet_api/src/core_test.rs @@ -3,7 +3,9 @@ use starknet_types_core::felt::Felt; use starknet_types_core::hash::{Pedersen, StarkHash as CoreStarkHash}; use crate::core::{ + ascii_as_felt, calculate_contract_address, + ChainId, ClassHash, ContractAddress, EthAddress, @@ -101,3 +103,12 @@ fn test_contract_address_display() { String::from("0x") + &"0".repeat(62) + "10" ); } + +#[test] +fn test_ascii_as_felt() { + let sn_main_id = ChainId::Mainnet; + let sn_main_felt = ascii_as_felt(sn_main_id.to_string().as_str()).unwrap(); + // This is the result of the Python snippet from the Chain-Id documentation. + let expected_sn_main = Felt::from(23448594291968334_u128); + assert_eq!(sn_main_felt, expected_sn_main); +} diff --git a/crates/starknet_api/src/executable_transaction.rs b/crates/starknet_api/src/executable_transaction.rs index 9b3a15fa18e..af8934c4cc1 100644 --- a/crates/starknet_api/src/executable_transaction.rs +++ b/crates/starknet_api/src/executable_transaction.rs @@ -191,7 +191,12 @@ impl DeployAccountTransaction { (contract_address_salt, ContractAddressSalt), (nonce, Nonce), (signature, TransactionSignature), - (version, TransactionVersion) + (version, TransactionVersion), + (resource_bounds, ValidResourceBounds), + (tip, Tip), + (nonce_data_availability_mode, DataAvailabilityMode), + (fee_data_availability_mode, DataAvailabilityMode), + (paymaster_data, PaymasterData) ); implement_getter_calls!((tx_hash, TransactionHash), (contract_address, ContractAddress)); @@ -235,7 +240,13 @@ impl InvokeTransaction { (nonce, Nonce), (signature, TransactionSignature), (sender_address, ContractAddress), - (version, TransactionVersion) + (version, TransactionVersion), + (resource_bounds, ValidResourceBounds), + (tip, Tip), + (nonce_data_availability_mode, DataAvailabilityMode), + (fee_data_availability_mode, DataAvailabilityMode), + (paymaster_data, PaymasterData), + (account_deployment_data, AccountDeploymentData) ); implement_getter_calls!((tx_hash, TransactionHash)); diff --git a/crates/starknet_api/src/execution_resources.rs b/crates/starknet_api/src/execution_resources.rs index 9a49d291ea9..07c5d20f25c 100644 --- a/crates/starknet_api/src/execution_resources.rs +++ b/crates/starknet_api/src/execution_resources.rs @@ -3,6 +3,37 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use strum_macros::EnumIter; +#[derive( + derive_more::Add, + derive_more::AddAssign, + derive_more::Sum, + derive_more::Display, + Clone, + Copy, + Debug, + Default, + Eq, + PartialEq, + PartialOrd, + Serialize, + Deserialize, +)] +pub struct GasAmount(pub u128); + +macro_rules! impl_from_uint_for_gas_amount { + ($($uint:ty),*) => { + $( + impl From<$uint> for GasAmount { + fn from(value: $uint) -> Self { + Self(u128::from(value)) + } + } + )* + }; +} + +impl_from_uint_for_gas_amount!(u8, u16, u32, u64, u128); + #[derive(Debug, Default, Deserialize, Serialize, Clone, Eq, PartialEq)] pub struct GasVector { pub l1_gas: u64, diff --git a/crates/starknet_api/src/test_utils.rs b/crates/starknet_api/src/test_utils.rs index c5671d68264..054bbd3c18b 100644 --- a/crates/starknet_api/src/test_utils.rs +++ b/crates/starknet_api/src/test_utils.rs @@ -11,7 +11,7 @@ pub mod declare; pub mod deploy_account; pub mod invoke; -#[derive(Default)] +#[derive(Debug, Default)] pub struct NonceManager { next_nonce: HashMap, } diff --git a/crates/starknet_api/src/transaction.rs b/crates/starknet_api/src/transaction.rs index 3260f1c3f67..80559a30217 100644 --- a/crates/starknet_api/src/transaction.rs +++ b/crates/starknet_api/src/transaction.rs @@ -112,6 +112,17 @@ pub struct TransactionOptions { pub only_query: bool, } +macro_rules! implement_v3_tx_getters { + ($(($field:ident, $field_type:ty)),*) => { + $(pub fn $field(&self) -> $field_type { + match self { + Self::V3(tx) => tx.$field.clone(), + _ => panic!("{:?} do not support the field {}; they are only available for V3 transactions.", self.version(), stringify!($field)), + } + })* + }; +} + /// A transaction output. #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] pub enum TransactionOutput { @@ -281,6 +292,25 @@ impl DeclareTransaction { (signature, TransactionSignature) ); + implement_v3_tx_getters!( + (resource_bounds, ValidResourceBounds), + (tip, Tip), + (nonce_data_availability_mode, DataAvailabilityMode), + (fee_data_availability_mode, DataAvailabilityMode), + (paymaster_data, PaymasterData), + (account_deployment_data, AccountDeploymentData) + ); + + pub fn compiled_class_hash(&self) -> CompiledClassHash { + match self { + DeclareTransaction::V0(_) | DeclareTransaction::V1(_) => { + panic!("Cairo0 DeclareTransaction (V0, V1) doesn't have compiled_class_hash.") + } + DeclareTransaction::V2(tx) => tx.compiled_class_hash, + DeclareTransaction::V3(tx) => tx.compiled_class_hash, + } + } + pub fn version(&self) -> TransactionVersion { match self { DeclareTransaction::V0(_) => TransactionVersion::ZERO, @@ -388,6 +418,14 @@ impl DeployAccountTransaction { (signature, TransactionSignature) ); + implement_v3_tx_getters!( + (resource_bounds, ValidResourceBounds), + (tip, Tip), + (nonce_data_availability_mode, DataAvailabilityMode), + (fee_data_availability_mode, DataAvailabilityMode), + (paymaster_data, PaymasterData) + ); + pub fn version(&self) -> TransactionVersion { match self { DeployAccountTransaction::V1(_) => TransactionVersion::ONE, @@ -519,6 +557,15 @@ macro_rules! implement_invoke_tx_getters { impl InvokeTransaction { implement_invoke_tx_getters!((calldata, Calldata), (signature, TransactionSignature)); + implement_v3_tx_getters!( + (resource_bounds, ValidResourceBounds), + (tip, Tip), + (nonce_data_availability_mode, DataAvailabilityMode), + (fee_data_availability_mode, DataAvailabilityMode), + (paymaster_data, PaymasterData), + (account_deployment_data, AccountDeploymentData) + ); + pub fn nonce(&self) -> Nonce { match self { Self::V0(_) => Nonce::default(), @@ -994,6 +1041,12 @@ where u128::from_str_radix(s.trim_start_matches("0x"), 16).map_err(serde::de::Error::custom) } +#[derive(Debug, PartialEq)] +pub enum GasVectorComputationMode { + All, + NoL2Gas, +} + /// A mapping from execution resources to their corresponding fee bounds.. #[derive(Clone, Debug, Default, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)] // TODO(Nimrod): Remove this struct definition. diff --git a/crates/starknet_api/src/transaction_hash.rs b/crates/starknet_api/src/transaction_hash.rs index ed8b6af2688..9178f0e9a06 100644 --- a/crates/starknet_api/src/transaction_hash.rs +++ b/crates/starknet_api/src/transaction_hash.rs @@ -3,7 +3,7 @@ use std::sync::LazyLock; use starknet_types_core::felt::Felt; use crate::block::BlockNumber; -use crate::core::{calculate_contract_address, ChainId, ContractAddress}; +use crate::core::{ascii_as_felt, calculate_contract_address, ChainId, ContractAddress}; use crate::crypto::utils::HashChain; use crate::data_availability::DataAvailabilityMode; use crate::transaction::{ @@ -175,12 +175,6 @@ pub fn validate_transaction_hash( Ok(possible_hashes.contains(&expected_hash)) } -// TODO: should be part of core::Felt -pub(crate) fn ascii_as_felt(ascii_str: &str) -> Result { - Felt::from_hex(hex::encode(ascii_str).as_str()) - .map_err(|_| StarknetApiError::OutOfRange { string: ascii_str.to_string() }) -} - // An implementation of the SNIP: https://github.com/EvyatarO/SNIPs/blob/snip-8/SNIPS/snip-8.md pub fn get_tip_resource_bounds_hash( resource_bounds: &ValidResourceBounds, diff --git a/crates/starknet_api/src/transaction_hash_test.rs b/crates/starknet_api/src/transaction_hash_test.rs index 998ea6d6b80..1abec8c3b06 100644 --- a/crates/starknet_api/src/transaction_hash_test.rs +++ b/crates/starknet_api/src/transaction_hash_test.rs @@ -3,26 +3,12 @@ use serde::{Deserialize, Serialize}; use sha3::{Digest, Keccak256}; use starknet_types_core::felt::Felt; -use super::{ - ascii_as_felt, - get_transaction_hash, - validate_transaction_hash, - CONSTRUCTOR_ENTRY_POINT_SELECTOR, -}; +use super::{get_transaction_hash, validate_transaction_hash, CONSTRUCTOR_ENTRY_POINT_SELECTOR}; use crate::block::BlockNumber; use crate::core::ChainId; use crate::test_utils::read_json_file; use crate::transaction::{Transaction, TransactionHash, TransactionOptions}; -#[test] -fn test_ascii_as_felt() { - let sn_main_id = ChainId::Mainnet; - let sn_main_felt = ascii_as_felt(sn_main_id.to_string().as_str()).unwrap(); - // This is the result of the Python snippet from the Chain-Id documentation. - let expected_sn_main = Felt::from(23448594291968334_u128); - assert_eq!(sn_main_felt, expected_sn_main); -} - #[test] fn test_constructor_selector() { let mut keccak = Keccak256::default(); diff --git a/crates/starknet_sierra_compile/src/build_utils.rs b/crates/starknet_sierra_compile/src/build_utils.rs index af239682e5e..fdb7958cc76 100644 --- a/crates/starknet_sierra_compile/src/build_utils.rs +++ b/crates/starknet_sierra_compile/src/build_utils.rs @@ -8,7 +8,7 @@ fn out_dir() -> PathBuf { } /// Get the crate's `OUT_DIR` and navigate up to reach the `target/BUILD_FLAVOR` directory. -/// This directory is shared accross all crates in this project. +/// This directory is shared across all crates in this project. fn target_dir() -> PathBuf { let out_dir = out_dir(); diff --git a/crates/tests-integration/Cargo.toml b/crates/tests-integration/Cargo.toml index 9f5a0aa3292..d4343cf8c6e 100644 --- a/crates/tests-integration/Cargo.toml +++ b/crates/tests-integration/Cargo.toml @@ -9,11 +9,11 @@ license.workspace = true workspace = true [dependencies] +assert_matches.workspace = true axum.workspace = true blockifier.workspace = true cairo-lang-starknet-classes.workspace = true indexmap.workspace = true -itertools.workspace = true mempool_test_utils.workspace = true papyrus_common.workspace = true papyrus_rpc.workspace = true @@ -22,6 +22,7 @@ reqwest.workspace = true serde_json.workspace = true starknet-types-core.workspace = true starknet_api.workspace = true +starknet_batcher.workspace = true starknet_client.workspace = true starknet_gateway = { workspace = true, features = ["testing"] } starknet_gateway_types.workspace = true @@ -31,6 +32,7 @@ starknet_mempool_node.workspace = true starknet_mempool_types.workspace = true starknet_task_executor.workspace = true strum.workspace = true +tempfile.workspace = true tokio.workspace = true [dev-dependencies] diff --git a/crates/tests-integration/src/integration_test_setup.rs b/crates/tests-integration/src/integration_test_setup.rs index 535cc1eb39a..41675cc0679 100644 --- a/crates/tests-integration/src/integration_test_setup.rs +++ b/crates/tests-integration/src/integration_test_setup.rs @@ -1,7 +1,6 @@ use std::net::SocketAddr; -use blockifier::test_utils::contracts::FeatureContract; -use blockifier::test_utils::CairoVersion; +use mempool_test_utils::starknet_api_test_utils::MultiAccountTransactionGenerator; use starknet_api::executable_transaction::Transaction; use starknet_api::rpc_transaction::RpcTransaction; use starknet_api::transaction::TransactionHash; @@ -10,8 +9,9 @@ use starknet_http_server::config::HttpServerConfig; use starknet_mempool_infra::errors::ComponentServerError; use starknet_mempool_infra::trace_util::configure_tracing; use starknet_mempool_node::servers::get_server_future; -use starknet_mempool_node::utils::create_clients_servers_from_config; +use starknet_mempool_node::utils::create_node_modules; use starknet_task_executor::tokio_executor::TokioExecutor; +use tempfile::TempDir; use tokio::runtime::Handle; use tokio::task::JoinHandle; @@ -22,44 +22,43 @@ use crate::state_reader::spawn_test_rpc_state_reader; pub struct IntegrationTestSetup { pub task_executor: TokioExecutor, pub http_test_client: HttpTestClient, + + pub batcher_storage_file_handle: TempDir, pub batcher: MockBatcher, + + pub gateway_storage_file_handle: TempDir, pub gateway_handle: JoinHandle>, + pub http_server_handle: JoinHandle>, pub mempool_handle: JoinHandle>, } impl IntegrationTestSetup { - pub async fn new(n_accounts: usize) -> Self { - let default_account_contract = - FeatureContract::AccountWithoutValidations(CairoVersion::Cairo1); - let accounts = std::iter::repeat(default_account_contract).take(n_accounts); - Self::new_for_account_contracts(accounts).await - } - - pub async fn new_for_account_contracts( - accounts: impl IntoIterator, - ) -> Self { + pub async fn new_from_tx_generator(tx_generator: &MultiAccountTransactionGenerator) -> Self { let handle = Handle::current(); let task_executor = TokioExecutor::new(handle); - // Configure and start tracing + // Configure and start tracing. configure_tracing(); // Spawn a papyrus rpc server for a papyrus storage reader. - let rpc_server_addr = spawn_test_rpc_state_reader(accounts).await; + let (rpc_server_addr, gateway_storage_file_handle) = + spawn_test_rpc_state_reader(tx_generator.accounts()).await; // Derive the configuration for the mempool node. - let config = create_config(rpc_server_addr).await; + let (config, batcher_storage_file_handle) = + create_config(rpc_server_addr, &gateway_storage_file_handle).await; - let (clients, servers) = create_clients_servers_from_config(&config); + let (clients, servers) = create_node_modules(&config); let HttpServerConfig { ip, port } = config.http_server_config; let http_test_client = HttpTestClient::new(SocketAddr::from((ip, port))); - let gateway_future = get_server_future("Gateway", true, servers.gateway); + let gateway_future = get_server_future("Gateway", true, servers.local_servers.gateway); let gateway_handle = task_executor.spawn_with_handle(gateway_future); - let http_server_future = get_server_future("HttpServer", true, servers.http_server); + let http_server_future = + get_server_future("HttpServer", true, servers.wrapper_servers.http_server); let http_server_handle = task_executor.spawn_with_handle(http_server_future); // Wait for server to spin up. @@ -71,13 +70,15 @@ impl IntegrationTestSetup { let batcher = MockBatcher::new(clients.get_mempool_client().unwrap()); // Build and run mempool. - let mempool_future = get_server_future("Mempool", true, servers.mempool); + let mempool_future = get_server_future("Mempool", true, servers.local_servers.mempool); let mempool_handle = task_executor.spawn_with_handle(mempool_future); Self { task_executor, http_test_client, + batcher_storage_file_handle, batcher, + gateway_storage_file_handle, gateway_handle, http_server_handle, mempool_handle, diff --git a/crates/tests-integration/src/integration_test_utils.rs b/crates/tests-integration/src/integration_test_utils.rs index bd2e5c014e6..84422430524 100644 --- a/crates/tests-integration/src/integration_test_utils.rs +++ b/crates/tests-integration/src/integration_test_utils.rs @@ -1,14 +1,17 @@ use std::net::SocketAddr; +use std::path::Path; use axum::body::Body; -use blockifier::test_utils::contracts::FeatureContract; -use mempool_test_utils::starknet_api_test_utils::{ - rpc_tx_to_json, - MultiAccountTransactionGenerator, -}; +use mempool_test_utils::starknet_api_test_utils::rpc_tx_to_json; +use papyrus_storage::db::DbConfig; +use papyrus_storage::test_utils::get_mmap_file_test_config; +use papyrus_storage::StorageConfig; +use papyrus_storage::StorageScope::StateOnly; use reqwest::{Client, Response}; +use starknet_api::core::ChainId; use starknet_api::rpc_transaction::RpcTransaction; use starknet_api::transaction::TransactionHash; +use starknet_batcher::config::BatcherConfig; use starknet_gateway::config::{ GatewayConfig, RpcStateReaderConfig, @@ -17,11 +20,11 @@ use starknet_gateway::config::{ }; use starknet_gateway_types::errors::GatewaySpecError; use starknet_http_server::config::HttpServerConfig; -use starknet_mempool_node::config::MempoolNodeConfig; +use starknet_mempool_node::config::SequencerNodeConfig; +use tempfile::{tempdir, TempDir}; +use tokio::fs; use tokio::net::TcpListener; -use crate::integration_test_setup::IntegrationTestSetup; - async fn create_gateway_config() -> GatewayConfig { let stateless_tx_validator_config = StatelessTransactionValidatorConfig { validate_non_zero_l1_gas_fee: true, @@ -40,16 +43,30 @@ async fn create_http_server_config() -> HttpServerConfig { HttpServerConfig { ip: socket.ip(), port: socket.port() } } -pub async fn create_config(rpc_server_addr: SocketAddr) -> MempoolNodeConfig { +pub async fn create_config( + rpc_server_addr: SocketAddr, + initialized_storage_path: &TempDir, +) -> (SequencerNodeConfig, TempDir) { + let batcher_storage_path = tempdir().unwrap(); + fs::copy( + initialized_storage_path.path().join("mdbx.dat"), + &batcher_storage_path.path().join("mdbx.dat"), + ) + .await + .unwrap(); + let batcher_config = create_batcher_config(batcher_storage_path.path()); let gateway_config = create_gateway_config().await; let http_server_config = create_http_server_config().await; let rpc_state_reader_config = test_rpc_state_reader_config(rpc_server_addr); - MempoolNodeConfig { + let sequencer_node_config = SequencerNodeConfig { + batcher_config, gateway_config, http_server_config, rpc_state_reader_config, - ..MempoolNodeConfig::default() - } + ..SequencerNodeConfig::default() + }; + + (sequencer_node_config, batcher_storage_path) } /// A test utility client for interacting with an http server. @@ -99,6 +116,22 @@ fn test_rpc_state_reader_config(rpc_server_addr: SocketAddr) -> RpcStateReaderCo } } +fn create_batcher_config(batcher_storage_path: &Path) -> BatcherConfig { + let storage_config = StorageConfig { + db_config: DbConfig { + path_prefix: batcher_storage_path.to_path_buf(), + chain_id: ChainId::Other("".to_owned()), + enforce_file_exists: true, + min_size: 1 << 20, // 1MB + max_size: 1 << 35, // 32GB + growth_step: 1 << 26, // 64MB + }, + scope: StateOnly, + mmap_file_config: get_mmap_file_test_config(), + }; + BatcherConfig { storage: storage_config } +} + /// Returns a unique IP address and port for testing purposes. /// /// Tests run in parallel, so servers (like RPC or web) running on separate tests must have @@ -113,15 +146,3 @@ pub async fn get_available_socket() -> SocketAddr { .local_addr() .expect("Failed to get local address") } - -/// Use to create a tx generator with _pre-funded_ accounts, alongside a mocked test setup. -pub async fn setup_with_tx_generation( - accounts: &[FeatureContract], -) -> (IntegrationTestSetup, MultiAccountTransactionGenerator) { - let integration_test_setup = - IntegrationTestSetup::new_for_account_contracts(accounts.iter().copied()).await; - let tx_generator = - MultiAccountTransactionGenerator::new_for_account_contracts(accounts.iter().copied()); - - (integration_test_setup, tx_generator) -} diff --git a/crates/tests-integration/src/state_reader.rs b/crates/tests-integration/src/state_reader.rs index fa90c1ff7a7..b05a2e07cfa 100644 --- a/crates/tests-integration/src/state_reader.rs +++ b/crates/tests-integration/src/state_reader.rs @@ -1,6 +1,7 @@ use std::net::SocketAddr; use std::sync::Arc; +use assert_matches::assert_matches; use blockifier::abi::abi_utils::get_fee_token_var_address; use blockifier::context::{BlockContext, ChainInfo}; use blockifier::test_utils::contracts::FeatureContract; @@ -10,12 +11,13 @@ use blockifier::test_utils::{ CURRENT_BLOCK_TIMESTAMP, DEFAULT_ETH_L1_GAS_PRICE, DEFAULT_STRK_L1_GAS_PRICE, + DEFAULT_STRK_L2_GAS_PRICE, TEST_SEQUENCER_ADDRESS, }; use blockifier::transaction::objects::FeeType; use cairo_lang_starknet_classes::casm_contract_class::CasmContractClass; -use indexmap::{indexmap, IndexMap}; -use itertools::Itertools; +use indexmap::IndexMap; +use mempool_test_utils::starknet_api_test_utils::Contract; use papyrus_common::pending_classes::PendingClasses; use papyrus_rpc::{run_server, RpcConfig}; use papyrus_storage::body::BodyStorageWriter; @@ -34,13 +36,20 @@ use starknet_api::block::{ GasPrice, GasPricePerToken, }; -use starknet_api::core::{ClassHash, ContractAddress, PatriciaKey, SequencerContractAddress}; +use starknet_api::core::{ + ClassHash, + ContractAddress, + Nonce, + PatriciaKey, + SequencerContractAddress, +}; use starknet_api::deprecated_contract_class::ContractClass as DeprecatedContractClass; use starknet_api::state::{StorageKey, ThinStateDiff}; use starknet_api::{contract_address, felt, patricia_key}; use starknet_client::reader::PendingData; use starknet_types_core::felt::Felt; use strum::IntoEnumIterator; +use tempfile::TempDir; use tokio::sync::RwLock; use crate::integration_test_utils::get_available_socket; @@ -54,105 +63,102 @@ type ContractClassesMap = /// Returns the address of the rpc server. /// A variable number of identical accounts and test contracts are initialized and funded. pub async fn spawn_test_rpc_state_reader( - accounts: impl IntoIterator, -) -> SocketAddr { + test_defined_accounts: Vec, +) -> (SocketAddr, TempDir) { let block_context = BlockContext::create_for_testing(); - // Map feature contracts to their number of instances inside the account array. - let mut account_to_n_instances: IndexMap = - IndexMap::from_iter(accounts.into_iter().counts()); - - // Add essential contracts to contract mapping, if not exist already. - // TODO: can this hard-coding be removed? - for contract in [ + let into_contract = |contract: FeatureContract| Contract { + contract, + sender_address: contract.get_instance_address(0), + }; + let default_test_contracts = [ FeatureContract::TestContract(CairoVersion::Cairo0), FeatureContract::TestContract(CairoVersion::Cairo1), - FeatureContract::ERC20(CairoVersion::Cairo0), - ] { - *account_to_n_instances.entry(contract).or_default() += 1; - } + ] + .into_iter() + .map(into_contract) + .collect(); + + let erc20_contract = FeatureContract::ERC20(CairoVersion::Cairo0); + let erc20_contract = into_contract(erc20_contract); - let storage_reader = - initialize_papyrus_test_state(block_context.chain_info(), account_to_n_instances); - run_papyrus_rpc_server(storage_reader).await + let (storage_reader, storage_path) = initialize_papyrus_test_state( + block_context.chain_info(), + test_defined_accounts, + default_test_contracts, + erc20_contract, + ); + (run_papyrus_rpc_server(storage_reader).await, storage_path) } fn initialize_papyrus_test_state( chain_info: &ChainInfo, - contract_instances: IndexMap, -) -> StorageReader { - let state_diff = prepare_state_diff(chain_info, &contract_instances); + test_defined_accounts: Vec, + default_test_contracts: Vec, + erc20_contract: Contract, +) -> (StorageReader, TempDir) { + let state_diff = prepare_state_diff( + chain_info, + &test_defined_accounts, + &default_test_contracts, + &erc20_contract, + ); + let contract_classes_to_retrieve = + test_defined_accounts.into_iter().chain(default_test_contracts).chain([erc20_contract]); let (cairo0_contract_classes, cairo1_contract_classes) = - prepare_compiled_contract_classes(contract_instances.into_keys()); + prepare_compiled_contract_classes(contract_classes_to_retrieve); write_state_to_papyrus_storage(state_diff, &cairo0_contract_classes, &cairo1_contract_classes) } fn prepare_state_diff( chain_info: &ChainInfo, - contract_instances: &IndexMap, + test_defined_accounts: &[Contract], + default_test_contracts: &[Contract], + erc20_contract: &Contract, ) -> ThinStateDiff { - let erc20 = FeatureContract::ERC20(CairoVersion::Cairo0); - let erc20_class_hash = erc20.get_class_hash(); + let mut state_diff_builder = ThinStateDiffBuilder::new(chain_info); - // Declare and deploy ERC20 contracts. - let mut deployed_contracts = indexmap! { - chain_info.fee_token_address(&FeeType::Eth) => erc20_class_hash, - chain_info.fee_token_address(&FeeType::Strk) => erc20_class_hash - }; - let mut deprecated_declared_classes = Vec::from([erc20.get_class_hash()]); + // Setup the common test contracts that are used by default in all test invokes. + // TODO(batcher): this does nothing until we actually start excuting stuff in the batcher. + state_diff_builder.set_contracts(default_test_contracts).declare().deploy(); - let mut storage_diffs = IndexMap::new(); - let mut declared_classes = IndexMap::new(); - for (contract, &n_instances) in contract_instances { - for instance in 0..n_instances { - // Declare and deploy the contracts - match contract.cairo_version() { - CairoVersion::Cairo0 => { - deprecated_declared_classes.push(contract.get_class_hash()); - } - CairoVersion::Cairo1 => { - declared_classes.insert(contract.get_class_hash(), Default::default()); - } - } - let instance = u16::try_from(instance).unwrap(); - deployed_contracts - .insert(contract.get_instance_address(instance), contract.get_class_hash()); - fund_feature_account_contract(&mut storage_diffs, contract, instance, chain_info); - } - } + // Declare and deploy and the ERC20 contract, so that transfers from it can be made. + state_diff_builder.set_contracts(std::slice::from_ref(erc20_contract)).declare().deploy(); - ThinStateDiff { - storage_diffs, - deployed_contracts, - declared_classes, - deprecated_declared_classes, - ..Default::default() - } + // TODO(deploy_account_support): once we have batcher with execution, replace with: + // ``` + // state_diff_builder.set_contracts(accounts_defined_in_the_test).declare().fund(); + // ``` + // or use declare txs and transfers for both. + state_diff_builder.inject_accounts_into_state(test_defined_accounts); + + state_diff_builder.build() } fn prepare_compiled_contract_classes( - contract_instances: impl Iterator, + contract_classes_to_retrieve: impl Iterator, ) -> ContractClassesMap { let mut cairo0_contract_classes = Vec::new(); let mut cairo1_contract_classes = Vec::new(); - for contract in contract_instances { + for contract in contract_classes_to_retrieve { match contract.cairo_version() { CairoVersion::Cairo0 => { cairo0_contract_classes.push(( - contract.get_class_hash(), - serde_json::from_str(&contract.get_raw_class()).unwrap(), + contract.class_hash(), + serde_json::from_str(&contract.raw_class()).unwrap(), )); } CairoVersion::Cairo1 => { cairo1_contract_classes.push(( - contract.get_class_hash(), - serde_json::from_str(&contract.get_raw_class()).unwrap(), + contract.class_hash(), + serde_json::from_str(&contract.raw_class()).unwrap(), )); } } } + (cairo0_contract_classes, cairo1_contract_classes) } @@ -160,13 +166,13 @@ fn write_state_to_papyrus_storage( state_diff: ThinStateDiff, cairo0_contract_classes: &[(ClassHash, DeprecatedContractClass)], cairo1_contract_classes: &[(ClassHash, CasmContractClass)], -) -> StorageReader { +) -> (StorageReader, TempDir) { let block_number = BlockNumber(0); let block_header = test_block_header(block_number); let cairo0_contract_classes: Vec<_> = cairo0_contract_classes.iter().map(|(hash, contract)| (*hash, contract)).collect(); - let (storage_reader, mut storage_writer) = get_test_storage().0; + let ((storage_reader, mut storage_writer), storage_path) = get_test_storage(); let mut write_txn = storage_writer.begin_rw_txn().unwrap(); for (class_hash, casm) in cairo1_contract_classes { @@ -184,7 +190,7 @@ fn write_state_to_papyrus_storage( .commit() .unwrap(); - storage_reader + (storage_reader, storage_path) } fn test_block_header(block_number: BlockNumber) -> BlockHeader { @@ -200,6 +206,10 @@ fn test_block_header(block_number: BlockNumber) -> BlockHeader { price_in_wei: GasPrice(DEFAULT_ETH_L1_GAS_PRICE), price_in_fri: GasPrice(DEFAULT_STRK_L1_GAS_PRICE), }, + l2_gas_price: GasPricePerToken { + price_in_wei: GasPrice(DEFAULT_ETH_L1_GAS_PRICE), + price_in_fri: GasPrice(DEFAULT_STRK_L2_GAS_PRICE), + }, timestamp: BlockTimestamp(CURRENT_BLOCK_TIMESTAMP), ..Default::default() }, @@ -207,38 +217,6 @@ fn test_block_header(block_number: BlockNumber) -> BlockHeader { } } -fn fund_feature_account_contract( - storage_diffs: &mut IndexMap>, - contract: &FeatureContract, - instance: u16, - chain_info: &ChainInfo, -) { - match contract { - FeatureContract::AccountWithLongValidate(_) - | FeatureContract::AccountWithoutValidations(_) - | FeatureContract::FaultyAccount(_) => { - fund_account(storage_diffs, &contract.get_instance_address(instance), chain_info); - } - _ => (), - } -} - -fn fund_account( - storage_diffs: &mut IndexMap>, - account_address: &ContractAddress, - chain_info: &ChainInfo, -) { - let key_value = indexmap! { - get_fee_token_var_address(*account_address) => felt!(BALANCE), - }; - for fee_type in FeeType::iter() { - storage_diffs - .entry(chain_info.fee_token_address(&fee_type)) - .or_default() - .extend(key_value.clone()); - } -} - async fn run_papyrus_rpc_server(storage_reader: StorageReader) -> SocketAddr { let rpc_config = RpcConfig { server_address: get_available_socket().await.to_string(), @@ -259,3 +237,109 @@ async fn run_papyrus_rpc_server(storage_reader: StorageReader) -> SocketAddr { tokio::spawn(handle.stopped()); addr } + +/// Constructs a thin state diff from lists of contracts, where each contract can be declared, +/// deployed, and in case it is an account, funded. +#[derive(Default)] +struct ThinStateDiffBuilder<'a> { + contracts: &'a [Contract], + deprecated_declared_classes: Vec, + declared_classes: IndexMap, + deployed_contracts: IndexMap, + storage_diffs: IndexMap>, + // TODO(deploy_account_support): delete field once we have batcher with execution. + nonces: IndexMap, + chain_info: ChainInfo, + initial_account_balance: Felt, +} + +impl<'a> ThinStateDiffBuilder<'a> { + fn new(chain_info: &ChainInfo) -> Self { + const TEST_INITIAL_ACCOUNT_BALANCE: u128 = BALANCE; + let erc20 = FeatureContract::ERC20(CairoVersion::Cairo0); + let erc20_class_hash = erc20.get_class_hash(); + + let deployed_contracts: IndexMap = FeeType::iter() + .map(|fee_type| (chain_info.fee_token_address(&fee_type), erc20_class_hash)) + .collect(); + + Self { + chain_info: chain_info.clone(), + initial_account_balance: felt!(TEST_INITIAL_ACCOUNT_BALANCE), + deployed_contracts, + ..Default::default() + } + } + + fn set_contracts(&mut self, contracts: &'a [Contract]) -> &mut Self { + self.contracts = contracts; + self + } + + fn declare(&mut self) -> &mut Self { + for contract in self.contracts { + match contract.cairo_version() { + CairoVersion::Cairo0 => { + self.deprecated_declared_classes.push(contract.class_hash()) + } + CairoVersion::Cairo1 => { + self.declared_classes.insert(contract.class_hash(), Default::default()); + } + } + } + self + } + + fn deploy(&mut self) -> &mut Self { + for contract in self.contracts { + self.deployed_contracts.insert(contract.sender_address, contract.class_hash()); + } + self + } + + /// Only applies for contracts that are accounts, for non-accounts only declare and deploy work. + fn fund(&mut self) -> &mut Self { + for account in self.contracts { + assert_matches!( + account.contract, + FeatureContract::AccountWithLongValidate(_) + | FeatureContract::AccountWithoutValidations(_) + | FeatureContract::FaultyAccount(_), + "Only Accounts can be funded, {account:?} is not an account", + ); + + let fee_token_address = get_fee_token_var_address(account.sender_address); + for fee_type in FeeType::iter() { + self.storage_diffs + .entry(self.chain_info.fee_token_address(&fee_type)) + .or_default() + .insert(fee_token_address, self.initial_account_balance); + } + } + + self + } + + // TODO(deploy_account_support): delete method once we have batcher with execution. + fn inject_accounts_into_state(&mut self, accounts_defined_in_the_test: &'a [Contract]) { + self.set_contracts(accounts_defined_in_the_test).declare().deploy().fund(); + + // Set nonces as 1 in the state so that subsequent invokes can pass validation. + self.nonces = self + .deployed_contracts + .iter() + .map(|(&address, _)| (address, Nonce(Felt::ONE))) + .collect(); + } + + fn build(self) -> ThinStateDiff { + ThinStateDiff { + storage_diffs: self.storage_diffs, + deployed_contracts: self.deployed_contracts, + declared_classes: self.declared_classes, + deprecated_declared_classes: self.deprecated_declared_classes, + nonces: self.nonces, + ..Default::default() + } + } +} diff --git a/crates/tests-integration/tests/end_to_end_test.rs b/crates/tests-integration/tests/end_to_end_test.rs index 08d7f79f03a..6c880838a4a 100644 --- a/crates/tests-integration/tests/end_to_end_test.rs +++ b/crates/tests-integration/tests/end_to_end_test.rs @@ -1,44 +1,51 @@ use blockifier::test_utils::contracts::FeatureContract; use blockifier::test_utils::CairoVersion; +use mempool_test_utils::starknet_api_test_utils::MultiAccountTransactionGenerator; +use pretty_assertions::assert_eq; +use rstest::{fixture, rstest}; use starknet_api::transaction::TransactionHash; -use starknet_mempool_integration_tests::integration_test_utils::setup_with_tx_generation; +use starknet_mempool_integration_tests::integration_test_setup::IntegrationTestSetup; -#[ignore = "Gilad: There are structural issues with funding new accounts and this need surgery. - Will fix soon. Once fixed, the test logic also need work, it's stale by now."] +#[fixture] +fn tx_generator() -> MultiAccountTransactionGenerator { + MultiAccountTransactionGenerator::new() +} + +#[rstest] #[tokio::test] -async fn test_end_to_end() { +async fn test_end_to_end(mut tx_generator: MultiAccountTransactionGenerator) { // Setup. - let accounts = [ + for account in [ FeatureContract::AccountWithoutValidations(CairoVersion::Cairo1), FeatureContract::AccountWithoutValidations(CairoVersion::Cairo0), - ]; - let (mock_running_system, mut tx_generator) = setup_with_tx_generation(&accounts).await; + ] { + tx_generator.register_account_for_flow_test(account); + } - let account0_invoke_nonce1 = tx_generator.account_with_id(0).generate_default_invoke(); - let account1_invoke_nonce0 = tx_generator.account_with_id(1).generate_default_invoke(); - let account0_invoke_nonce2 = tx_generator.account_with_id(0).generate_default_invoke(); + let mock_running_system = IntegrationTestSetup::new_from_tx_generator(&tx_generator).await; - // Test. + let account0_invoke_nonce1 = tx_generator.account_with_id(0).generate_invoke_with_tip(1); + let account0_invoke_nonce2 = tx_generator.account_with_id(0).generate_invoke_with_tip(1); + let account1_invoke_nonce1 = tx_generator.account_with_id(1).generate_invoke_with_tip(1); - mock_running_system.assert_add_tx_success(&account0_invoke_nonce1).await; + let account0_invoke_nonce1_tx_hash = + mock_running_system.assert_add_tx_success(&account0_invoke_nonce1).await; - // FIXME: invoke with nonce0 shouldn't be possible, fix it, make this FAIL. - let account1_invoke_nonce0_tx_hash = - mock_running_system.assert_add_tx_success(&account1_invoke_nonce0).await; + let account1_invoke_nonce1_tx_hash = + mock_running_system.assert_add_tx_success(&account1_invoke_nonce1).await; - mock_running_system.assert_add_tx_success(&account0_invoke_nonce2).await; + let account0_invoke_nonce2_tx_hash = + mock_running_system.assert_add_tx_success(&account0_invoke_nonce2).await; + // Test. let mempool_txs = mock_running_system.get_txs(4).await; // Assert. - - // Only the transactions with nonce 0 should be returned from the mempool, - // because we haven't merged queue-replenishment yet. - let expected_tx_hashes_from_get_txs = [account1_invoke_nonce0_tx_hash]; - - // This assert should be replaced with 4 once queue-replenishment is merged, also add a tx hole - // at that point, and ensure the assert doesn't change due to that. - assert_eq!(mempool_txs.len(), 2); + let expected_tx_hashes_from_get_txs = [ + account1_invoke_nonce1_tx_hash, + account0_invoke_nonce1_tx_hash, + account0_invoke_nonce2_tx_hash, + ]; let actual_tx_hashes: Vec = mempool_txs.iter().map(|tx| tx.tx_hash()).collect(); assert_eq!(expected_tx_hashes_from_get_txs, *actual_tx_hashes); diff --git a/scripts/dependencies.sh b/scripts/dependencies.sh new file mode 100755 index 00000000000..2577f7c8700 --- /dev/null +++ b/scripts/dependencies.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +set -e + +[[ ${UID} == "0" ]] || SUDO="sudo" + +function install_essential_deps_linux() { + $SUDO bash -c ' + apt update && apt install -y \ + ca-certificates \ + curl \ + git \ + gnupg \ + jq \ + libssl-dev \ + lsb-release \ + pkg-config \ + ripgrep \ + software-properties-common \ + zstd \ + wget + ' +} + +function setup_llvm_deps() { + case "$(uname)" in + Darwin) + brew update + brew install llvm@18 + ;; + Linux) + $SUDO bash -c 'curl https://apt.llvm.org/llvm.sh -Lo llvm.sh + bash ./llvm.sh 18 all + apt update && apt install -y \ + libgmp3-dev \ + libmlir-18-dev \ + libpolly-18-dev \ + libzstd-dev \ + mlir-18-tools + ' + ;; + *) + echo "Error: Unsupported operating system" + exit 1 + ;; + esac +} + +function main() { + [ "$(uname)" = "Linux" ] && install_essential_deps_linux + setup_llvm_deps + echo "LLVM and Cairo native runtime dependencies installed successfully." +} + +main "$@" diff --git a/scripts/install_build_tools.sh b/scripts/install_build_tools.sh old mode 100644 new mode 100755 index ad5f20c87e4..e9b649b45c3 --- a/scripts/install_build_tools.sh +++ b/scripts/install_build_tools.sh @@ -2,39 +2,54 @@ set -e +[[ ${UID} == "0" ]] || SUDO="sudo" + +function install_common_packages() { + $SUDO bash -c ' + apt update && DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC apt -y install \ + build-essential \ + clang \ + curl \ + gnupg \ + libzstd-dev \ + python3-dev \ + sudo \ + tzdata \ + wget + ' +} + function install_pypy() { - pushd /opt - $SUDO bash -c ' - curl -Lo pypy3.9-v7.3.11-linux64.tar.bz2 https://downloads.python.org/pypy/pypy3.9-v7.3.11-linux64.tar.bz2 - tar -xf pypy3.9-v7.3.11-linux64.tar.bz2 - rm pypy3.9-v7.3.11-linux64.tar.bz2 - chmod +x pypy3.9-v7.3.11-linux64/bin/pypy3 - - if [ -L /usr/local/bin/pypy3.9 ]; then - unlink /usr/local/bin/pypy3.9 - fi - - ln -s /opt/pypy3.9-v7.3.11-linux64/bin/pypy3 /usr/local/bin/pypy3.9 - - if [ -L /opt/pypy3.9 ]; then - unlink /opt/pypy3.9 - fi - - ln -s /opt/pypy3.9-v7.3.11-linux64 /opt/pypy3.9 - pypy3.9 -m ensurepip - pypy3.9 -m pip install wheel - ' - popd + pushd /opt + $SUDO bash -c ' + curl -Lo pypy3.9-v7.3.11-linux64.tar.bz2 https://downloads.python.org/pypy/pypy3.9-v7.3.11-linux64.tar.bz2 + tar -xf pypy3.9-v7.3.11-linux64.tar.bz2 + rm pypy3.9-v7.3.11-linux64.tar.bz2 + chmod +x pypy3.9-v7.3.11-linux64/bin/pypy3 + + if [ -L /usr/local/bin/pypy3.9 ]; then + unlink /usr/local/bin/pypy3.9 + fi + + ln -s /opt/pypy3.9-v7.3.11-linux64/bin/pypy3 /usr/local/bin/pypy3.9 + + if [ -L /opt/pypy3.9 ]; then + unlink /opt/pypy3.9 + fi + + ln -s /opt/pypy3.9-v7.3.11-linux64 /opt/pypy3.9 + pypy3.9 -m ensurepip + pypy3.9 -m pip install wheel + ' + popd } -function install_rust () { +function install_rust() { curl https://sh.rustup.rs -sSf | sh -s -- -y } -if [ "$(id -u)" -ne 0 ]; then - SUDO=sudo -fi - +install_common_packages install_pypy & install_rust & wait +./dependencies.sh diff --git a/scripts/sequencer-ci.Dockerfile b/scripts/sequencer-ci.Dockerfile index fd807e0e1a9..77a9902dff6 100644 --- a/scripts/sequencer-ci.Dockerfile +++ b/scripts/sequencer-ci.Dockerfile @@ -1,17 +1,10 @@ FROM ubuntu:20.04 -ARG DEBIAN_FRONTEND=noninteractive ARG USERNAME=sequencer ARG USER_UID=1000 ARG USER_GID=$USER_UID -RUN apt update && apt -y install \ - build-essential \ - clang \ - curl \ - python3-dev \ - sudo - +RUN apt update && apt install -y sudo RUN groupadd --gid $USER_GID $USERNAME && \ useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME @@ -24,4 +17,6 @@ ENV CARGO_HOME=${RUSTUP_HOME} ENV PATH=$PATH:${RUSTUP_HOME}/bin COPY install_build_tools.sh . -RUN bash install_build_tools.sh +COPY dependencies.sh . + +RUN ./install_build_tools.sh