diff --git a/.github/workflows/hyper_threading_benchmarks.yml b/.github/workflows/hyper_threading_benchmarks.yml new file mode 100644 index 0000000000..cebfa0084c --- /dev/null +++ b/.github/workflows/hyper_threading_benchmarks.yml @@ -0,0 +1,110 @@ +name: Benchmark Hyper Threading + +on: + pull_request: + branches: + - main + +jobs: + benchmark: + runs-on: ubuntu-latest + steps: + - name: Checkout PR + uses: actions/checkout@v2 + with: + ref: ${{ github.head_ref }} + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Install Dependencies + run: | + pip install -r requirements.txt + sudo apt update + sudo apt-get install -y hyperfine + + - name: Install Rust + uses: dtolnay/rust-toolchain@1.74.1 + with: + components: rustfmt, clippy + + - name: Compile PR Version + run: | + cargo build --release -p hyper_threading + cp target/release/hyper_threading ${{ github.workspace }}/hyper_threading_pr + cp ./examples/hyper_threading/hyper-threading-workflow.sh ${{ github.workspace }}/hyper-threading-workflow.sh + + - name: Upload PR Binary + uses: actions/upload-artifact@v4 + with: + name: hyper_threading_pr_binary + path: ${{ github.workspace }}/hyper_threading_pr + + - name: Upload Workflow Script + uses: actions/upload-artifact@v4 + with: + name: hyper_threading_workflow_script + path: ${{ github.workspace }}/hyper-threading-workflow.sh + + + - name: Checkout Main Branch + uses: actions/checkout@v2 + with: + ref: 'main' + + - name: Compile Main Version + run: | + cargo build --release -p hyper_threading + cp target/release/hyper_threading ${{ github.workspace }}/hyper_threading_main + + - name: Download hyper_threading_pr_binary + uses: actions/download-artifact@v4 + with: + name: hyper_threading_pr_binary + path: ${{ github.workspace }}/ + + - name: Download hyper_threading_workflow_script + uses: actions/download-artifact@v4 + with: + name: hyper_threading_workflow_script + path: ${{ github.workspace }}/ + + - name: Compile programs + run: make cairo_bench_programs + + - name: Run Benchmarks + run: | + cd ${{ github.workspace }} + chmod +x ./hyper_threading_main + chmod +x ./hyper_threading_pr + chmod +x hyper-threading-workflow.sh + ./hyper-threading-workflow.sh + + - name: Compare Results + run: | + cat result.md + + - name: Find comment + uses: peter-evans/find-comment@v2 + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: "**Hyper Thereading Benchmark results**" + + - name: Create comment + if: steps.fc.outputs.comment-id == '' + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ github.event.pull_request.number }} + body-path: result.md + + - name: Update comment + if: steps.fc.outputs.comment-id != '' + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + body-path: result.md + edit-mode: replace diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3e13f56f9b..83452c25ff 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -194,6 +194,10 @@ jobs: smoke: needs: merge-caches name: Make sure all builds work + strategy: + fail-fast: false + matrix: + crate: ["vm", "cairo-vm-cli", "cairo1-run"] runs-on: ubuntu-22.04 steps: - name: Install Rust @@ -210,6 +214,7 @@ jobs: uses: taiki-e/install-action@v2 with: tool: cargo-all-features + - name: Checkout uses: actions/checkout@v3 @@ -226,19 +231,86 @@ jobs: fail-on-cache-miss: true # NOTE: we do this separately because --workspace operates in weird ways - - name: Check all features (vm) + - name: Check all features (${{ matrix.crate }}) run: | - cd vm + cd ${{ matrix.crate }} cargo check-all-features + cargo check-all-features --workspace --all-targets + + smoke-workspace: + needs: merge-caches + name: Make sure all builds work (workspace) + strategy: + fail-fast: false + matrix: + chunk: [1, 2, 3, 4, 5, 6] + runs-on: ubuntu-22.04 + steps: + - name: Install Rust + uses: dtolnay/rust-toolchain@1.74.1 + with: + targets: wasm32-unknown-unknown + + - name: Set up cargo cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + + - name: Install cargo-all-features + uses: taiki-e/install-action@v2 + with: + tool: cargo-all-features + + - name: Checkout + uses: actions/checkout@v3 + + - name: Download proof programs symlinks + uses: actions/download-artifact@master + with: + name: proof_programs + path: cairo_programs/proof_programs/ + + - name: Fetch programs + uses: actions/cache/restore@v3 + with: + path: ${{ env.CAIRO_PROGRAMS_PATH }} + key: all-programs-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} + fail-on-cache-miss: true - - name: Check all features (CLI) - run: | - cd cairo-vm-cli - cargo check-all-features - - name: Check all features (workspace) run: | - cargo check-all-features --workspace --all-targets + cargo check-all-features --n-chunks 6 --chunk ${{ matrix.chunk }} --workspace --all-targets + + smoke-no-std: + needs: merge-caches + name: Make sure all builds work (no_std) + runs-on: ubuntu-22.04 + steps: + - name: Install Rust + uses: dtolnay/rust-toolchain@1.74.1 + with: + targets: wasm32-unknown-unknown + + - name: Set up cargo cache + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + + - name: Checkout + uses: actions/checkout@v3 + + - name: Download proof programs symlinks + uses: actions/download-artifact@master + with: + name: proof_programs + path: cairo_programs/proof_programs/ + + - name: Fetch programs + uses: actions/cache/restore@v3 + with: + path: ${{ env.CAIRO_PROGRAMS_PATH }} + key: all-programs-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} + fail-on-cache-miss: true - name: Check no-std run: | @@ -251,7 +323,7 @@ jobs: strategy: fail-fast: false matrix: - special_features: ["", "extensive_hints"] + special_features: ["", "extensive_hints", "mod_builtin"] target: [ test#1, test#2, test#3, test#4, test-no_std#1, test-no_std#2, test-no_std#3, test-no_std#4, test-wasm ] name: Run tests runs-on: ubuntu-22.04 @@ -613,3 +685,42 @@ jobs: cairo-compile cairo_programs/array_sum.cairo --no_debug_info --output cairo_programs/array_sum.json cd examples/wasm-demo wasm-pack build --target=web + + compare-factorial-outputs-all-layouts: + name: Compare factorial outputs for all layouts + needs: [ build-programs, build-release ] + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Python3 Build + uses: actions/setup-python@v4 + with: + python-version: '3.9' + cache: 'pip' + + - name: Install cairo-lang and deps + run: pip install -r requirements.txt + + - name: Fetch release binary + uses: actions/cache/restore@v3 + with: + key: cli-bin-rel-${{ github.sha }} + path: target/release/cairo-vm-cli + fail-on-cache-miss: true + + - uses: actions/download-artifact@master + with: + name: proof_programs + path: cairo_programs/proof_programs/ + + - name: Fetch programs + uses: actions/cache/restore@v3 + with: + path: ${{ env.CAIRO_PROGRAMS_PATH }} + key: cairo_proof_programs-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} + fail-on-cache-miss: true + + - name: Run script + run: ./vm/src/tests/compare_factorial_outputs_all_layouts.sh diff --git a/.gitignore b/.gitignore index c2c0ebea83..2e1f8ff93e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ **/*.memory **/*.air_public_input **/*.air_private_input +**/*.pie.zip **/*.swp bench/results .python-version diff --git a/CHANGELOG.md b/CHANGELOG.md index b020db2a92..a9cc5a97e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,95 @@ #### Upcoming Changes +* Bump `starknet-types-core` version + Use the lib's pedersen hash [#1692](https://github.com/lambdaclass/cairo-vm/pull/1692) + +* refactor: Remove unused code & use constants whenever possible for builtin instance definitions[#1707](https://github.com/lambdaclass/cairo-vm/pull/1707) + +* feat: missing EC hints for Starknet OS 0.13.1 [#1706](https://github.com/lambdaclass/cairo-vm/pull/1706) + +* fix(BREAKING): Use program builtins in `initialize_main_entrypoint` & `read_return_values`[#1703](https://github.com/lambdaclass/cairo-vm/pull/1703) + * `initialize_main_entrypoint` now iterates over the program builtins when building the stack & inserts 0 for any missing builtin + * `read_return_values` now only computes the final stack of the builtins in the program + * BREAKING: `read_return_values` now takes a boolean argument `allow_missing_builtins` + * Added method `BuiltinRunner::identifier` to get the `BuiltinName` of each builtin + * BREAKING: `OutputBuiltinRunner::get_public_memory` now takes a reference to `MemorySegmentManager` + * BREAKING: method `VirtualMachine::get_memory_segment_addresses` moved to `CairoRunner::get_memory_segment_addresses` + +* feat(BREAKING): Add range_check96 builtin[#1698](https://github.com/lambdaclass/cairo-vm/pull/1698) + * Add the new `range_check96` builtin to the `all_cairo` layout. + * `RangeCheckBuiltinRunner` changes: + * Remove field `n_parts`, replacing it with const generic `N_PARTS`. + * Remome `n_parts` argument form method `new`. + * Remove field `_bound`, replacing it with public method `bound`. + * Add public methods `name` & `n_parts`. + +* feat(BREAKING): Add mod builtin [#1673](https://github.com/lambdaclass/cairo-vm/pull/1673) + + Main Changes: + * Add the new `ModBuiltinRunner`, implementing the builtins `add_mod` & `mul_mod` + * Adds `add_mod` & `mul_mod` to the `all_cairo` & `dynamic` layouts under the `mod_builtin` feature flag. This will be added to the main code in a future update. + * Add method `VirtualMachine::fill_memory` in order to perform the new builtin's main logic from within hints + * Add hints to run arithmetic circuits using `add_mod` and/or `mul_mod` builtins + + Other Changes: + * BREAKING: BuiltinRunner method signature change from + `air_private_input(&self, memory: &Memory) -> Vec` to `pub fn air_private_input(&self, segments: &MemorySegmentManager) -> Vec` + * Add `MayleRelocatable::sub_usize` + * Implement `Add for Relocatable` + * Add `Memory::get_usize` + * BREAKING: Clean up unused/duplicated code from builtins module: + * Remove unused method `get_memory_segment_addresses` from all builtin runners & the enum + * Remove empty implementations of `deduce_memory_cell` & `add_validation_rules` from all builtin runners + * Remove duplicated implementation of `final_stack` from all builtin runners except output and move it to the enum implementation + +* bugfix(BREAKING): Handle off2 immediate case in `get_integer_from_reference`[#1701](https://github.com/lambdaclass/cairo-vm/pull/1701) + * `get_integer_from_reference` & `get_integer_from_var_name` output changed from `Result, HintError>` to `Result` + +* feat: Reorganized builtins to be in the top of stack at the end of a run (Cairo1). + +* BREAKING: Remove `CairoRunner::add_additional_hash_builtin` & `VirtualMachine::disable_trace`[#1658](https://github.com/lambdaclass/cairo-vm/pull/1658) + +* feat: output builtin add_attribute method [#1691](https://github.com/lambdaclass/cairo-vm/pull/1691) + +* feat: add a method to retrieve the output builtin from the VM [#1690](https://github.com/lambdaclass/cairo-vm/pull/1690) + +* feat: Add zero segment [#1668](https://github.com/lambdaclass/cairo-vm/pull/1668) + +* feat: Bump cairo_lang to 0.13.1 in testing env [#1687](https://github.com/lambdaclass/cairo-vm/pull/1687) + +* feat(BREAKING): Use return type info from sierra when serializing return values in cairo1-run crate [#1665](https://github.com/lambdaclass/cairo-vm/pull/1665) + * Removed public function `serialize_output`. + * Add field `serialize_output` to `Cairo1RunConfig`. + * Function `cairo_run_program` now returns an extra `Option` value with the serialized output if `serialize_output` is enabled in the config. + * Output serialization improved as it now uses the sierra program data to identify return value's types. + +* feat: Create hyper_threading crate to benchmark the `cairo-vm` in a hyper-threaded environment [#1679](https://github.com/lambdaclass/cairo-vm/pull/1679) + +* feat: add a `--tracer` option which hosts a web server that shows the line by line execution of cairo code along with memory registers [#1265](https://github.com/lambdaclass/cairo-vm/pull/1265) + +* feat: Fix error handling in `initialize_state`[#1657](https://github.com/lambdaclass/cairo-vm/pull/1657) + +* feat: Make air public inputs deserializable [#1657](https://github.com/lambdaclass/cairo-vm/pull/1648) + +* feat: Show only layout builtins in air private input [#1651](https://github.com/lambdaclass/cairo-vm/pull/1651) + +* feat: Sort builtin segment info upon serialization for Cairo PIE [#1654](https://github.com/lambdaclass/cairo-vm/pull/1654) + +* feat: Fix output serialization for cairo 1 [#1645](https://github.com/lambdaclass/cairo-vm/pull/1645) + * Reverts changes added by #1630 + * Extends the serialization of Arrays added by the `print_output` flag to Spans and Dictionaries + * Now dereferences references upon serialization + +* feat: Add flag to append return values to output segment when not running in proof_mode [#1646](https://github.com/lambdaclass/cairo-vm/pull/1646) + * Adds the flag `append_return_values` to both the CLI and `Cairo1RunConfig` struct. + * Enabling flag will add the output builtin and the necessary instructions to append the return values to the output builtin's memory segment. + +* feat: Compute program hash chain [#1647](https://github.com/lambdaclass/cairo-vm/pull/1647) + * feat: Add cairo1-run output pretty-printing for felts, arrays/spans and dicts [#1630](https://github.com/lambdaclass/cairo-vm/pull/1630) +* feat: output builtin features for bootloader support [#1580](https://github.com/lambdaclass/cairo-vm/pull/1580) + #### [1.0.0-rc1] - 2024-02-23 * Bump `starknet-types-core` dependency version to 0.0.9 [#1628](https://github.com/lambdaclass/cairo-vm/pull/1628) diff --git a/CODEOWNERS b/CODEOWNERS index 6d1f46e986..4e1812b071 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @igaray @Oppen @fmoletta @entropidelic @juanbono @pefontana +* @igaray @Oppen @fmoletta @juanbono @pefontana diff --git a/Cargo.lock b/Cargo.lock index 6fc683d8c4..499b5fe6c0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -51,6 +60,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "allocator-api2" version = "0.2.16" @@ -211,12 +235,109 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" +[[package]] +name = "async-compression" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a116f46a969224200a0a97f29cfd4c50e7534e4b4826bd23ea2c3c533039c82c" +dependencies = [ + "brotli", + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", + "zstd 0.13.0", + "zstd-safe 7.0.0", +] + +[[package]] +name = "async-trait" +version = "0.1.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" +dependencies = [ + "async-trait", + "axum-core", + "bitflags 1.3.2", + "bytes", + "futures-util", + "http", + "http-body", + "hyper", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "mime", + "rustversion", + "tower-layer", + "tower-service", +] + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + [[package]] name = "base64ct" version = "1.6.0" @@ -280,6 +401,27 @@ dependencies = [ "generic-array", ] +[[package]] +name = "brotli" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "bumpalo" version = "3.15.3" @@ -298,6 +440,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + [[package]] name = "bzip2" version = "0.4.4" @@ -780,6 +928,7 @@ dependencies = [ "assert_matches", "bincode", "cairo-vm", + "cairo-vm-tracer", "clap", "mimalloc", "nom", @@ -787,6 +936,25 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cairo-vm-tracer" +version = "1.0.0-rc1" +dependencies = [ + "axum", + "cairo-vm", + "include_dir", + "mime_guess", + "num-bigint", + "num-traits 0.2.18", + "serde", + "thiserror-no-std", + "tokio", + "tower", + "tower-http", + "tracing", + "tracing-subscriber", +] + [[package]] name = "cairo1-run" version = "1.0.0-rc1" @@ -805,6 +973,7 @@ dependencies = [ "clap", "itertools 0.11.0", "mimalloc", + "num-traits 0.2.18", "rstest", "thiserror", ] @@ -1214,6 +1383,15 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + [[package]] name = "funty" version = "2.0.0" @@ -1360,6 +1538,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + [[package]] name = "good_lp" version = "1.7.0" @@ -1445,6 +1629,78 @@ dependencies = [ "digest", ] +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "http-range-header" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "0.14.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper_threading" +version = "1.0.0-rc1" +dependencies = [ + "cairo-vm", + "rayon", + "tracing", +] + [[package]] name = "iai-callgrind" version = "0.3.1" @@ -1468,6 +1724,25 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "include_dir" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indent" version = "0.1.1" @@ -1520,6 +1795,16 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "iri-string" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21859b667d66a4c1dacd9df0863b3efb65785474255face87f5bca39dd8407c0" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "is-terminal" version = "0.4.12" @@ -1607,9 +1892,9 @@ dependencies = [ [[package]] name = "lambdaworks-crypto" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d4c222d5b2fdc0faf702d3ab361d14589b097f40eac9dc550e27083483edc65" +checksum = "458fee521f12d0aa97a2e06eaf134398a5d2ae7b2074af77eb402b0d93138c47" dependencies = [ "lambdaworks-math", "serde", @@ -1619,9 +1904,9 @@ dependencies = [ [[package]] name = "lambdaworks-math" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ee7dcab3968c71896b8ee4dc829147acc918cffe897af6265b1894527fe3add" +checksum = "6c74ce6f0d9cb672330b6ca59e85a6c3607a3329e0372ab0d3fe38c2d38e50f9" dependencies = [ "serde", "serde_json", @@ -1700,6 +1985,12 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "matrixmultiply" version = "0.2.4" @@ -1724,6 +2015,22 @@ dependencies = [ "libmimalloc-sys", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minilp" version = "0.2.2" @@ -1749,6 +2056,17 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "ndarray" version = "0.13.1" @@ -1778,6 +2096,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-bigint" version = "0.4.4" @@ -1862,6 +2190,25 @@ dependencies = [ "libm", ] +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -1874,6 +2221,12 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parity-scale-codec" version = "3.6.9" @@ -1983,6 +2336,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + [[package]] name = "petgraph" version = "0.6.4" @@ -2008,6 +2367,26 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" +[[package]] +name = "pin-project" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0302c4a0442c456bd56f841aee5c3bfd17967563f6fadc9ceb9f9c23cf3807e0" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266c042b60c9c76b8d53061e52b2e0d1116abc57cefc8c5cd671619a56ac3690" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -2178,9 +2557,9 @@ checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" [[package]] name = "rayon" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" dependencies = [ "either", "rayon-core", @@ -2302,6 +2681,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2477,6 +2862,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_spanned" version = "0.6.5" @@ -2486,6 +2881,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2518,6 +2925,15 @@ dependencies = [ "keccak", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "siphasher" version = "0.3.11" @@ -2548,6 +2964,16 @@ dependencies = [ "serde", ] +[[package]] +name = "socket2" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "spin" version = "0.5.2" @@ -2619,15 +3045,14 @@ dependencies = [ [[package]] name = "starknet-types-core" -version = "0.0.9" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d53160556d1f23425100f42b3230df747ea05763efee685a2cd939dfb640701" +checksum = "1051b4f4af0bb9b546388a404873ee1e6b9787b9d5b0b3319ecbfadf315ef276" dependencies = [ "arbitrary", "bitvec", "lambdaworks-crypto", "lambdaworks-math", - "lazy_static", "num-bigint", "num-integer", "num-traits 0.2.18", @@ -2681,6 +3106,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "tap" version = "1.0.1" @@ -2750,6 +3181,16 @@ dependencies = [ "thiserror-impl-no-std", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.34" @@ -2788,6 +3229,47 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tokio" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + [[package]] name = "toml" version = "0.8.10" @@ -2833,6 +3315,128 @@ dependencies = [ "winnow 0.6.2", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +dependencies = [ + "async-compression", + "base64", + "bitflags 2.4.2", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "httpdate", + "iri-string", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", + "tokio", + "tokio-util", + "tower", + "tower-layer", + "tower-service", + "tracing", + "uuid", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + +[[package]] +name = "tower-service" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + [[package]] name = "typenum" version = "1.17.0" @@ -2854,6 +3458,15 @@ dependencies = [ "thiserror", ] +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -2878,6 +3491,21 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "uuid" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f00cc9702ca12d3c81455259621e676d0f7251cec66a21e98fe2e9a37db93b2a" +dependencies = [ + "getrandom", +] + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "version_check" version = "0.9.4" @@ -2903,6 +3531,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -3283,7 +3920,7 @@ dependencies = [ "pbkdf2", "sha1", "time", - "zstd", + "zstd 0.11.2+zstd.1.5.2", ] [[package]] @@ -3292,7 +3929,16 @@ version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ - "zstd-safe", + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" +dependencies = [ + "zstd-safe 7.0.0", ] [[package]] @@ -3305,6 +3951,15 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" +dependencies = [ + "zstd-sys", +] + [[package]] name = "zstd-sys" version = "2.0.9+zstd.1.5.5" diff --git a/Cargo.toml b/Cargo.toml index 45b7340838..f3825f5efa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,9 @@ members = [ "vm", "hint_accountant", "examples/wasm-demo", - "cairo1-run" + "cairo1-run", + "cairo-vm-tracer", + "examples/hyper_threading" ] default-members = [ "cairo-vm-cli", @@ -27,6 +29,7 @@ keywords = ["starknet", "cairo", "vm", "wasm", "no_std"] [workspace.dependencies] cairo-vm = { path = "./vm", version = "1.0.0-rc1", default-features = false } +cairo-vm-tracer = { path = "./cairo-vm-tracer", version = "1.0.0-rc1", default-features = false } mimalloc = { version = "0.1.37", default-features = false } num-bigint = { version = "0.4", default-features = false, features = [ "serde", diff --git a/Makefile b/Makefile index 5467a61bbf..86b6d7d7df 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ endif compare_benchmarks_deps compare_benchmarks docs clean \ compare_trace_memory compare_trace compare_memory compare_pie compare_all_no_proof \ compare_trace_memory_proof compare_all_proof compare_trace_proof compare_memory_proof compare_air_public_input compare_air_private_input\ + hyper-threading-benchmarks \ cairo_bench_programs cairo_proof_programs cairo_test_programs cairo_1_test_contracts cairo_2_test_contracts \ cairo_trace cairo-vm_trace cairo_proof_trace cairo-vm_proof_trace \ fuzzer-deps fuzzer-run-cairo-compiled fuzzer-run-hint-diff build-cairo-lang hint-accountant \ create-proof-programs-symlinks \ @@ -48,6 +49,10 @@ PROOF_BENCH_DIR=cairo_programs/benchmarks PROOF_BENCH_FILES:=$(wildcard $(PROOF_BENCH_DIR)/*.cairo) PROOF_COMPILED_BENCHES:=$(patsubst $(PROOF_BENCH_DIR)/%.cairo, $(PROOF_BENCH_DIR)/%.json, $(PROOF_BENCH_FILES)) +MOD_BUILTIN_TEST_PROOF_DIR=cairo_programs/mod_builtin_feature/proof +MOD_BUILTIN_TEST_PROOF_FILES:=$(wildcard $(MOD_BUILTIN_TEST_PROOF_DIR)/*.cairo) +COMPILED_MOD_BUILTIN_PROOF_TESTS:=$(patsubst $(MOD_BUILTIN_TEST_PROOF_DIR)/%.cairo, $(MOD_BUILTIN_TEST_PROOF_DIR)/%.json, $(MOD_BUILTIN_TEST_PROOF_FILES)) + $(TEST_PROOF_DIR)/%.json: $(TEST_PROOF_DIR)/%.cairo cairo-compile --cairo_path="$(TEST_PROOF_DIR):$(PROOF_BENCH_DIR)" $< --output $@ --proof_mode @@ -60,6 +65,9 @@ $(TEST_PROOF_DIR)/%.trace $(TEST_PROOF_DIR)/%.memory $(TEST_PROOF_DIR)/%.air_pub $(PROOF_BENCH_DIR)/%.json: $(PROOF_BENCH_DIR)/%.cairo cairo-compile --cairo_path="$(TEST_PROOF_DIR):$(PROOF_BENCH_DIR)" $< --output $@ --proof_mode +$(MOD_BUILTIN_TEST_PROOF_DIR)/%.json: $(MOD_BUILTIN_TEST_PROOF_DIR)/%.cairo + cairo-compile --cairo_path="$(MOD_BUILTIN_TEST_PROOF_DIR):$(MOD_BUILTIN_TEST_PROOF_DIR)" $< --output $@ --proof_mode + # ====================== # Run without proof mode # ====================== @@ -86,6 +94,10 @@ PRINT_TEST_DIR=cairo_programs/print_feature PRINT_TEST_FILES:=$(wildcard $(PRINT_TEST_DIR)/*.cairo) COMPILED_PRINT_TESTS:=$(patsubst $(PRINT_TEST_DIR)/%.cairo, $(PRINT_TEST_DIR)/%.json, $(PRINT_TEST_FILES)) +MOD_BUILTIN_TEST_DIR=cairo_programs/mod_builtin_feature +MOD_BUILTIN_TEST_FILES:=$(wildcard $(MOD_BUILTIN_TEST_DIR)/*.cairo) +COMPILED_MOD_BUILTIN_TESTS:=$(patsubst $(MOD_BUILTIN_TEST_DIR)/%.cairo, $(MOD_BUILTIN_TEST_DIR)/%.json, $(MOD_BUILTIN_TEST_FILES)) + NORETROCOMPAT_DIR:=cairo_programs/noretrocompat NORETROCOMPAT_FILES:=$(wildcard $(NORETROCOMPAT_DIR)/*.cairo) COMPILED_NORETROCOMPAT_TESTS:=$(patsubst $(NORETROCOMPAT_DIR)/%.cairo, $(NORETROCOMPAT_DIR)/%.json, $(NORETROCOMPAT_FILES)) @@ -227,8 +239,8 @@ run: check: cargo check -cairo_test_programs: $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COMPILED_NORETROCOMPAT_TESTS) $(COMPILED_PRINT_TESTS) -cairo_proof_programs: $(COMPILED_PROOF_TESTS) +cairo_test_programs: $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COMPILED_NORETROCOMPAT_TESTS) $(COMPILED_PRINT_TESTS) $(COMPILED_MOD_BUILTIN_TESTS) +cairo_proof_programs: $(COMPILED_PROOF_TESTS) $(COMPILED_MOD_BUILTIN_PROOF_TESTS) cairo_bench_programs: $(COMPILED_BENCHES) cairo_1_test_contracts: $(CAIRO_1_COMPILED_CASM_CONTRACTS) cairo_2_test_contracts: $(CAIRO_2_COMPILED_CASM_CONTRACTS) @@ -375,3 +387,7 @@ hint-accountant: build-cairo-lang create-proof-programs-symlinks: cd cairo_programs/proof_programs; ln -s ../*.cairo . + +hyper-threading-benchmarks: cairo_bench_programs + cargo build -p hyper_threading --release && \ + sh examples/hyper_threading/hyper-threading.sh diff --git a/README.md b/README.md index d076edd945..337ca4e140 100644 --- a/README.md +++ b/README.md @@ -23,34 +23,37 @@ A faster and safer implementation of the Cairo VM in Rust ## Table of Contents -- [Disclaimer](#%EF%B8%8F-disclaimer) -- [About](#-about) +- [Table of Contents](#table-of-contents) +- [📖 About](#-about) - [The Cairo language](#the-cairo-language) -- [Getting Started](#-getting-started) +- [🌅 Getting Started](#-getting-started) - [Dependencies](#dependencies) -- [Usage](#-usage) + - [Required](#required) + - [Optional](#optional) + - [Installation script](#installation-script) +- [🚀 Usage](#-usage) - [Adding cairo-vm as a dependency](#adding-cairo-vm-as-a-dependency) - - [Running cairo-vm from the CLI](#running-cairo-vm-from-cli) + - [Running cairo-vm from CLI](#running-cairo-vm-from-cli) - [Using hints](#using-hints) - [Running a function in a Cairo program with arguments](#running-a-function-in-a-cairo-program-with-arguments) - [WebAssembly Demo](#webassembly-demo) - [Testing](#testing) -- [Benchmarks](#-benchmarks) -- [Changelog](#-changelog) -- [Contributing](#-contributing) -- [Related Projects](#-related-projects) -- [Documentation](#-documentation) + - [Tracer](#tracer) +- [📊 Benchmarks](#-benchmarks) +- [📜 Changelog](#-changelog) +- [🛠 Contributing](#-contributing) +- [🌞 Related Projects](#-related-projects) +- [📚 Documentation](#-documentation) - [Cairo](#cairo) - [Original Cairo VM Internals](#original-cairo-vm-internals) - [Compilers and Interpreters](#compilers-and-interpreters) - [StarkNet](#starknet) - - [Computational Integrity and Zero-Knowledge Proofs](#computational-integrity-and-zero-knowledge-proofs) -- [License](#%EF%B8%8F-license) + - [Computational Integrity and Zero Knowledge Proofs](#computational-integrity-and-zero-knowledge-proofs) + - [Basics](#basics) + - [ZK SNARKs](#zk-snarks) + - [STARKs](#starks) +- [⚖️ License](#️-license) -## ⚠️ Disclaimer - -🚧 `cairo-vm` is still being built therefore breaking changes might happen often so use it at your own risk. 🚧 -Cargo doesn't comply with [semver](https://semver.org/), so we advise to pin the version to 0.1.0. This can be done adding `cairo-vm = "0.1.0"` to your Cargo.toml ## 📖 About @@ -262,6 +265,10 @@ Now that you have the dependencies necessary to run the test suite you can run: make test ``` +### Tracer + +Cairo-vm offers a tracer which gives you a visualization of how your memory and registers change line after line as the VM executes the code. You can read more about it [here](./docs/tracer/README.md) + ## 📊 Benchmarks Running a [Cairo program](./cairo_programs/benchmarks/big_fibonacci.cairo) that gets the 1.5 millionth Fibonacci number we got the following benchmarks: @@ -290,6 +297,12 @@ Run only the `iai_benchmark` benchmark suite with cargo: cargo bench --bench iai_benchmark ``` +Benchmark the `cairo-vm` in a hyper-threaded environment with the [`examples/hyper_threading/ crate`](examples/hyper_threading/) +```bash +make hyper-threading-benchmarks +``` + + ## 📜 Changelog Keeps track of the latest changes [here](CHANGELOG.md). diff --git a/bench/criterion_benchmark.rs b/bench/criterion_benchmark.rs index 5f4c4c1193..48b095cc1f 100644 --- a/bench/criterion_benchmark.rs +++ b/bench/criterion_benchmark.rs @@ -4,10 +4,8 @@ use cairo_vm::{ }; use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion}; -#[cfg(feature = "with_mimalloc")] use mimalloc::MiMalloc; -#[cfg(feature = "with_mimalloc")] #[global_allocator] static ALLOC: MiMalloc = MiMalloc; diff --git a/bench/iai_benchmark.rs b/bench/iai_benchmark.rs index 07fcf214b8..02666c93c4 100644 --- a/bench/iai_benchmark.rs +++ b/bench/iai_benchmark.rs @@ -6,10 +6,8 @@ use cairo_vm::{ vm::{runners::cairo_runner::CairoRunner, vm_core::VirtualMachine}, }; -#[cfg(feature = "with_mimalloc")] use mimalloc::MiMalloc; -#[cfg(feature = "with_mimalloc")] #[global_allocator] static ALLOC: MiMalloc = MiMalloc; diff --git a/cairo-vm-cli/Cargo.toml b/cairo-vm-cli/Cargo.toml index 572c4db8af..0b63d2745e 100644 --- a/cairo-vm-cli/Cargo.toml +++ b/cairo-vm-cli/Cargo.toml @@ -9,6 +9,7 @@ keywords.workspace = true [dependencies] cairo-vm = { workspace = true, features = ["std"] } +cairo-vm-tracer = { workspace = true, optional = true } clap = { version = "4.3.10", features = ["derive"] } mimalloc = { version = "0.1.37", default-features = false, optional = true } nom = "7" @@ -21,4 +22,6 @@ rstest = "0.17.0" [features] default = ["with_mimalloc"] -with_mimalloc = ["cairo-vm/with_mimalloc", "dep:mimalloc"] +with_mimalloc = ["dep:mimalloc"] +with_tracer = ["cairo-vm/tracer", "cairo-vm-tracer"] +mod_builtin = ["cairo-vm/mod_builtin"] diff --git a/cairo-vm-cli/src/main.rs b/cairo-vm-cli/src/main.rs index 276dd6b70b..ddb97c068a 100644 --- a/cairo-vm-cli/src/main.rs +++ b/cairo-vm-cli/src/main.rs @@ -4,9 +4,19 @@ use bincode::enc::write::Writer; use cairo_vm::air_public_input::PublicInputError; use cairo_vm::cairo_run::{self, EncodeTraceError}; use cairo_vm::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; +#[cfg(feature = "with_tracer")] +use cairo_vm::serde::deserialize_program::DebugInfo; use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; use cairo_vm::vm::errors::trace_errors::TraceError; use cairo_vm::vm::errors::vm_errors::VirtualMachineError; +#[cfg(feature = "with_tracer")] +use cairo_vm::vm::runners::cairo_runner::CairoRunner; +#[cfg(feature = "with_tracer")] +use cairo_vm::vm::vm_core::VirtualMachine; +#[cfg(feature = "with_tracer")] +use cairo_vm_tracer::error::trace_data_errors::TraceDataError; +#[cfg(feature = "with_tracer")] +use cairo_vm_tracer::tracer::run_tracer; use clap::{Parser, ValueHint}; use std::io::{self, Write}; use std::path::{Path, PathBuf}; @@ -42,7 +52,7 @@ struct Args { air_public_input: Option, #[clap( long = "air_private_input", - requires_all = ["proof_mode", "trace_file", "memory_file"] + requires_all = ["proof_mode", "trace_file", "memory_file"] )] air_private_input: Option, #[clap( @@ -54,6 +64,9 @@ struct Args { cairo_pie_output: Option, #[structopt(long = "allow_missing_builtins")] allow_missing_builtins: Option, + #[structopt(long = "tracer")] + #[cfg(feature = "with_tracer")] + tracer: bool, } fn validate_layout(value: &str) -> Result { @@ -61,6 +74,7 @@ fn validate_layout(value: &str) -> Result { "plain" | "small" | "dex" + | "recursive" | "starknet" | "starknet_with_keccak" | "recursive_large_output" @@ -87,6 +101,9 @@ enum Error { Trace(#[from] TraceError), #[error(transparent)] PublicInput(#[from] PublicInputError), + #[error(transparent)] + #[cfg(feature = "with_tracer")] + TraceDataError(#[from] TraceDataError), } struct FileWriter { @@ -122,6 +139,31 @@ impl FileWriter { } } +#[cfg(feature = "with_tracer")] +fn start_tracer(cairo_runner: &CairoRunner, vm: &VirtualMachine) -> Result<(), TraceDataError> { + let relocation_table = vm + .relocate_segments() + .map_err(TraceDataError::FailedToGetRelocationTable)?; + let instruction_locations = cairo_runner + .get_program() + .get_relocated_instruction_locations(relocation_table.as_ref()); + let debug_info = instruction_locations.map(DebugInfo::new); + + let relocated_trace = cairo_runner + .relocated_trace + .clone() + .ok_or(TraceDataError::FailedToGetRelocatedTrace)?; + + run_tracer( + cairo_runner.get_program().clone(), + cairo_runner.relocated_memory.clone(), + relocated_trace.clone(), + 1, + debug_info, + )?; + Ok(()) +} + fn run(args: impl Iterator) -> Result<(), Error> { let args = Args::try_parse_from(args)?; @@ -183,6 +225,11 @@ fn run(args: impl Iterator) -> Result<(), Error> { std::fs::write(file_path, json)?; } + #[cfg(feature = "with_tracer")] + if args.tracer { + start_tracer(&cairo_runner, &vm)?; + } + if let (Some(file_path), Some(ref trace_file), Some(ref memory_file)) = (args.air_private_input, args.trace_file, args.memory_file) { @@ -291,7 +338,6 @@ mod tests { #[values(false, true)] memory_file: bool, #[values(false, true)] mut trace_file: bool, #[values(false, true)] proof_mode: bool, - #[values(false, true)] secure_run: bool, #[values(false, true)] print_output: bool, #[values(false, true)] entrypoint: bool, #[values(false, true)] air_public_input: bool, @@ -324,9 +370,6 @@ mod tests { if trace_file { args.extend_from_slice(&["--trace_file".to_string(), "/dev/null".to_string()]); } - if secure_run { - args.extend_from_slice(&["--secure_run".to_string(), "true".to_string()]); - } if print_output { args.extend_from_slice(&["--print_output".to_string()]); } diff --git a/cairo-vm-tracer/Cargo.toml b/cairo-vm-tracer/Cargo.toml new file mode 100644 index 0000000000..e8f933f4b0 --- /dev/null +++ b/cairo-vm-tracer/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "cairo-vm-tracer" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +readme.workspace = true + +[features] +default = ["std"] +std = [] +alloc = [] + +[dependencies] +cairo-vm = { workspace = true, features = ["arbitrary", "std"] } +thiserror-no-std = { workspace = true } +num-bigint = { workspace = true } +num-traits = { workspace = true } +axum = "0.6.18" +tokio = {version = "1.28.2", features = ["rt", "macros","rt-multi-thread"]} +serde = { workspace = true } +tower = { version = "0.4.13", features = ["util"] } +tower-http = { version = "0.4.0", features = ["full"] } +tracing = "0.1.37" +tracing-subscriber = "0.3.17" +include_dir = "0.7.3" +mime_guess = "2.0.4" diff --git a/cairo-vm-tracer/src/error/mod.rs b/cairo-vm-tracer/src/error/mod.rs new file mode 100644 index 0000000000..67535674a0 --- /dev/null +++ b/cairo-vm-tracer/src/error/mod.rs @@ -0,0 +1 @@ +pub mod trace_data_errors; diff --git a/cairo-vm-tracer/src/error/trace_data_errors.rs b/cairo-vm-tracer/src/error/trace_data_errors.rs new file mode 100644 index 0000000000..7af3f540cf --- /dev/null +++ b/cairo-vm-tracer/src/error/trace_data_errors.rs @@ -0,0 +1,24 @@ +use cairo_vm::vm::errors::{memory_errors::MemoryError, vm_errors::VirtualMachineError}; +use thiserror_no_std::Error; + +#[derive(Debug, Error)] +pub enum TraceDataError { + #[error("Instruction is None at pc {0} when encoding")] + InstructionIsNone(String), + #[error(transparent)] + InstructionDecodeError(#[from] VirtualMachineError), + #[error(transparent)] + FailedToGetRelocationTable(#[from] MemoryError), + #[error("Failed to get relocated trace")] + FailedToGetRelocatedTrace, + #[error("Failed to read file {0}")] + FailedToReadFile(String), + #[error("Input file is None {0}")] + InputFileIsNone(String), + #[error("Instruction encoding must be convertible to a u64")] + FailedToConvertInstructionEncoding, + #[error("Offset must be convertible to a usize")] + FailedToConvertOffset, + #[error("Imm address {0} must be convertible to a usize")] + FailedToImmAddress(String), +} diff --git a/cairo-vm-tracer/src/lib.rs b/cairo-vm-tracer/src/lib.rs new file mode 100644 index 0000000000..a17ac87737 --- /dev/null +++ b/cairo-vm-tracer/src/lib.rs @@ -0,0 +1,4 @@ +pub mod error; +pub mod tracer; +mod tracer_data; +mod types; diff --git a/cairo-vm-tracer/src/tracer.rs b/cairo-vm-tracer/src/tracer.rs new file mode 100644 index 0000000000..0edf2f9fe1 --- /dev/null +++ b/cairo-vm-tracer/src/tracer.rs @@ -0,0 +1,134 @@ +use std::{collections::HashMap, net::SocketAddr}; + +use axum::{ + body::{self, Empty, Full}, + extract::{Path, State}, + http::{header, HeaderValue, Response, StatusCode}, + response::IntoResponse, + routing::get, + Json, Router, +}; +use cairo_vm::utils::PRIME_STR; +use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry; +use cairo_vm::{serde::deserialize_program::DebugInfo, types::program::Program, Felt252}; +use include_dir::{include_dir, Dir}; +use num_bigint::BigInt; +use num_traits::{One, Signed}; +use serde::Serialize; +use tower_http::trace::{DefaultMakeSpan, DefaultOnResponse, TraceLayer}; +use tracing::Level; + +use crate::{ + error::trace_data_errors::TraceDataError, tracer_data::TracerData, + types::memory_access::MemoryAccess, +}; + +static STATIC_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/static"); + +#[tokio::main] +pub async fn run_tracer( + program: Program, + memory: Vec>, + trace: Vec, + program_base: u64, + debug_info: Option, +) -> Result<(), TraceDataError> { + let tracer_data = TracerData::new(program, memory, trace, program_base, debug_info)?; + + tracing_subscriber::fmt::init(); + let app = Router::new() + .route("/static/data.json", get(get_data)) + .route("/static/eval.json", get(get_eval)) + .route("/static/*path", get(static_path)) + .with_state(tracer_data) + .layer( + TraceLayer::new_for_http() + .make_span_with(DefaultMakeSpan::new().level(Level::INFO)) + .on_response(DefaultOnResponse::new().level(Level::INFO)), + ); + + let addr = SocketAddr::from(([127, 0, 0, 1], 8100)); + tracing::info!("listening on http://{}/static/index.html", addr); + axum::Server::bind(&addr) + .serve(app.into_make_service()) + .await + .unwrap(); + Ok(()) +} + +async fn get_data(tracer_data: State) -> Json { + let data_response = DataReponse { + code: tracer_data + .input_files + .iter() + .map(|(k, v)| (k.clone(), v.to_html())) + .collect(), + trace: tracer_data.trace.clone(), + memory: tracer_data + .memory + .iter() + .filter_map(|x| x.as_ref().map(|_| (*x).unwrap())) + .map(|x| { + field_element_repr( + &x.to_bigint(), + &BigInt::parse_bytes(PRIME_STR[2..].as_bytes(), 16).unwrap(), + ) + }) + .enumerate() + .map(|(i, v)| (i + 1, v)) + .collect(), + memory_accesses: tracer_data.memory_accesses.clone(), + public_memory: vec![], + }; + + // filter a vector of options to remove none values + + Json(data_response) +} + +async fn get_eval(_tracer_data: State) {} + +async fn static_path(Path(path): Path) -> impl IntoResponse { + let path = path.trim_start_matches('/'); + let mime_type = mime_guess::from_path(path).first_or_text_plain(); + + match STATIC_DIR.get_file(path) { + None => Response::builder() + .status(StatusCode::NOT_FOUND) + .body(body::boxed(Empty::new())) + .unwrap(), + Some(file) => Response::builder() + .status(StatusCode::OK) + .header( + header::CONTENT_TYPE, + HeaderValue::from_str(mime_type.as_ref()).unwrap(), + ) + .body(body::boxed(Full::from(file.contents()))) + .unwrap(), + } +} + +fn field_element_repr(val: &BigInt, prime: &BigInt) -> String { + // Shift val to the range (-prime / 2, prime / 2). + let shifted_val: BigInt = (val.clone() + prime.clone() / 2) % prime.clone() - prime.clone() / 2; + // If shifted_val is small, use decimal representation. + let two_pow_40: BigInt = BigInt::one() << 40; + if shifted_val.abs() < two_pow_40 { + return shifted_val.to_string(); + } + // Otherwise, use hex representation (allowing a sign if the number is close to prime). + let two_pow_100: BigInt = BigInt::one() << 100; + if shifted_val.abs() < two_pow_100 { + return format!("0x{:x}", shifted_val); + } + format!("0x{:x}", val) +} + +#[derive(Serialize)] +struct DataReponse { + code: HashMap, + trace: Vec, + memory: HashMap, + public_memory: Vec, + memory_accesses: Vec, +} diff --git a/cairo-vm-tracer/src/tracer_data.rs b/cairo-vm-tracer/src/tracer_data.rs new file mode 100644 index 0000000000..7c5268152e --- /dev/null +++ b/cairo-vm-tracer/src/tracer_data.rs @@ -0,0 +1,221 @@ +use std::collections::{BTreeMap, HashMap}; + +use cairo_vm::vm::trace::trace_entry::RelocatedTraceEntry; +use cairo_vm::{ + serde::deserialize_program::{DebugInfo, InstructionLocation}, + types::{ + instruction::Op1Addr, + program::Program, + relocatable::{MaybeRelocatable, Relocatable}, + }, + vm::{context::run_context::RunContext, decoding::decoder::decode_instruction}, + Felt252, +}; +use num_bigint::BigUint; +use num_traits::ToPrimitive; + +use crate::{error::trace_data_errors::TraceDataError, types::memory_access::MemoryAccess}; + +#[derive(Clone)] +pub struct InputCodeFile { + content: String, + lines: Vec, + tags: Vec<(usize, isize, String)>, +} + +impl InputCodeFile { + fn new(content: &str) -> Self { + let lines: Vec = content.lines().map(|line| line.to_string()).collect(); + InputCodeFile { + content: content.to_string(), + lines, + tags: Vec::new(), + } + } + + fn mark_text( + &mut self, + line_start: usize, + col_start: usize, + line_end: usize, + col_end: usize, + classes: &[&str], + ) { + let offset_start = self + .lines + .iter() + .take(line_start - 1) + .map(|line| line.len()) + .sum::() + + line_start + + col_start + - 2; + + let offset_end = self + .lines + .iter() + .take(line_end - 1) + .map(|line| line.len()) + .sum::() + + line_end + + col_end + - 2; + + self.tags.push(( + offset_start, + -(offset_end as isize), + format!("", classes.join(" ")), + )); + self.tags + .push((offset_end, -std::isize::MAX, "".to_string())); + } + + pub fn to_html(&self) -> String { + let mut res = self.content.replace(' ', "\0"); + let mut sorted_tags = self.tags.clone(); + sorted_tags.sort_by_key(|&(key, _, _)| key); + for &(pos, _, ref tag_content) in sorted_tags.iter().rev() { + res.insert_str(pos, tag_content); + } + res.replace('\0', " ").replace('\n', "
\n") + } +} + +// TODO: add support for taking air_public_input as an argument +#[derive(Clone)] +pub struct TracerData { + pub(crate) _program: Program, + pub(crate) memory: Vec>, + pub(crate) trace: Vec, + pub(crate) _program_base: u64, // TODO: adjust size based on maximum instructions possible + pub(crate) _debug_info: Option, + pub(crate) memory_accesses: Vec, + pub(crate) input_files: HashMap, +} + +impl TracerData { + pub fn new( + program: Program, + memory: Vec>, + trace: Vec, + program_base: u64, + debug_info: Option, + ) -> Result { + let mut input_files = HashMap::::new(); + + if let Some(debug_info) = debug_info.clone() { + // loop over debug_info + + //sort hashmap by key + let instruction_locations: BTreeMap = + debug_info.get_instruction_locations().into_iter().collect(); + for (pc_offset, instruction_location) in instruction_locations.iter() { + let loc = &instruction_location.inst; + let filename = &loc.input_file.filename; + let content = loc + .input_file + .get_content() + .map_err(|_| TraceDataError::FailedToReadFile(filename.clone()))?; + if !input_files.contains_key(filename) { + input_files.insert(filename.clone(), InputCodeFile::new(content.as_str())); + } + let input_file = input_files.get_mut(filename); + if input_file.is_none() { + return Err(TraceDataError::InputFileIsNone(filename.clone())); + } + let input_file = input_file.unwrap(); + + input_file.mark_text( + loc.start_line as usize, + loc.start_col as usize, + loc.end_line as usize, + loc.end_col as usize, + &[format!("inst{}", pc_offset).as_str(), "instruction"], + ); + } + } + + let mut memory_accesses: Vec = vec![]; + //loop of trace + for entry in trace.iter() { + let run_context = RunContext::new(Relocatable::from((0, entry.pc)), entry.ap, entry.fp); + + let (instruction_encoding, _) = + get_instruction_encoding(entry.pc, &memory, program.prime())?; + + let instruction_encoding = instruction_encoding.to_u64(); + if instruction_encoding.is_none() { + return Err(TraceDataError::FailedToConvertInstructionEncoding); + } + let instruction_encoding = instruction_encoding.unwrap(); + let instruction = decode_instruction(instruction_encoding)?; + + // get dst_addr + let dst_addr = run_context.compute_dst_addr(&instruction)?.offset; + + // get op0_addr + let op0_addr = run_context.compute_op0_addr(&instruction)?.offset; + + // get op1_addr + let mut op0: Result, TraceDataError> = Ok(None); + if instruction.op1_addr == Op1Addr::Op0 { + let op0_memory = &memory[op0_addr]; + op0 = match op0_memory { + None => Ok(None), + Some(felt) => { + let offset = felt.clone().to_usize(); + if offset.is_none() { + return Err(TraceDataError::FailedToConvertOffset); + } + let offset = offset.unwrap(); + Ok(Some(MaybeRelocatable::RelocatableValue(Relocatable { + segment_index: 1_isize, + offset, + }))) + } + }; + } + let op0 = op0?; + let op1_addr = run_context + .compute_op1_addr(&instruction, op0.as_ref())? + .offset; + + // add to memory access + memory_accesses.push(MemoryAccess { + dst: dst_addr, + op0: op0_addr, + op1: op1_addr, + }); + } + + Ok(TracerData { + _program: program, + memory, + trace, + _program_base: program_base, + _debug_info: debug_info, + memory_accesses, + input_files, + }) + } +} + +// Returns the encoded instruction (the value at pc) and the immediate value (the value at +// pc + 1, if it exists in the memory). +pub fn get_instruction_encoding( + pc: usize, + memory: &[Option], + prime: &str, +) -> Result<(Felt252, Option), TraceDataError> { + if memory[pc].is_none() { + return Err(TraceDataError::InstructionIsNone(pc.to_string())); + } + let instruction_encoding = memory[pc].unwrap(); + let prime = BigUint::parse_bytes(prime[2..].as_bytes(), 16).unwrap(); + + let imm_addr = BigUint::from(pc + 1) % prime; + let imm_addr = usize::try_from(imm_addr.clone()) + .map_err(|_| TraceDataError::FailedToImmAddress(imm_addr.to_string()))?; + let optional_imm = memory[imm_addr]; + Ok((instruction_encoding, optional_imm)) +} diff --git a/cairo-vm-tracer/src/types/memory_access.rs b/cairo-vm-tracer/src/types/memory_access.rs new file mode 100644 index 0000000000..21507b313f --- /dev/null +++ b/cairo-vm-tracer/src/types/memory_access.rs @@ -0,0 +1,9 @@ +use serde::Serialize; + +// TODO: check if the sizes are corect +#[derive(Serialize, Clone)] +pub struct MemoryAccess { + pub(crate) dst: usize, + pub(crate) op0: usize, + pub(crate) op1: usize, +} diff --git a/cairo-vm-tracer/src/types/mod.rs b/cairo-vm-tracer/src/types/mod.rs new file mode 100644 index 0000000000..8b7ec4be21 --- /dev/null +++ b/cairo-vm-tracer/src/types/mod.rs @@ -0,0 +1 @@ +pub mod memory_access; diff --git a/cairo-vm-tracer/static/index.html b/cairo-vm-tracer/static/index.html new file mode 100644 index 0000000000..9606f87125 --- /dev/null +++ b/cairo-vm-tracer/static/index.html @@ -0,0 +1,56 @@ + + + + + + Cairo Tracer + + + + + + + +
+
+
+
+
+
+ Follow: + +
+
+
+
+ (s) Step
+ (S) Previous step
+ (n) Step over
+ (N) Previous step over
+ (o) Step out
+ (b) Run until next breakpoint
+ (B) Run until previous breakpoint
+
+ pc =
+ ap =
+ fp =
+
+ dst_addr =
+ op0_addr =
+ op1_addr =
+
+ Stack trace: +
+
+ + +
+ + + diff --git a/cairo-vm-tracer/static/tracer.css b/cairo-vm-tracer/static/tracer.css new file mode 100644 index 0000000000..c4ec672eed --- /dev/null +++ b/cairo-vm-tracer/static/tracer.css @@ -0,0 +1,81 @@ +html, body { + margin: 0px; + padding: 0px; + height: 100%; +} + +#code_div, #info_td, #memory_div_parent { + font-family: monospace; +} + +.current_instruction, .current_pc_mem { + background: yellow !important; +} + +.highlight_instruction { + background: lightblue; +} + +.breakpoint { + background: #99ccff; +} + +#code_div_parent, #memory_div_parent { + flex-flow: column; + height: 100%; + vertical-align: top; +} + +#code_div_parent { + background: rgb(238, 255, 255); + overflow-y: scroll; +} + +#memory_div_parent { + background: rgb(255, 255, 250); + display: flex; +} + +#memory_div { + overflow-y: scroll; + flex-flow: row; +} + +.mem_info { + display: inline-block; + width: 40px; + text-align: right; + font-size: 70%; +} + +#main_table { + height: 100%; +} + +#code_div { + margin: 10px; +} + +#memory_div>table { + height: 100%; + margin: 10px; +} + +.table_with_border { + border-collapse: collapse; +} + +.table_with_border>tr>td, .table_with_border>tr>th { + border: 1px black solid; +} + +.filename { + font-weight: bold; + text-decoration: underline; + margin-top: 10px; + margin-bottom: 5px; +} + +.public_memory { + background-color: #f0fff8; +} diff --git a/cairo-vm-tracer/static/tracer.js b/cairo-vm-tracer/static/tracer.js new file mode 100644 index 0000000000..61b97a60b5 --- /dev/null +++ b/cairo-vm-tracer/static/tracer.js @@ -0,0 +1,387 @@ +/* + The number of the current step being viewed in the tracer. +*/ +var current_step; + +var trace; +var memory; +var memory_accesses; + +/* + A list of objects {watch_expr: ..., watch_result: ...} where watch_expr is the element + and watch_result is the result element. +*/ +var watch_exprs = []; + +/* + The maximum number of entries shown in the stack trace. +*/ +const MAX_STACK_TRACE = 100; + +function load_json() { + $.getJSON("data.json", function (data) { + trace = data.trace; + memory = data.memory; + memory_accesses = data.memory_accesses; + for (const filename in data.code) { + $("#code_div") + .append($("
").addClass("filename").text(filename)) + .append($("
").html(data.code[filename])); + } + $("#slider_div").append(create_slider()); + $("#memory_div").append(create_memory_table()); + $("#watch_table").append(create_watch_row()); + mark_public_memory(data.public_memory); + goto_step(0); + + $(".instruction").dblclick(toggle_breakpoint); + $(".mem_row").dblclick(toggle_breakpoint); + }); +} + +/* + Adds a slider that tracks the progress of the program. +*/ +function create_slider() { + const slider = $("").attr({ + id: "slider", + type: "range", + min: 0, + max: trace.length - 1, + value: 0, + }); + + slider[0].oninput = function () { + goto_step(parseInt($("#slider").val())); + }; + + return slider; +} + +function create_memory_table() { + const table = $("").addClass("table_with_border"); + for (const addr in memory) { + table.append( + $("") + .attr({ id: "mem_row" + addr }) + .addClass("mem_row") + .append( + $("") + .append($("").append(fp_cell).append(pc_cell); + + pc_cell.mouseenter(function () { + $(".inst" + pc).addClass("highlight_instruction"); + }); + pc_cell.mouseleave(function () { + $(".inst" + pc).removeClass("highlight_instruction"); + }); + + return row; +} + +function create_watch_row() { + const watch_expr = $("").attr({ type: "text" }); + const watch_result = $(""); + + watch_exprs.push({ watch_expr: watch_expr, watch_result: watch_result }); + + watch_expr.keyup(function (event) { + if (event.key == "Enter") { + update_watch(); + } + }); + + const n_watches = watch_exprs.length; + watch_expr.change(function (event) { + // Add new watch if needed. + if (watch_expr.val() != "" && watch_exprs.length == n_watches) { + $("#watch_table").append(create_watch_row()); + } + + update_watch(); + }); + + // Make sure navigation keys (s, n, ...) will not work when watch_expr is focused. + watch_expr.keypress(function (event) { + event.stopPropagation(); + }); + + return $("") + .append($("
").append( + $("") + .attr({ id: "mem_info" + addr }) + .addClass("mem_info") + ) + ) + .append($("").text(addr)) + .append($("").text(memory[addr])) + ); + } + return table; +} + +function mark_public_memory(public_memory) { + for (const addr of public_memory) { + $("#mem_row" + addr).addClass("public_memory"); + } +} + +function update_current_instruction_view() { + const entry = trace[current_step]; + const pc = entry.pc; + const ap = entry.ap; + const fp = entry.fp; + + $(".instruction").removeClass("current_instruction"); + $(".inst" + pc).addClass("current_instruction"); + $("#pc").text(pc); + $("#ap").text(ap); + $("#fp").text(fp); + + const mem_accesses = memory_accesses[current_step]; + $("#dst_addr").text(mem_accesses.dst); + $("#op0_addr").text(mem_accesses.op0); + $("#op1_addr").text(mem_accesses.op1); + + $(".mem_row").removeClass("current_pc_mem"); + $("#mem_row" + pc).addClass("current_pc_mem"); + + $(".mem_info").text(""); + $("#mem_info" + ap).append("ap "); + $("#mem_info" + fp).append("fp "); + + update_stack_trace_view(); + + if ($("#slider").val() != current_step) { + $("#slider").val(current_step); + } + + scrollIntoViewIfNeeded($(".inst" + pc)[0]); + + if ($("#memory_follow").val() == "pc") { + scrollIntoViewIfNeeded($("#mem_row" + pc)[0]); + } else if ($("#memory_follow").val() == "ap") { + scrollIntoViewIfNeeded($("#mem_row" + ap)[0]); + } else if ($("#memory_follow").val() == "fp") { + scrollIntoViewIfNeeded($("#mem_row" + fp)[0]); + } + + update_watch(); +} + +function scrollIntoViewIfNeeded(target) { + if (target === undefined) { + return; + } + + const rect = target.getBoundingClientRect(); + const scrollParent = getScrollParent(target); + const scrollParentRect = scrollParent.getBoundingClientRect(); + if (rect.bottom > scrollParentRect.bottom) { + // Scroll down. + const scrollMargin = 64; + const targetBottom = scrollParent.scrollTop + rect.bottom; + scrollParent.scrollTo({ + top: targetBottom - scrollParent.clientHeight + scrollMargin, + behavior: "smooth", + }); + } else if (rect.top < scrollParentRect.top) { + // Scroll up. + const scrollMargin = 32; + const targetTop = scrollParent.scrollTop + rect.top; + scrollParent.scrollTo({ + top: targetTop - scrollMargin, + behavior: "smooth", + }); + } +} + +function getScrollParent(node) { + node = node.parentNode; + while (getComputedStyle(node)["overflow-y"] != "scroll") { + node = node.parentNode; + } + return node; +} + +function update_stack_trace_view() { + const entry = trace[current_step]; + const initial_fp = trace[0].fp; + + $(".mem_row").css("border-top", ""); + + const stack_trace = $("#stack_trace"); + stack_trace + .empty() + .append( + $("
").append("fp")) + .append($("").append("return pc")) + ); + + var fp = entry.fp; + for (var i = 0; i < MAX_STACK_TRACE && fp != initial_fp; i++) { + const pc = memory[fp - 1]; + stack_trace.append(create_stack_trace_row(fp, pc)); + $("#mem_row" + fp).css("border-top", "2px black solid"); + fp = memory[fp - 2]; + } +} + +function create_stack_trace_row(fp, pc) { + const fp_cell = $("").append(fp); + const pc_cell = $("").append(pc); + const row = $("
").append(watch_expr)) + .append($("").append(watch_result)); +} + +var update_watch_ajax = { abort: function () {} }; + +function update_watch() { + // REMOVE RETURN TO EVALUATION EXPRESSION + return; + var query_str = "eval.json?step=" + encodeURIComponent(current_step); + for (const entry of watch_exprs) { + const expr_txt = entry.watch_expr.val(); + query_str += + "&expr=" + encodeURIComponent(expr_txt == "" ? "null" : expr_txt); + } + // Abort previous AJAX request if exists. + update_watch_ajax.abort(); + update_watch_ajax = $.getJSON(query_str, function (data) { + for (var idx = 0; idx < data.length; ++idx) { + watch_exprs[idx].watch_result.text(data[idx]); + } + }); +} + +/* + Clears the text selection in the window. + This function was copied from + https://stackoverflow.com/questions/880512/prevent-text-selection-after-double-click. +*/ +function clearSelection() { + if (document.selection && document.selection.empty) { + document.selection.empty(); + } else if (window.getSelection) { + var sel = window.getSelection(); + sel.removeAllRanges(); + } +} + +function toggle_breakpoint(event) { + $(this).toggleClass("breakpoint"); + clearSelection(); + event.stopPropagation(); +} + +function has_breakpoint(step) { + return ( + $("#mem_row" + trace[step].pc).hasClass("breakpoint") || + $("#mem_row" + memory_accesses[step].dst).hasClass("breakpoint") || + $("#mem_row" + memory_accesses[step].op0).hasClass("breakpoint") || + $("#mem_row" + memory_accesses[step].op1).hasClass("breakpoint") + ); +} + +function goto_step(i) { + // Update global variable. + current_step = i; + update_current_instruction_view(); +} + +function step() { + if (current_step < trace.length - 1) { + goto_step(current_step + 1); + } +} + +function previous_step() { + if (current_step > 0) { + goto_step(current_step - 1); + } +} + +function step_over() { + const current_fp = trace[current_step].fp; + for (var i = current_step + 1; i < trace.length; i++) { + if (trace[i].fp == current_fp || has_breakpoint(i)) { + goto_step(i); + return; + } + } +} + +function previous_step_over() { + const current_fp = trace[current_step].fp; + for (var i = current_step - 1; i >= 0; i--) { + if (trace[i].fp == current_fp) { + goto_step(i); + return; + } + } +} + +function step_out() { + const current_fp = trace[current_step].fp; + const previous_fp = memory[current_fp - 2]; + for (var i = current_step + 1; i < trace.length; i++) { + if (trace[i].fp == previous_fp) { + goto_step(i); + return; + } + } +} + +function next_breakpoint() { + for (var i = current_step + 1; i < trace.length; i++) { + if (has_breakpoint(i)) { + goto_step(i); + return; + } + } +} + +function previous_breakpoint() { + const current_fp = trace[current_step].fp; + for (var i = current_step - 1; i >= 0; i--) { + if (has_breakpoint(i)) { + goto_step(i); + return; + } + } +} + +$(document).ready(function () { + load_json(); +}); + +$(document).keypress(function (event) { + if (event.key == "s") { + step(); + event.stopPropagation(); + } + if (event.key == "S") { + previous_step(); + event.stopPropagation(); + } + if (event.key == "n") { + step_over(); + event.stopPropagation(); + } + if (event.key == "N") { + previous_step_over(); + event.stopPropagation(); + } + if (event.key == "o") { + step_out(); + event.stopPropagation(); + } + if (event.key == "b") { + next_breakpoint(); + event.stopPropagation(); + } + if (event.key == "B") { + previous_breakpoint(); + event.stopPropagation(); + } +}); diff --git a/cairo1-run/Cargo.toml b/cairo1-run/Cargo.toml index 391774f86a..a4423fabf8 100644 --- a/cairo1-run/Cargo.toml +++ b/cairo1-run/Cargo.toml @@ -26,7 +26,8 @@ bincode.workspace = true assert_matches = "1.5.0" rstest = "0.17.0" mimalloc = { version = "0.1.37", default-features = false, optional = true } +num-traits = { version = "0.2", default-features = false } [features] default = ["with_mimalloc"] -with_mimalloc = ["cairo-vm/with_mimalloc", "dep:mimalloc"] +with_mimalloc = ["dep:mimalloc"] diff --git a/cairo1-run/README.md b/cairo1-run/README.md index 2f0cc5a8b3..8b7244314f 100644 --- a/cairo1-run/README.md +++ b/cairo1-run/README.md @@ -66,3 +66,5 @@ The cairo1-run cli supports the following optional arguments: * `--air_private_input `: Receives the name of a file and outputs the AIR private inputs into it. Can only be used if proof_mode, trace_file & memory_file are also enabled. * `--cairo_pie_output `: Receives the name of a file and outputs the Cairo PIE into it. Can only be used if proof_mode, is not enabled. + +* `--append_return_values`: Adds extra instructions to the program in order to append the return values to the output builtin's segment. This is the default behaviour for proof_mode. diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 5dbda6c672..8477eb7d88 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -1,4 +1,6 @@ -use cairo_lang_casm::{casm, casm_extend, hints::Hint, instructions::Instruction}; +use cairo_lang_casm::{ + casm, casm_extend, hints::Hint, inline::CasmContext, instructions::Instruction, +}; use cairo_lang_sierra::{ extensions::{ bitwise::BitwiseType, @@ -13,19 +15,20 @@ use cairo_lang_sierra::{ ConcreteType, NamedType, }, ids::ConcreteTypeId, - program::{Function, Program as SierraProgram}, + program::{Function, GenericArg, Program as SierraProgram}, program_registry::ProgramRegistry, }; use cairo_lang_sierra_ap_change::calc_ap_changes; -use cairo_lang_sierra_gas::gas_info::GasInfo; +use cairo_lang_sierra_gas::{gas_info::GasInfo, objects::CostInfoProvider}; use cairo_lang_sierra_to_casm::{ compiler::CairoProgram, metadata::{calc_metadata, Metadata, MetadataComputationConfig, MetadataError}, }; use cairo_lang_sierra_type_size::get_type_size_map; -use cairo_lang_utils::unordered_hash_map::UnorderedHashMap; +use cairo_lang_utils::{casts::IntoOrPanic, unordered_hash_map::UnorderedHashMap}; use cairo_vm::{ hint_processor::cairo_1_hint_processor::hint_processor::Cairo1HintProcessor, + math_utils::signed_felt, serde::deserialize_program::{ ApTracking, BuiltinName, FlowTrackingData, HintParams, ReferenceManager, }, @@ -43,14 +46,17 @@ use cairo_vm::{ }, Felt252, }; -use itertools::chain; -use std::collections::HashMap; +use itertools::{chain, Itertools}; +use num_traits::{cast::ToPrimitive, Zero}; +use std::{collections::HashMap, iter::Peekable}; use crate::{Error, FuncArg}; #[derive(Debug)] pub struct Cairo1RunConfig<'a> { pub args: &'a [FuncArg], + // Serializes program output into a user-friendly format + pub serialize_output: bool, pub trace_enabled: bool, pub relocate_mem: bool, pub layout: &'a str, @@ -58,27 +64,39 @@ pub struct Cairo1RunConfig<'a> { // Should be true if either air_public_input or cairo_pie_output are needed // Sets builtins stop_ptr by calling `final_stack` on each builtin pub finalize_builtins: bool, + // Appends return values to the output segment. This is performed by default when running in proof_mode + pub append_return_values: bool, } impl Default for Cairo1RunConfig<'_> { fn default() -> Self { Self { args: Default::default(), + serialize_output: false, trace_enabled: false, relocate_mem: false, layout: "plain", proof_mode: false, finalize_builtins: false, + append_return_values: false, } } } // Runs a Cairo 1 program -// Returns the runner & VM after execution + the return values +// Returns the runner & VM after execution + the return values + the serialized return values (if serialize_output is enabled) pub fn cairo_run_program( sierra_program: &SierraProgram, cairo_run_config: Cairo1RunConfig, -) -> Result<(CairoRunner, VirtualMachine, Vec), Error> { +) -> Result< + ( + CairoRunner, + VirtualMachine, + Vec, + Option, + ), + Error, +> { let metadata = create_metadata(sierra_program, Some(Default::default()))?; let sierra_program_registry = ProgramRegistry::::new(sierra_program)?; let type_sizes = @@ -98,8 +116,7 @@ pub fn cairo_run_program( &type_sizes, main_func, initial_gas, - cairo_run_config.proof_mode, - cairo_run_config.args, + &cairo_run_config, )?; // Fetch return type data @@ -115,20 +132,12 @@ pub fn cairo_run_program( // This footer is used by lib funcs let libfunc_footer = create_code_footer(); - - // Header used to initiate the infinite loop after executing the program - // Also appends return values to output segment - let proof_mode_header = if cairo_run_config.proof_mode { - create_proof_mode_header(builtins.len() as i16, return_type_size) - } else { - casm! {}.instructions - }; + let builtin_count: i16 = builtins.len().into_or_panic(); // This is the program we are actually running/proving // With (embedded proof mode), cairo1 header and the libfunc footer let instructions = chain!( - proof_mode_header.iter(), - entry_code.iter(), + entry_code.instructions.iter(), casm_program.instructions.iter(), libfunc_footer.iter(), ); @@ -147,12 +156,12 @@ pub fn cairo_run_program( let program = if cairo_run_config.proof_mode { Program::new_for_proof( - builtins, + builtins.clone(), data, 0, // Proof mode is on top - // jmp rel 0 is on PC == 2 - 2, + // `jmp rel 0` is the last line of the entry code. + entry_code.current_code_offset - 2, program_hints, ReferenceManager { references: Vec::new(), @@ -163,7 +172,7 @@ pub fn cairo_run_program( )? } else { Program::new( - builtins, + builtins.clone(), data, Some(0), program_hints, @@ -191,38 +200,68 @@ pub fn cairo_run_program( // Run it until the end / infinite loop in proof_mode runner.run_until_pc(end, &mut vm, &mut hint_processor)?; if cairo_run_config.proof_mode { - // As we will be inserting the return values into the output segment after running the main program (right before the infinite loop) the computed size for the output builtin will be 0 - // We need to manually set the segment size for the output builtin's segment so memory hole counting doesn't fail due to having a higher accessed address count than the segment's size - vm.segments - .segment_sizes - .insert(2, return_type_size as usize); + runner.run_for_steps(1, &mut vm, &mut hint_processor)?; } + runner.end_run(false, false, &mut vm, &mut hint_processor)?; + let skip_output = cairo_run_config.proof_mode || cairo_run_config.append_return_values; // Fetch return values - let return_values = fetch_return_values(return_type_size, return_type_id, &vm)?; + let return_values = fetch_return_values( + return_type_size, + return_type_id, + &vm, + builtin_count, + skip_output, + )?; + + let serialized_output = if cairo_run_config.serialize_output { + Some(serialize_output( + &return_values, + &mut vm, + return_type_id, + &sierra_program_registry, + &type_sizes, + )) + } else { + None + }; // Set stop pointers for builtins so we can obtain the air public input if cairo_run_config.finalize_builtins { - finalize_builtins( - cairo_run_config.proof_mode, - &main_func.signature.ret_types, - &type_sizes, - &mut vm, - )?; + if skip_output { + // Set stop pointer for each builtin + vm.builtins_final_stack_from_stack_pointer_dict( + &builtins + .iter() + .enumerate() + .map(|(i, builtin)| { + ( + builtin.name(), + (vm.get_ap() - (builtins.len() - 1 - i)).unwrap(), + ) + }) + .collect(), + false, + )?; + } else { + finalize_builtins( + &main_func.signature.ret_types, + &type_sizes, + &mut vm, + builtin_count, + )?; + } // Build execution public memory if cairo_run_config.proof_mode { - // As the output builtin is not used by the program we need to compute it's stop ptr manually - vm.set_output_stop_ptr_offset(return_type_size as usize); - runner.finalize_segments(&mut vm)?; } } runner.relocate(&mut vm, true)?; - Ok((runner, vm, return_values)) + Ok((runner, vm, return_values, serialized_output)) } fn additional_initialization(vm: &mut VirtualMachine, data_len: usize) -> Result<(), Error> { @@ -302,38 +341,6 @@ fn create_code_footer() -> Vec { .instructions } -// Create proof_mode specific instructions -// Including the "canonical" proof mode instructions (the ones added by the compiler in cairo 0) -// wich call the firt program instruction and then initiate an infinite loop. -// And also appending the return values to the output builtin's memory segment -fn create_proof_mode_header(builtin_count: i16, return_type_size: i16) -> Vec { - // As the output builtin is not used by cairo 1 (we forced it for this purpose), it's segment is always empty - // so we can start writing values directly from it's base, which is located relative to the fp before the other builtin's bases - let output_fp_offset: i16 = -(builtin_count + 2); // The 2 here represents the return_fp & end segments - - // The pc offset where the original program should start - // Without this header it should start at 0, but we add 2 for each call and jump instruction (as both of them use immediate values) - // and also 1 for each instruction added to copy each return value into the output segment - let program_start_offset: i16 = 4 + return_type_size; - - let mut ctx = casm! {}; - casm_extend! {ctx, - call rel program_start_offset; // Begin program execution by calling the first instruction in the original program - }; - // Append each return value to the output segment - for (i, j) in (1..return_type_size + 1).rev().enumerate() { - casm_extend! {ctx, - // [ap -j] is where each return value is located in memory - // [[fp + output_fp_offet] + 0] is the base of the output segment - [ap - j] = [[fp + output_fp_offset] + i as i16]; - }; - } - casm_extend! {ctx, - jmp rel 0; // Infinite loop - }; - ctx.instructions -} - /// Returns the instructions to add to the beginning of the code to successfully call the main /// function, as well as the builtins required to execute the program. fn create_entry_code( @@ -342,18 +349,19 @@ fn create_entry_code( type_sizes: &UnorderedHashMap, func: &Function, initial_gas: usize, - proof_mode: bool, - args: &[FuncArg], -) -> Result<(Vec, Vec), Error> { - let mut ctx = casm! {}; + config: &Cairo1RunConfig, +) -> Result<(CasmContext, Vec), Error> { + let copy_to_output_builtin = config.proof_mode || config.append_return_values; + let signature = &func.signature; // The builtins in the formatting expected by the runner. - let (builtins, builtin_offset) = get_function_builtins(func, proof_mode); - + let (builtins, builtin_offset) = + get_function_builtins(&signature.param_types, copy_to_output_builtin); + let mut ctx = casm! {}; // Load all vecs to memory. // Load all array args content to memory. let mut array_args_data = vec![]; let mut ap_offset: i16 = 0; - for arg in args { + for arg in config.args { let FuncArg::Array(values) = arg else { continue; }; @@ -373,10 +381,10 @@ fn create_entry_code( } let mut array_args_data_iter = array_args_data.iter(); let after_arrays_data_offset = ap_offset; - let mut arg_iter = args.iter().enumerate(); + let mut arg_iter = config.args.iter().enumerate(); let mut param_index = 0; let mut expected_arguments_size = 0; - if func.signature.param_types.iter().any(|ty| { + if signature.param_types.iter().any(|ty| { get_info(sierra_program_registry, ty) .map(|x| x.long_id.generic_id == SegmentArenaType::ID) .unwrap_or_default() @@ -395,19 +403,13 @@ fn create_entry_code( } ap_offset += 3; } - for ty in func.signature.param_types.iter() { + + for ty in &signature.param_types { let info = get_info(sierra_program_registry, ty) .ok_or_else(|| Error::NoInfoForType(ty.clone()))?; let generic_ty = &info.long_id.generic_id; if let Some(offset) = builtin_offset.get(generic_ty) { - let mut offset = *offset; - if proof_mode { - // Everything is off by 2 due to the proof mode header - offset += 2; - } - casm_extend! {ctx, - [ap + 0] = [fp - offset], ap++; - } + casm_extend!(ctx, [ap + 0] = [fp - *offset], ap++;); ap_offset += 1; } else if generic_ty == &SystemType::ID { casm_extend! {ctx, @@ -416,15 +418,11 @@ fn create_entry_code( } ap_offset += 1; } else if generic_ty == &GasBuiltinType::ID { - casm_extend! {ctx, - [ap + 0] = initial_gas, ap++; - } + casm_extend!(ctx, [ap + 0] = initial_gas, ap++;); ap_offset += 1; } else if generic_ty == &SegmentArenaType::ID { let offset = -ap_offset + after_arrays_data_offset; - casm_extend! {ctx, - [ap + 0] = [ap + offset] + 3, ap++; - } + casm_extend!(ctx, [ap + 0] = [ap + offset] + 3, ap++;); ap_offset += 1; } else { let ty_size = type_sizes[ty]; @@ -460,7 +458,8 @@ fn create_entry_code( param_index += 1; }; } - let actual_args_size = args + let actual_args_size = config + .args .iter() .map(|arg| match arg { FuncArg::Single(_) => 1, @@ -475,17 +474,107 @@ fn create_entry_code( } let before_final_call = ctx.current_code_offset; - let final_call_size = 3; + + let return_type_id = signature + .ret_types + .last() + .ok_or(Error::NoRetTypesInSignature)?; + let return_type_size = type_sizes + .get(return_type_id) + .cloned() + .ok_or_else(|| Error::NoTypeSizeForId(return_type_id.clone()))?; + let builtin_count: i16 = builtins.len().into_or_panic(); + let builtin_locations: Vec = builtins + .iter() + .enumerate() + .map(|(i, name)| { + let fp_loc = i.into_or_panic::() - builtin_count - 2; + let generic = match name { + BuiltinName::range_check => RangeCheckType::ID, + BuiltinName::pedersen => PedersenType::ID, + BuiltinName::bitwise => BitwiseType::ID, + BuiltinName::ec_op => EcOpType::ID, + BuiltinName::poseidon => PoseidonType::ID, + BuiltinName::segment_arena => SegmentArenaType::ID, + BuiltinName::keccak + | BuiltinName::ecdsa + | BuiltinName::output + | BuiltinName::range_check96 + | BuiltinName::add_mod + | BuiltinName::mul_mod => return fp_loc, + }; + signature + .ret_types + .iter() + .position(|ty| { + sierra_program_registry + .get_type(ty) + .unwrap() + .info() + .long_id + .generic_id + == generic + }) + .map(|i| (signature.ret_types.len() - i).into_or_panic()) + .unwrap_or(fp_loc) + }) + .collect(); + if copy_to_output_builtin { + assert!( + builtins.iter().contains(&BuiltinName::output), + "Output builtin is required for proof mode or append_return_values" + ); + } + + let final_call_size = + // The call. + 2 + // The copying of the return values to the output segment. + + if copy_to_output_builtin { return_type_size.into_or_panic::() + 1 } else { 0 } + // Rewriting the builtins to top of the stack. + + builtins.len() + // The return or infinite loop. + + if config.proof_mode { 2 } else { 1 }; let offset = final_call_size + casm_program.debug_info.sierra_statement_info[func.entry_point.0].code_offset; - casm_extend! {ctx, - call rel offset; - ret; + casm_extend!(ctx, call rel offset;); + + if copy_to_output_builtin { + let Some(output_builtin_idx) = builtins.iter().position(|b| b == &BuiltinName::output) + else { + panic!("Output builtin is required for proof mode or append_return_values."); + }; + let output_fp_offset: i16 = builtin_locations[output_builtin_idx]; + for (i, j) in (1..return_type_size + 1).rev().enumerate() { + casm_extend! {ctx, + // [ap -j] is where each return value is located in memory + // [[fp + output_fp_offet] + 0] is the base of the output segment + [ap - j] = [[fp + output_fp_offset] + i as i16]; + }; + } + } + let mut ret_builtin_offset = return_type_size - 1; + for (builtin, location) in builtins.iter().zip(builtin_locations) { + if builtin == &BuiltinName::output && copy_to_output_builtin { + casm_extend!(ctx, [ap + 0] = [fp + location] + return_type_size, ap++;); + } else if location < 0 { + casm_extend!(ctx, [ap + 0] = [fp + location], ap++;); + } else { + casm_extend!(ctx, [ap + 0] = [ap - (ret_builtin_offset + location)], ap++;); + } + ret_builtin_offset += 1; + } + + if config.proof_mode { + casm_extend!(ctx, jmp rel 0;); + } else { + casm_extend!(ctx, ret;); } + assert_eq!(before_final_call + final_call_size, ctx.current_code_offset); - Ok((ctx.instructions, builtins)) + Ok((ctx, builtins)) } fn get_info<'a>( @@ -520,74 +609,35 @@ fn create_metadata( } } -/// Type representing the Output builtin. -#[derive(Default)] -pub struct OutputType {} -impl cairo_lang_sierra::extensions::NoGenericArgsGenericType for OutputType { - const ID: cairo_lang_sierra::ids::GenericTypeId = - cairo_lang_sierra::ids::GenericTypeId::new_inline("Output"); - const STORABLE: bool = true; - const DUPLICATABLE: bool = false; - const DROPPABLE: bool = false; - const ZERO_SIZED: bool = false; -} - fn get_function_builtins( - func: &Function, - proof_mode: bool, + params: &[cairo_lang_sierra::ids::ConcreteTypeId], + append_output: bool, ) -> ( Vec, HashMap, ) { - let entry_params = &func.signature.param_types; let mut builtins = Vec::new(); let mut builtin_offset: HashMap = HashMap::new(); let mut current_offset = 3; - // Fetch builtins from the entry_params in the standard order - if entry_params - .iter() - .any(|ti| ti.debug_name == Some("Poseidon".into())) - { - builtins.push(BuiltinName::poseidon); - builtin_offset.insert(PoseidonType::ID, current_offset); - current_offset += 1; - } - if entry_params - .iter() - .any(|ti| ti.debug_name == Some("EcOp".into())) - { - builtins.push(BuiltinName::ec_op); - builtin_offset.insert(EcOpType::ID, current_offset); - current_offset += 1 - } - if entry_params - .iter() - .any(|ti| ti.debug_name == Some("Bitwise".into())) - { - builtins.push(BuiltinName::bitwise); - builtin_offset.insert(BitwiseType::ID, current_offset); - current_offset += 1; - } - if entry_params - .iter() - .any(|ti| ti.debug_name == Some("RangeCheck".into())) - { - builtins.push(BuiltinName::range_check); - builtin_offset.insert(RangeCheckType::ID, current_offset); - current_offset += 1; - } - if entry_params - .iter() - .any(|ti| ti.debug_name == Some("Pedersen".into())) - { - builtins.push(BuiltinName::pedersen); - builtin_offset.insert(PedersenType::ID, current_offset); - current_offset += 1; + for (debug_name, builtin_name, sierra_id) in [ + ("Poseidon", BuiltinName::poseidon, PoseidonType::ID), + ("EcOp", BuiltinName::ec_op, EcOpType::ID), + ("Bitwise", BuiltinName::bitwise, BitwiseType::ID), + ("RangeCheck", BuiltinName::range_check, RangeCheckType::ID), + ("Pedersen", BuiltinName::pedersen, PedersenType::ID), + ] { + if params + .iter() + .any(|id| id.debug_name.as_deref() == Some(debug_name)) + { + builtins.push(builtin_name); + builtin_offset.insert(sierra_id, current_offset); + current_offset += 1; + } } // Force an output builtin so that we can write the program output into it's segment - if proof_mode { + if append_output { builtins.push(BuiltinName::output); - builtin_offset.insert(OutputType::ID, current_offset); } builtins.reverse(); (builtins, builtin_offset) @@ -597,8 +647,21 @@ fn fetch_return_values( return_type_size: i16, return_type_id: &ConcreteTypeId, vm: &VirtualMachine, + builtin_count: i16, + fetch_from_output: bool, ) -> Result, Error> { - let mut return_values = vm.get_return_values(return_type_size as usize)?; + let mut return_values = if fetch_from_output { + let output_builtin_end = vm + .get_relocatable((vm.get_ap() + (-builtin_count as i32)).unwrap()) + .unwrap(); + let output_builtin_base = (output_builtin_end + (-return_type_size as i32)).unwrap(); + vm.get_continuous_range(output_builtin_base, return_type_size.into_or_panic())? + } else { + vm.get_continuous_range( + (vm.get_ap() - (return_type_size + builtin_count) as usize).unwrap(), + return_type_size as usize, + )? + }; // Check if this result is a Panic result if return_type_id .debug_name @@ -639,10 +702,10 @@ fn fetch_return_values( // Calculates builtins' final_stack setting each stop_ptr // Calling this function is a must if either air_public_input or cairo_pie are needed fn finalize_builtins( - proof_mode: bool, main_ret_types: &[ConcreteTypeId], type_sizes: &UnorderedHashMap, vm: &mut VirtualMachine, + builtin_count: i16, ) -> Result<(), Error> { // Set stop pointers for builtins so we can obtain the air public input // Cairo 1 programs have other return values aside from the used builtin's final pointers, so we need to hand-pick them @@ -652,8 +715,9 @@ fn finalize_builtins( let ret_types_and_sizes = main_ret_types.iter().zip(ret_types_sizes.clone()); let full_ret_types_size: i16 = ret_types_sizes.sum(); - let mut stack_pointer = (vm.get_ap() - (full_ret_types_size as usize).saturating_sub(1)) - .map_err(VirtualMachineError::Math)?; + let mut stack_pointer = (vm.get_ap() + - (full_ret_types_size as usize + builtin_count as usize).saturating_sub(1)) + .map_err(VirtualMachineError::Math)?; // Calculate the stack_ptr for each return builtin in the return values let mut builtin_name_to_stack_pointer = HashMap::new(); @@ -678,10 +742,375 @@ fn finalize_builtins( } // Set stop pointer for each builtin - vm.builtins_final_stack_from_stack_pointer_dict(&builtin_name_to_stack_pointer, proof_mode)?; + vm.builtins_final_stack_from_stack_pointer_dict(&builtin_name_to_stack_pointer, false)?; Ok(()) } +fn serialize_output( + return_values: &[MaybeRelocatable], + vm: &mut VirtualMachine, + return_type_id: &ConcreteTypeId, + sierra_program_registry: &ProgramRegistry, + type_sizes: &UnorderedHashMap, +) -> String { + let mut output_string = String::new(); + let mut return_values_iter = return_values.iter().peekable(); + serialize_output_inner( + &mut return_values_iter, + &mut output_string, + vm, + return_type_id, + sierra_program_registry, + type_sizes, + ); + output_string +} + +fn serialize_output_inner<'a>( + return_values_iter: &mut Peekable>, + output_string: &mut String, + vm: &mut VirtualMachine, + return_type_id: &ConcreteTypeId, + sierra_program_registry: &ProgramRegistry, + type_sizes: &UnorderedHashMap, +) { + match sierra_program_registry.get_type(return_type_id).unwrap() { + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Array(info) => { + // Fetch array from memory + let array_start = return_values_iter + .next() + .expect("Missing return value") + .get_relocatable() + .expect("Array start_ptr not Relocatable"); + // Arrays can come in two formats: either [start_ptr, end_ptr] or [end_ptr], with the start_ptr being implicit (base of the end_ptr's segment) + let (array_start, array_size ) = match return_values_iter.peek().and_then(|mr| mr.get_relocatable()) { + Some(array_end) if array_end.segment_index == array_start.segment_index && array_end.offset >= array_start.offset => { + // Pop the value we just peeked + return_values_iter.next(); + (array_start, (array_end - array_start).unwrap()) + } + _ => ((array_start.segment_index, 0).into(), array_start.offset), + }; + let array_data = vm.get_continuous_range(array_start, array_size).unwrap(); + let mut array_data_iter = array_data.iter().peekable(); + let array_elem_id = &info.ty; + // Serialize array data + maybe_add_whitespace(output_string); + output_string.push('['); + while array_data_iter.peek().is_some() { + serialize_output_inner( + &mut array_data_iter, + output_string, + vm, + array_elem_id, + sierra_program_registry, + type_sizes, + ) + } + output_string.push(']'); + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Box(info) => { + // As this represents a pointer, we need to extract it's values + let ptr = return_values_iter + .next() + .expect("Missing return value") + .get_relocatable() + .expect("Box Pointer is not Relocatable"); + let type_size = type_sizes.type_size(&info.ty); + let data = vm + .get_continuous_range(ptr, type_size) + .expect("Failed to extract value from nullable ptr"); + let mut data_iter = data.iter().peekable(); + serialize_output_inner( + &mut data_iter, + output_string, + vm, + &info.ty, + sierra_program_registry, + type_sizes, + ) + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Const(_) => { + unimplemented!("Not supported in the current version") + }, + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Felt252(_) + // Only unsigned integer values implement Into + | cairo_lang_sierra::extensions::core::CoreTypeConcrete::Bytes31(_) + | cairo_lang_sierra::extensions::core::CoreTypeConcrete::Uint8(_) + | cairo_lang_sierra::extensions::core::CoreTypeConcrete::Uint16(_) + | cairo_lang_sierra::extensions::core::CoreTypeConcrete::Uint32(_) + | cairo_lang_sierra::extensions::core::CoreTypeConcrete::Uint64(_) + | cairo_lang_sierra::extensions::core::CoreTypeConcrete::Uint128(_) => { + maybe_add_whitespace(output_string); + let val = return_values_iter + .next() + .expect("Missing return value") + .get_int() + .expect("Value is not an integer"); + output_string.push_str(&val.to_string()); + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Sint8(_) + | cairo_lang_sierra::extensions::core::CoreTypeConcrete::Sint16(_) + | cairo_lang_sierra::extensions::core::CoreTypeConcrete::Sint32(_) + | cairo_lang_sierra::extensions::core::CoreTypeConcrete::Sint64(_) + | cairo_lang_sierra::extensions::core::CoreTypeConcrete::Sint128(_) => { + maybe_add_whitespace(output_string); + let val = return_values_iter + .next() + .expect("Missing return value") + .get_int() + .expect("Value is not an integer"); + output_string.push_str(&signed_felt(val).to_string()); + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::NonZero(info) => { + serialize_output_inner( + return_values_iter, + output_string, + vm, + &info.ty, + sierra_program_registry, + type_sizes, + ) + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Nullable(info) => { + // As this represents a pointer, we need to extract it's values + let ptr = match return_values_iter.next().expect("Missing return value") { + MaybeRelocatable::RelocatableValue(ptr) => *ptr, + MaybeRelocatable::Int(felt) if felt.is_zero() => { + // Nullable is Null + maybe_add_whitespace(output_string); + output_string.push_str("null"); + return; + } + _ => panic!("Invalid Nullable"), + }; + let type_size = type_sizes.type_size(&info.ty); + let data = vm + .get_continuous_range(ptr, type_size) + .expect("Failed to extract value from nullable ptr"); + let mut data_iter = data.iter().peekable(); + serialize_output_inner( + &mut data_iter, + output_string, + vm, + &info.ty, + sierra_program_registry, + type_sizes, + ) + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Enum(info) => { + // First we check if it is a Panic enum, as we already handled panics when fetching return values, + // we can ignore them and move on to the non-panic variant + if let GenericArg::UserType(user_type) = &info.info.long_id.generic_args[0] { + if user_type + .debug_name + .as_ref() + .is_some_and(|n| n.starts_with("core::panics::PanicResult")) + { + return serialize_output_inner( + return_values_iter, + output_string, + vm, + &info.variants[0], + sierra_program_registry, + type_sizes, + ); + } + } + let num_variants = &info.variants.len(); + let casm_variant_idx: usize = return_values_iter + .next() + .expect("Missing return value") + .get_int() + .expect("Enum tag is not integer") + .to_usize() + .expect("Invalid enum tag"); + // Convert casm variant idx to sierra variant idx + let variant_idx = if *num_variants > 2 { + num_variants - 1 - (casm_variant_idx >> 1) + } else { + casm_variant_idx + }; + let variant_type_id = &info.variants[variant_idx]; + + // Handle core::bool separately + if let GenericArg::UserType(user_type) = &info.info.long_id.generic_args[0] { + if user_type + .debug_name + .as_ref() + .is_some_and(|n| n == "core::bool") + { + // Sanity checks + assert!( + *num_variants == 2 + && variant_idx < 2 + && type_sizes + .get(&info.variants[0]) + .is_some_and(|size| size.is_zero()) + && type_sizes + .get(&info.variants[1]) + .is_some_and(|size| size.is_zero()), + "Malformed bool enum" + ); + + let boolean_string = match variant_idx { + 0 => "false", + _ => "true", + }; + maybe_add_whitespace(output_string); + output_string.push_str(boolean_string); + return; + } + } + // TODO: Something similar to the bool handling could be done for unit enum variants if we could get the type info with the variant names + + // Space is always allocated for the largest enum member, padding with zeros in front for the smaller variants + let mut max_variant_size = 0; + for variant in &info.variants { + let variant_size = type_sizes.get(variant).unwrap(); + max_variant_size = std::cmp::max(max_variant_size, *variant_size) + } + for _ in 0..max_variant_size - type_sizes.get(variant_type_id).unwrap() { + // Remove padding + assert_eq!( + return_values_iter.next(), + Some(&MaybeRelocatable::from(0)), + "Malformed enum" + ); + } + serialize_output_inner( + return_values_iter, + output_string, + vm, + variant_type_id, + sierra_program_registry, + type_sizes, + ) + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Struct(info) => { + for member_type_id in &info.members { + serialize_output_inner( + return_values_iter, + output_string, + vm, + member_type_id, + sierra_program_registry, + type_sizes, + ) + } + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Felt252Dict(info) => { + // Process Dictionary + let dict_ptr = return_values_iter + .next() + .expect("Missing return val") + .get_relocatable() + .expect("Dict Ptr not Relocatable"); + if !(dict_ptr.offset + == vm + .get_segment_size(dict_ptr.segment_index as usize) + .unwrap_or_default() + && dict_ptr.offset % 3 == 0) + { + panic!("Return value is not a valid Felt252Dict") + } + // Fetch dictionary values type id + let value_type_id = &info.ty; + // Fetch the dictionary's memory + let dict_mem = vm + .get_continuous_range((dict_ptr.segment_index, 0).into(), dict_ptr.offset) + .expect("Malformed dictionary memory"); + // Serialize the dictionary + output_string.push('{'); + // The dictionary's memory is made up of (key, prev_value, next_value) tuples + // The prev value is not relevant to the user so we can skip over it for calrity + for (key, _, value) in dict_mem.iter().tuples() { + maybe_add_whitespace(output_string); + // Serialize the key wich should always be a Felt value + output_string.push_str(&key.to_string()); + output_string.push(':'); + // Serialize the value + // We create a peekable array here in order to use the serialize_output_inner as the value could be a span + let value_vec = vec![value.clone()]; + let mut value_iter = value_vec.iter().peekable(); + serialize_output_inner( + &mut value_iter, + output_string, + vm, + value_type_id, + sierra_program_registry, + type_sizes, + ); + } + output_string.push('}'); + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::SquashedFelt252Dict(info) => { + // Process Dictionary + let dict_start = return_values_iter + .next() + .expect("Missing return val") + .get_relocatable() + .expect("Squashed dict_start ptr not Relocatable"); + let dict_end = return_values_iter + .next() + .expect("Missing return val") + .get_relocatable() + .expect("Squashed dict_end ptr not Relocatable"); + let dict_size = (dict_end - dict_start).unwrap(); + if dict_size % 3 != 0 { + panic!("Return value is not a valid SquashedFelt252Dict") + } + // Fetch dictionary values type id + let value_type_id = &info.ty; + // Fetch the dictionary's memory + let dict_mem = vm + .get_continuous_range(dict_start, dict_size) + .expect("Malformed squashed dictionary memory"); + // Serialize the dictionary + output_string.push('{'); + // The dictionary's memory is made up of (key, prev_value, next_value) tuples + // The prev value is not relevant to the user so we can skip over it for calrity + for (key, _, value) in dict_mem.iter().tuples() { + maybe_add_whitespace(output_string); + // Serialize the key wich should always be a Felt value + output_string.push_str(&key.to_string()); + output_string.push(':'); + // Serialize the value + // We create a peekable array here in order to use the serialize_output_inner as the value could be a span + let value_vec = vec![value.clone()]; + let mut value_iter = value_vec.iter().peekable(); + serialize_output_inner( + &mut value_iter, + output_string, + vm, + value_type_id, + sierra_program_registry, + type_sizes, + ); + } + output_string.push('}'); + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Span(_) => unimplemented!("Span types get resolved to Array in the current version"), + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Snapshot(info) => { + serialize_output_inner( + return_values_iter, + output_string, + vm, + &info.ty, + sierra_program_registry, + type_sizes, + ) + } + _ => panic!("Unexpected return type") + } +} + +fn maybe_add_whitespace(string: &mut String) { + if !string.is_empty() && !string.ends_with('[') && !string.ends_with('{') { + string.push(' '); + } +} + #[cfg(test)] mod tests { use std::path::Path; @@ -732,17 +1161,23 @@ mod tests { #[case("../cairo_programs/cairo-1-programs/simple_struct.cairo")] #[case("../cairo_programs/cairo-1-programs/simple.cairo")] #[case("../cairo_programs/cairo-1-programs/struct_span_return.cairo")] - fn check_append_ret_values_to_output_segment(#[case] filename: &str) { + fn check_append_ret_values_to_output_segment( + #[case] filename: &str, + #[values(true, false)] proof_mode: bool, + ) { // Compile to sierra let sierra_program = compile_to_sierra(filename); // Set proof_mode let cairo_run_config = Cairo1RunConfig { - proof_mode: true, + proof_mode, layout: "all_cairo", + append_return_values: !proof_mode, // This is so we can test appending return values when not running in proof_mode + finalize_builtins: true, ..Default::default() }; // Run program - let (_, vm, return_values) = cairo_run_program(&sierra_program, cairo_run_config).unwrap(); + let (runner, vm, return_values, _) = + cairo_run_program(&sierra_program, cairo_run_config).unwrap(); // When the return type is a PanicResult, we remove the panic wrapper when returning the ret values // And handle the panics returning an error, so we need to add it here let return_values = if main_hash_panic_result(&sierra_program) { @@ -762,5 +1197,10 @@ mod tests { assert!(vm .get_maybe(&Relocatable::from((2_isize, return_values.len()))) .is_none()); + + // Check that cairo_pie can be outputted when not running in proof_mode + if !proof_mode { + assert!(runner.get_cairo_pie(&vm).is_ok()) + } } } diff --git a/cairo1-run/src/main.rs b/cairo1-run/src/main.rs index c970d113cb..8e2a99e021 100644 --- a/cairo1-run/src/main.rs +++ b/cairo1-run/src/main.rs @@ -15,7 +15,6 @@ use cairo_vm::{ }; use clap::{Parser, ValueHint}; use itertools::Itertools; -use serialize_output::serialize_output; use std::{ io::{self, Write}, path::PathBuf, @@ -23,7 +22,6 @@ use std::{ use thiserror::Error; pub mod cairo_run; -pub mod serialize_output; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -58,6 +56,13 @@ struct Args { args: FuncArgs, #[clap(long = "print_output", value_parser)] print_output: bool, + #[clap( + long = "append_return_values", + // We need to add these air_private_input & air_public_input or else + // passing cairo_pie_output + either of these without proof_mode will not fail + conflicts_with_all = ["proof_mode", "air_private_input", "air_public_input"] + )] + append_return_values: bool, } #[derive(Debug, Clone)] @@ -109,6 +114,7 @@ fn validate_layout(value: &str) -> Result { "plain" | "small" | "dex" + | "recursive" | "starknet" | "starknet_with_keccak" | "recursive_large_output" @@ -206,11 +212,13 @@ fn run(args: impl Iterator) -> Result, Error> { let cairo_run_config = Cairo1RunConfig { proof_mode: args.proof_mode, + serialize_output: args.print_output, relocate_mem: args.memory_file.is_some() || args.air_public_input.is_some(), layout: &args.layout, trace_enabled: args.trace_file.is_some() || args.air_public_input.is_some(), args: &args.args.0, finalize_builtins: args.air_private_input.is_some() || args.cairo_pie_output.is_some(), + append_return_values: args.append_return_values, }; let compiler_config = CompilerConfig { @@ -221,15 +229,9 @@ fn run(args: impl Iterator) -> Result, Error> { let sierra_program = compile_cairo_project_at_path(&args.filename, compiler_config) .map_err(|err| Error::SierraCompilation(err.to_string()))?; - let (runner, vm, return_values) = + let (runner, vm, _, serialized_output) = cairo_run::cairo_run_program(&sierra_program, cairo_run_config)?; - let output_string = if args.print_output { - Some(serialize_output(&vm, &return_values)) - } else { - None - }; - if let Some(file_path) = args.air_public_input { let json = runner.get_air_public_input(&vm)?.serialize_json()?; std::fs::write(file_path, json)?; @@ -286,7 +288,7 @@ fn run(args: impl Iterator) -> Result, Error> { memory_writer.flush()?; } - Ok(output_string) + Ok(serialized_output) } fn main() -> Result<(), Error> { @@ -294,7 +296,7 @@ fn main() -> Result<(), Error> { Err(Error::Cli(err)) => err.exit(), Ok(output) => { if let Some(output_string) = output { - println!("{}", output_string); + println!("Program Output : {}", output_string); } Ok(()) } @@ -371,7 +373,7 @@ mod tests { #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/hello.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--proof_mode", "--air_public_input", "/dev/null", "--air_private_input", "/dev/null"].as_slice())] fn test_run_hello_ok(#[case] args: &[&str]) { let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(Some(res)) if res == "1 1234"); + assert_matches!(run(args), Ok(Some(res)) if res == "1234"); } #[rstest] @@ -435,7 +437,7 @@ mod tests { #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/simple.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--proof_mode", "--air_public_input", "/dev/null", "--air_private_input", "/dev/null"].as_slice())] fn test_run_simple_ok(#[case] args: &[&str]) { let args = args.iter().cloned().map(String::from); - assert_matches!(run(args), Ok(Some(res)) if res == "1"); + assert_matches!(run(args), Ok(Some(res)) if res == "true"); } #[rstest] @@ -523,7 +525,7 @@ mod tests { #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/felt_dict.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--proof_mode", "--air_public_input", "/dev/null", "--air_private_input", "/dev/null"].as_slice())] fn test_run_felt_dict(#[case] args: &[&str]) { let args = args.iter().cloned().map(String::from); - let expected_output = "{\n\t0x10473: [0x8,0x9,0xa,0xb,],\n\t0x10474: [0x1,0x2,0x3,],\n}\n"; + let expected_output = "{66675: [8 9 10 11] 66676: [1 2 3]}"; assert_matches!(run(args), Ok(Some(res)) if res == expected_output); } @@ -532,7 +534,70 @@ mod tests { #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/felt_span.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--proof_mode", "--air_public_input", "/dev/null", "--air_private_input", "/dev/null"].as_slice())] fn test_run_felt_span(#[case] args: &[&str]) { let args = args.iter().cloned().map(String::from); - let expected_output = "[0x8,0x9,0xa,0xb,]"; + let expected_output = "[8 9 10 11]"; + assert_matches!(run(args), Ok(Some(res)) if res == expected_output); + } + + #[rstest] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/array_integer_tuple.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--cairo_pie_output", "/dev/null"].as_slice())] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/array_integer_tuple.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--proof_mode", "--air_public_input", "/dev/null", "--air_private_input", "/dev/null"].as_slice())] + fn test_run_array_integer_tuple(#[case] args: &[&str]) { + let args = args.iter().cloned().map(String::from); + let expected_output = "[1] 1"; + assert_matches!(run(args), Ok(Some(res)) if res == expected_output); + } + + #[rstest] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/nullable_box_vec.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--cairo_pie_output", "/dev/null"].as_slice())] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/nullable_box_vec.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--proof_mode", "--air_public_input", "/dev/null", "--air_private_input", "/dev/null"].as_slice())] + fn test_run_nullable_box_vec(#[case] args: &[&str]) { + let args = args.iter().cloned().map(String::from); + let expected_output = "{0: 10 1: 20 2: 30} 3"; + assert_matches!(run(args), Ok(Some(res)) if res == expected_output); + } + + #[rstest] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/dict_with_struct.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--cairo_pie_output", "/dev/null"].as_slice())] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/dict_with_struct.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--proof_mode", "--air_public_input", "/dev/null", "--air_private_input", "/dev/null"].as_slice())] + fn test_run_dict_with_struct(#[case] args: &[&str]) { + let args = args.iter().cloned().map(String::from); + let expected_output = "{0: 1 true 1: 1 false 2: 1 true}"; + assert_matches!(run(args), Ok(Some(res)) if res == expected_output); + } + + #[rstest] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/felt_dict_squash.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--cairo_pie_output", "/dev/null"].as_slice())] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/felt_dict_squash.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--proof_mode", "--air_public_input", "/dev/null", "--air_private_input", "/dev/null"].as_slice())] + fn test_run_felt_dict_squash(#[case] args: &[&str]) { + let args = args.iter().cloned().map(String::from); + let expected_output = "{66675: [4 5 6] 66676: [1 2 3]}"; + assert_matches!(run(args), Ok(Some(res)) if res == expected_output); + } + + #[rstest] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/null_ret.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--cairo_pie_output", "/dev/null"].as_slice())] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/null_ret.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--proof_mode", "--air_public_input", "/dev/null", "--air_private_input", "/dev/null"].as_slice())] + fn test_run_null_ret(#[case] args: &[&str]) { + let args = args.iter().cloned().map(String::from); + let expected_output = "null"; + assert_matches!(run(args), Ok(Some(res)) if res == expected_output); + } + + #[rstest] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/bytes31_ret.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--cairo_pie_output", "/dev/null"].as_slice())] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/bytes31_ret.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--proof_mode", "--air_public_input", "/dev/null", "--air_private_input", "/dev/null"].as_slice())] + fn test_run_bytes31_ret(#[case] args: &[&str]) { + let args = args.iter().cloned().map(String::from); + let expected_output = "123"; + assert_matches!(run(args), Ok(Some(res)) if res == expected_output); + } + + #[rstest] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/tensor_new.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--cairo_pie_output", "/dev/null"].as_slice())] + #[case(["cairo1-run", "../cairo_programs/cairo-1-programs/tensor_new.cairo", "--print_output", "--trace_file", "/dev/null", "--memory_file", "/dev/null", "--layout", "all_cairo", "--proof_mode", "--air_public_input", "/dev/null", "--air_private_input", "/dev/null"].as_slice())] + fn test_run_tensor_new(#[case] args: &[&str]) { + let args = args.iter().cloned().map(String::from); + let expected_output = "[1 2] [1 false 1 true]"; assert_matches!(run(args), Ok(Some(res)) if res == expected_output); } } diff --git a/cairo1-run/src/serialize_output.rs b/cairo1-run/src/serialize_output.rs deleted file mode 100644 index 562f2dc832..0000000000 --- a/cairo1-run/src/serialize_output.rs +++ /dev/null @@ -1,169 +0,0 @@ -use cairo_vm::{ - types::relocatable::{MaybeRelocatable, Relocatable}, - vm::{errors::memory_errors::MemoryError, vm_core::VirtualMachine}, - Felt252, -}; -use itertools::Itertools; -use std::{collections::HashMap, iter::Peekable, slice::Iter}; -use thiserror::Error; - -#[derive(Debug)] -pub(crate) enum Output { - Felt(Felt252), - FeltSpan(Vec), - FeltDict(HashMap), -} - -#[derive(Debug, Error)] -pub struct FormatError; - -impl std::fmt::Display for FormatError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Format error") - } -} - -impl Output { - pub fn from_memory( - vm: &VirtualMachine, - relocatable: &Relocatable, - ) -> Result { - match vm.get_relocatable(*relocatable) { - Ok(relocatable_value) => { - let segment_size = vm - .get_segment_size(relocatable_value.segment_index as usize) - .ok_or(FormatError)?; - let segment_data = vm - .get_continuous_range(relocatable_value, segment_size) - .map_err(|_| FormatError)?; - - // check if the segment data is a valid array of felts - if segment_data - .iter() - .all(|v| matches!(v, MaybeRelocatable::Int(_))) - { - let span_segment: Vec = segment_data - .iter() - .map(|v| Output::Felt(v.get_int().unwrap())) - .collect(); - Ok(Output::FeltSpan(span_segment)) - } else { - Err(FormatError) - } - } - Err(MemoryError::UnknownMemoryCell(relocatable_value)) => { - // here we assume that the value is a dictionary - let mut felt252dict: HashMap = HashMap::new(); - - let segment_size = vm - .get_segment_size(relocatable_value.segment_index as usize) - .ok_or(FormatError)?; - let mut segment_start = relocatable_value.clone(); - segment_start.offset = 0; - let segment_data = vm - .get_continuous_range(*segment_start, segment_size) - .map_err(|_| FormatError)?; - - for (dict_key, _, value_relocatable) in segment_data.iter().tuples() { - let key = dict_key.get_int().ok_or(FormatError)?; - let value_segment = value_relocatable.get_relocatable().ok_or(FormatError)?; - let value = Output::from_memory(vm, &value_segment)?; - felt252dict.insert(key, value); - } - Ok(Output::FeltDict(felt252dict)) - } - _ => Err(FormatError), - } - } -} - -impl std::fmt::Display for Output { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Output::Felt(felt) => write!(f, "{}", felt.to_hex_string()), - Output::FeltSpan(span) => { - write!(f, "[")?; - for elem in span { - write!(f, "{}", elem)?; - write!(f, ",")?; - } - write!(f, "]")?; - Ok(()) - } - Output::FeltDict(felt_dict) => { - let mut keys: Vec<_> = felt_dict.keys().collect(); - keys.sort(); - writeln!(f, "{{")?; - for key in keys { - writeln!(f, "\t{}: {},", key.to_hex_string(), felt_dict[key])?; - } - writeln!(f, "}}")?; - Ok(()) - } - } - } -} - -pub(crate) fn serialize_output(vm: &VirtualMachine, return_values: &[MaybeRelocatable]) -> String { - let mut output_string = String::new(); - let mut return_values_iter: Peekable> = return_values.iter().peekable(); - let result = serialize_output_inner(&mut return_values_iter, &mut output_string, vm); - if result.is_err() { - return result.err().unwrap().to_string(); - } - - output_string -} - -fn maybe_add_whitespace(string: &mut String) { - if !string.is_empty() && !string.ends_with('[') { - string.push(' '); - } -} - -fn serialize_output_inner( - iter: &mut Peekable>, - output_string: &mut String, - vm: &VirtualMachine, -) -> Result<(), FormatError> { - while let Some(val) = iter.next() { - match val { - MaybeRelocatable::Int(x) => { - maybe_add_whitespace(output_string); - output_string.push_str(&x.to_string()); - continue; - } - MaybeRelocatable::RelocatableValue(x) if ((iter.len() + 1) % 2) == 0 /* felt array */ => { - // Check if the next value is a relocatable of the same index - let y = iter.next().unwrap().get_relocatable().ok_or(FormatError)?; - // Check if the two relocatable values represent a valid array in memory - if x.segment_index == y.segment_index && x.offset <= y.offset { - // Fetch array - maybe_add_whitespace(output_string); - output_string.push('['); - let array = vm.get_continuous_range(*x, y.offset - x.offset).map_err(|_| FormatError)?; - let mut array_iter: Peekable> = - array.iter().peekable(); - serialize_output_inner(&mut array_iter, output_string, vm)?; - output_string.push(']'); - continue; - } - }, - MaybeRelocatable::RelocatableValue(x) if iter.len() > 1 => { - let mut segment_start = *x; - segment_start.offset = 0; - for elem in iter.into_iter() { - let output_value = Output::from_memory(vm, &elem.get_relocatable().ok_or(FormatError)?)?; - output_string.push_str(output_value.to_string().as_str()) - } - } - MaybeRelocatable::RelocatableValue(x) => { - match Output::from_memory(vm, x) { - Ok(output_value) => output_string.push_str(format!("{}", output_value).as_str()), - Err(_) => output_string.push_str("The output could not be formatted"), - } - } - } - } - Ok(()) -} diff --git a/cairo_programs/cairo-1-programs/array_integer_tuple.cairo b/cairo_programs/cairo-1-programs/array_integer_tuple.cairo new file mode 100644 index 0000000000..cb018f3a4a --- /dev/null +++ b/cairo_programs/cairo-1-programs/array_integer_tuple.cairo @@ -0,0 +1,9 @@ +use core::array::ArrayTrait; + + +fn main() -> (Array, u32) { + let mut numbers = ArrayTrait::new(); + numbers.append(1); + + (numbers, 1) +} diff --git a/cairo_programs/cairo-1-programs/bytes31_ret.cairo b/cairo_programs/cairo-1-programs/bytes31_ret.cairo new file mode 100644 index 0000000000..3a67f9cbf5 --- /dev/null +++ b/cairo_programs/cairo-1-programs/bytes31_ret.cairo @@ -0,0 +1,5 @@ +fn main() -> bytes31 { + let a: u128 = 123; + let b: bytes31 = a.into(); + b +} diff --git a/cairo_programs/cairo-1-programs/dict_with_struct.cairo b/cairo_programs/cairo-1-programs/dict_with_struct.cairo new file mode 100644 index 0000000000..e24df874c2 --- /dev/null +++ b/cairo_programs/cairo-1-programs/dict_with_struct.cairo @@ -0,0 +1,24 @@ +use core::nullable::{nullable_from_box, match_nullable, FromNullableResult}; + + +#[derive(Drop, Copy)] +struct FP16x16 { + mag: u32, + sign: bool +} + +fn main() -> Felt252Dict> { + // Create the dictionary + let mut d: Felt252Dict> = Default::default(); + + let box_a = BoxTrait::new(FP16x16 { mag: 1, sign: false }); + let box_b = BoxTrait::new(FP16x16 { mag: 1, sign: true }); + let box_c = BoxTrait::new(FP16x16 { mag: 1, sign: true }); + + // Insert it as a `Span` + d.insert(0, nullable_from_box(box_c)); + d.insert(1, nullable_from_box(box_a)); + d.insert(2, nullable_from_box(box_b)); + + d +} diff --git a/cairo_programs/cairo-1-programs/felt_dict_squash.cairo b/cairo_programs/cairo-1-programs/felt_dict_squash.cairo new file mode 100644 index 0000000000..7f1e284e32 --- /dev/null +++ b/cairo_programs/cairo-1-programs/felt_dict_squash.cairo @@ -0,0 +1,18 @@ +use core::nullable::{nullable_from_box, match_nullable, FromNullableResult}; +use core::dict::Felt252DictEntry; + +fn main() -> SquashedFelt252Dict>> { + // Create the dictionary + let mut d: Felt252Dict>> = Default::default(); + + // Create the array to insert + let a = array![8, 9, 10, 11]; + let b = array![1, 2, 3]; + let c = array![4, 5, 6]; + + // Insert it as a `Span` + d.insert(66675, nullable_from_box(BoxTrait::new(a.span()))); + d.insert(66676, nullable_from_box(BoxTrait::new(b.span()))); + d.insert(66675, nullable_from_box(BoxTrait::new(c.span()))); + d.squash() +} diff --git a/cairo_programs/cairo-1-programs/null_ret.cairo b/cairo_programs/cairo-1-programs/null_ret.cairo new file mode 100644 index 0000000000..85769a8e15 --- /dev/null +++ b/cairo_programs/cairo-1-programs/null_ret.cairo @@ -0,0 +1,3 @@ +fn main() -> Nullable { + null() +} diff --git a/cairo_programs/cairo-1-programs/nullable_box_vec.cairo b/cairo_programs/cairo-1-programs/nullable_box_vec.cairo new file mode 100644 index 0000000000..37b2b8042a --- /dev/null +++ b/cairo_programs/cairo-1-programs/nullable_box_vec.cairo @@ -0,0 +1,19 @@ +struct NullableVec { + items: Felt252Dict>>, + len: usize, +} + +fn main() -> NullableVec { + let mut d: Felt252Dict>> = Default::default(); + + // Populate the dictionary + d.insert(0, nullable_from_box(BoxTrait::new(BoxTrait::new(10)))); + d.insert(1, nullable_from_box(BoxTrait::new(BoxTrait::new(20)))); + d.insert(2, nullable_from_box(BoxTrait::new(BoxTrait::new(30)))); + + // Return NullableVec + NullableVec { + items: d, + len: 3, + } +} diff --git a/cairo_programs/cairo-1-programs/tensor_new.cairo b/cairo_programs/cairo-1-programs/tensor_new.cairo new file mode 100644 index 0000000000..2af717beed --- /dev/null +++ b/cairo_programs/cairo-1-programs/tensor_new.cairo @@ -0,0 +1,65 @@ +// FP16x16 +#[derive(Serde, Copy, Drop)] +struct FP16x16 { + mag: u32, + sign: bool +} + +trait FixedTrait { + fn new(mag: MAG, sign: bool) -> T; +} + +impl FP16x16Impl of FixedTrait { + fn new(mag: u32, sign: bool) -> FP16x16 { + FP16x16 { mag: mag, sign: sign } + } +} + +//Tensor +#[derive(Copy, Drop)] +struct Tensor { + shape: Span, + data: Span, +} + +trait TensorTrait { + fn new(shape: Span, data: Span) -> Tensor; +} + +impl FP16x16Tensor of TensorTrait { + fn new(shape: Span, data: Span) -> Tensor { + new_tensor(shape, data) + } +} + +fn new_tensor(shape: Span, data: Span) -> Tensor { + check_shape::(shape, data); + Tensor:: { shape, data } +} + +fn check_shape(shape: Span, data: Span) { + assert(len_from_shape(shape) == data.len(), 'wrong tensor shape'); +} + +fn len_from_shape(mut shape: Span) -> usize { + let mut result: usize = 1; + + loop { + match shape.pop_front() { + Option::Some(item) => { result *= *item; }, + Option::None => { break; } + }; + }; + + result +} + +fn main() -> Tensor { + TensorTrait::new( + array![1, 2].span(), + array![ + FixedTrait::new(1, false), + FixedTrait::new(1, true) + ].span() + ) +} diff --git a/cairo_programs/mod_builtin_feature/apply_poly.cairo b/cairo_programs/mod_builtin_feature/apply_poly.cairo new file mode 100644 index 0000000000..da42bd84c6 --- /dev/null +++ b/cairo_programs/mod_builtin_feature/apply_poly.cairo @@ -0,0 +1,140 @@ +%builtins range_check range_check96 add_mod mul_mod +// TODO: Import directly from common library once released +from cairo_programs.mod_builtin_feature.common.modulo import ModBuiltin, UInt384, run_mod_p_circuit +// from starkware.cairo.common.modulo import run_mod_p_circuit +// from starkware.cairo.common.cairo_builtins import ModBuiltin, UInt384 +from starkware.cairo.common.registers import get_label_location +from starkware.cairo.common.memcpy import memcpy +from starkware.cairo.common.alloc import alloc + +// Computes the polynomial f(x) = x^8 + 5*x^2 + 1. +func apply_poly{ + range_check_ptr, + range_check96_ptr: felt*, + add_mod_ptr: ModBuiltin*, + mul_mod_ptr: ModBuiltin* +}(x: UInt384*, p: UInt384) -> (res: UInt384*) { + + // Copy inputs and constants into the values_ptr segment. + memcpy(dst=range_check96_ptr, src=x, len=UInt384.SIZE); + let (constants_ptr) = get_label_location(constants); + memcpy(dst=range_check96_ptr + UInt384.SIZE, src=constants_ptr, len=2 * UInt384.SIZE); + let values_ptr = cast(range_check96_ptr, UInt384*); + let range_check96_ptr = range_check96_ptr + 36; + + + let (add_mod_offsets_ptr) = get_label_location(add_offsets); + let (mul_mod_offsets_ptr) = get_label_location(mul_offsets); + run_mod_p_circuit( + p=p, + values_ptr=values_ptr, + add_mod_offsets_ptr=add_mod_offsets_ptr, + add_mod_n=2, + mul_mod_offsets_ptr=mul_mod_offsets_ptr, + mul_mod_n=4, + ); + + return (res=values_ptr + 32); + + // values_ptr points to a segment within the range_check96_ptr segment that looks like this: + // + // offset value + // 0 x + // 4 1 + // 8 5 + // 12 x^2 + // 16 x^4 + // 20 x^8 + // 24 5*x^2 + // 28 x^8 + 5*x^2 + // 32 x^8 + 5*x^2 + 1 + + constants: + dw 1; + dw 0; + dw 0; + dw 0; + + dw 5; + dw 0; + dw 0; + dw 0; + + add_offsets: + dw 20; // x^8 + dw 24; // 5*x^2 + dw 28; // x^8 + 5*x^2 + + dw 4; // 1 + dw 28; // x^8 + 5*x^2 + dw 32; // x^8 + 5*x^2 + 1 + + // Placeholders (copies of the first 3 offsets): + dw 20; + dw 24; + dw 28; + dw 20; + dw 24; + dw 28; + dw 20; + dw 24; + dw 28; + dw 20; + dw 24; + dw 28; + dw 20; + dw 24; + dw 28; + dw 20; + dw 24; + dw 28; + + + mul_offsets: + dw 0; // x + dw 0; // x + dw 12; // x^2 + + dw 12; // x^2 + dw 12; // x^2 + dw 16; // x^4 + + dw 16; // x^4 + dw 16; // x^4 + dw 20; // x^8 + + dw 8; // 5 + dw 12; // x^2 + dw 24; // 5*x^2 + + // Placeholders (copies of the first 3 offsets): + dw 0; + dw 0; + dw 12; + dw 0; + dw 0; + dw 12; + dw 0; + dw 0; + dw 12; + dw 0; + dw 0; + dw 12; +} + +func main{range_check_ptr, range_check96_ptr: felt*, add_mod_ptr: ModBuiltin*, mul_mod_ptr: ModBuiltin*}() { + alloc_locals; + + let p = UInt384(d0=0xffff, d1=0xffff, d2=0xffff, d3=0xffff); + let (local inputs: UInt384*) = alloc(); + assert inputs[0] = UInt384(d0=0xbbbb, d1=0xaaaa, d2=0x6666, d3=0xffff); + + let res: UInt384* = apply_poly(inputs, p); + + assert res[0].d0 = 0xdb0030d69941baf9893cd667; + assert res[0].d1 = 0xfffffffffffffffee43128e7; + assert res[0].d2 = 0xfd4c69cdf6010eab465c3055; + assert res[0].d3 = 0xea52; + + return(); +} diff --git a/cairo_programs/mod_builtin_feature/common/modulo.cairo b/cairo_programs/mod_builtin_feature/common/modulo.cairo new file mode 100644 index 0000000000..b52a85370e --- /dev/null +++ b/cairo_programs/mod_builtin_feature/common/modulo.cairo @@ -0,0 +1,124 @@ +// This file is a copy of common/modulo.cairo + added structs from common/cairo_builtins.cairo so that we can run modulo programs in CI +from starkware.cairo.common.math import safe_div, unsigned_div_rem +from starkware.cairo.common.registers import get_label_location + +// Represents a 384-bit unsigned integer d0 + 2**96 * d1 + 2**192 * d2 + 2**288 * d3 +// where each di is in [0, 2**96). +struct UInt384 { + d0: felt, + d1: felt, + d2: felt, + d3: felt, +} + +// Specifies the Add and Mul Mod builtins memory structure. +struct ModBuiltin { + // The modulus. + p: UInt384, + // A pointer to input values, the intermediate results and the output. + values_ptr: UInt384*, + // A pointer to offsets inside the values array, defining the circuit. + // The offsets array should contain 3 * n elements. + offsets_ptr: felt*, + // The number of operations to perform. + n: felt, +} + +const BATCH_SIZE = 1; + +// Returns the smallest felt 0 <= q < rc_bound such that x <= q * y. +func div_ceil{range_check_ptr}(x: felt, y: felt) -> felt { + let (q, r) = unsigned_div_rem(x, y); + if (r != 0) { + return q + 1; + } else { + return q; + } +} + +// Fills the first instance of the add_mod and mul_mod builtins and calls the fill_memory hint to +// fill the rest of the instances and the missing values in the values table. +// +// This function uses a hardcoded value of batch_size=8, and asserts the instance definitions use +// the same value. +func run_mod_p_circuit_with_large_batch_size{ + range_check_ptr, add_mod_ptr: ModBuiltin*, mul_mod_ptr: ModBuiltin* +}( + p: UInt384, + values_ptr: UInt384*, + add_mod_offsets_ptr: felt*, + add_mod_n: felt, + mul_mod_offsets_ptr: felt*, + mul_mod_n: felt, +) { + const BATCH_SIZE = 8; + let add_mod_n_instances = div_ceil(add_mod_n, BATCH_SIZE); + assert add_mod_ptr[0] = ModBuiltin( + p=p, + values_ptr=values_ptr, + offsets_ptr=add_mod_offsets_ptr, + n=add_mod_n_instances * BATCH_SIZE, + ); + + let mul_mod_n_instances = div_ceil(mul_mod_n, BATCH_SIZE); + assert mul_mod_ptr[0] = ModBuiltin( + p=p, + values_ptr=values_ptr, + offsets_ptr=mul_mod_offsets_ptr, + n=mul_mod_n_instances * BATCH_SIZE, + ); + + %{ + from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner + assert builtin_runners["add_mod_builtin"].instance_def.batch_size == ids.BATCH_SIZE + assert builtin_runners["mul_mod_builtin"].instance_def.batch_size == ids.BATCH_SIZE + + ModBuiltinRunner.fill_memory( + memory=memory, + add_mod=(ids.add_mod_ptr.address_, builtin_runners["add_mod_builtin"], ids.add_mod_n), + mul_mod=(ids.mul_mod_ptr.address_, builtin_runners["mul_mod_builtin"], ids.mul_mod_n), + ) + %} + + let add_mod_ptr = &add_mod_ptr[add_mod_n_instances]; + let mul_mod_ptr = &mul_mod_ptr[mul_mod_n_instances]; + return (); +} + +// Fills the first instance of the add_mod and mul_mod builtins and calls the fill_memory hint to +// fill the rest of the instances and the missing values in the values table. +// +// This function uses a hardcoded value of batch_size=1, and asserts the instance definitions use +// the same value. +func run_mod_p_circuit{add_mod_ptr: ModBuiltin*, mul_mod_ptr: ModBuiltin*}( + p: UInt384, + values_ptr: UInt384*, + add_mod_offsets_ptr: felt*, + add_mod_n: felt, + mul_mod_offsets_ptr: felt*, + mul_mod_n: felt, +) { + assert add_mod_ptr[0] = ModBuiltin( + p=p, values_ptr=values_ptr, offsets_ptr=add_mod_offsets_ptr, n=add_mod_n + ); + + assert mul_mod_ptr[0] = ModBuiltin( + p=p, values_ptr=values_ptr, offsets_ptr=mul_mod_offsets_ptr, n=mul_mod_n + ); + + %{ + from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner + assert builtin_runners["add_mod_builtin"].instance_def.batch_size == 1 + assert builtin_runners["mul_mod_builtin"].instance_def.batch_size == 1 + + ModBuiltinRunner.fill_memory( + memory=memory, + add_mod=(ids.add_mod_ptr.address_, builtin_runners["add_mod_builtin"], ids.add_mod_n), + mul_mod=(ids.mul_mod_ptr.address_, builtin_runners["mul_mod_builtin"], ids.mul_mod_n), + ) + %} + + let add_mod_ptr = &add_mod_ptr[add_mod_n]; + let mul_mod_ptr = &mul_mod_ptr[mul_mod_n]; + return (); +} diff --git a/cairo_programs/mod_builtin_feature/mod_builtin.cairo b/cairo_programs/mod_builtin_feature/mod_builtin.cairo new file mode 100644 index 0000000000..f7ca04517e --- /dev/null +++ b/cairo_programs/mod_builtin_feature/mod_builtin.cairo @@ -0,0 +1,53 @@ +%builtins range_check add_mod mul_mod +// TODO: Import directly from common library once released +from cairo_programs.mod_builtin_feature.common.modulo import ModBuiltin, UInt384, run_mod_p_circuit +// from starkware.common.cairo_builtins import ModBuiltin, UInt384 +// from starkware.cairo.common.modulo import run_mod_p_circuit +from starkware.cairo.common.registers import get_label_location +from starkware.cairo.common.alloc import alloc + +func main{range_check_ptr, add_mod_ptr: ModBuiltin*, mul_mod_ptr: ModBuiltin*}() { + alloc_locals; + + let p = UInt384(d0=1, d1=1, d2=0, d3=0); + let x1 = UInt384(d0=1, d1=0, d2=0, d3=0); + let x2 = UInt384(d0=2, d1=1, d2=0, d3=0); + let x3 = UInt384(d0=2, d1=0, d2=0, d3=0); + let res = UInt384(d0=1, d1=0, d2=0, d3=0); + + let (local values_arr: UInt384*) = alloc(); + assert values_arr[0] = x1; + assert values_arr[1] = x2; + assert values_arr[2] = x3; + assert values_arr[7] = res; + + let (local add_mod_offsets_arr: felt*) = alloc(); + assert add_mod_offsets_arr[0] = 0; // x1 + assert add_mod_offsets_arr[1] = 12; // x2 - x1 + assert add_mod_offsets_arr[2] = 4; // x2 + assert add_mod_offsets_arr[3] = 16; // (x2 - x1) * x3 + assert add_mod_offsets_arr[4] = 20; // x1 * x3 + assert add_mod_offsets_arr[5] = 24; // (x2 - x1) * x3 + x1 * x3 + + let (local mul_mod_offsets_arr: felt*) = alloc(); + assert mul_mod_offsets_arr[0] = 12; // x2 - x1 + assert mul_mod_offsets_arr[1] = 8; // x3 + assert mul_mod_offsets_arr[2] = 16; // (x2 - x1) * x3 + assert mul_mod_offsets_arr[3] = 0; // x1 + assert mul_mod_offsets_arr[4] = 8; // x3 + assert mul_mod_offsets_arr[5] = 20; // x1 * x3 + assert mul_mod_offsets_arr[6] = 8; // x3 + assert mul_mod_offsets_arr[7] = 28; // ((x2 - x1) * x3 + x1 * x3) / x3 = x2 mod p + assert mul_mod_offsets_arr[8] = 24; // (x2 - x1) * x3 + x1 * x3 + + run_mod_p_circuit( + p=p, + values_ptr=values_arr, + add_mod_offsets_ptr=add_mod_offsets_arr, + add_mod_n=2, + mul_mod_offsets_ptr=mul_mod_offsets_arr, + mul_mod_n=3, + ); + + return (); +} diff --git a/cairo_programs/mod_builtin_feature/mod_builtin_failure.cairo b/cairo_programs/mod_builtin_feature/mod_builtin_failure.cairo new file mode 100644 index 0000000000..6e7a9a3175 --- /dev/null +++ b/cairo_programs/mod_builtin_feature/mod_builtin_failure.cairo @@ -0,0 +1,53 @@ +%builtins range_check add_mod mul_mod +// TODO: Import directly from common library once released +from cairo_programs.mod_builtin_feature.common.modulo import ModBuiltin, UInt384, run_mod_p_circuit +// from starkware.common.cairo_builtins import ModBuiltin, UInt384 +// from starkware.cairo.common.modulo import run_mod_p_circuit +from starkware.cairo.common.registers import get_label_location +from starkware.cairo.common.alloc import alloc + +func main{range_check_ptr, add_mod_ptr: ModBuiltin*, mul_mod_ptr: ModBuiltin*}() { + alloc_locals; + + let p = UInt384(d0=1, d1=1, d2=0, d3=0); + let x1 = UInt384(d0=1, d1=0, d2=0, d3=0); + let x2 = UInt384(d0=2, d1=1, d2=0, d3=0); + let x3 = UInt384(d0=2, d1=0, d2=0, d3=0); + let res = UInt384(d0=2, d1=0, d2=0, d3=0); + + let (local values_arr: UInt384*) = alloc(); + assert values_arr[0] = x1; + assert values_arr[1] = x2; + assert values_arr[2] = x3; + assert values_arr[7] = res; + + let (local add_mod_offsets_arr: felt*) = alloc(); + assert add_mod_offsets_arr[0] = 0; // x1 + assert add_mod_offsets_arr[1] = 12; // x2 - x1 + assert add_mod_offsets_arr[2] = 4; // x2 + assert add_mod_offsets_arr[3] = 16; // (x2 - x1) * x3 + assert add_mod_offsets_arr[4] = 20; // x1 * x3 + assert add_mod_offsets_arr[5] = 24; // (x2 - x1) * x3 + x1 * x3 + + let (local mul_mod_offsets_arr: felt*) = alloc(); + assert mul_mod_offsets_arr[0] = 12; // x2 - x1 + assert mul_mod_offsets_arr[1] = 8; // x3 + assert mul_mod_offsets_arr[2] = 16; // (x2 - x1) * x3 + assert mul_mod_offsets_arr[3] = 0; // x1 + assert mul_mod_offsets_arr[4] = 8; // x3 + assert mul_mod_offsets_arr[5] = 20; // x1 * x3 + assert mul_mod_offsets_arr[6] = 8; // x3 + assert mul_mod_offsets_arr[7] = 28; // ((x2 - x1) * x3 + x1 * x3) / x3 = x2 mod p + assert mul_mod_offsets_arr[8] = 24; // (x2 - x1) * x3 + x1 * x3 + + run_mod_p_circuit( + p=p, + values_ptr=values_arr, + add_mod_offsets_ptr=add_mod_offsets_arr, + add_mod_n=2, + mul_mod_offsets_ptr=mul_mod_offsets_arr, + mul_mod_n=3, + ); + + return (); +} diff --git a/cairo_programs/mod_builtin_feature/mod_builtin_large_batch_size.cairo b/cairo_programs/mod_builtin_feature/mod_builtin_large_batch_size.cairo new file mode 100644 index 0000000000..e25670459e --- /dev/null +++ b/cairo_programs/mod_builtin_feature/mod_builtin_large_batch_size.cairo @@ -0,0 +1,53 @@ +%builtins range_check add_mod mul_mod +// TODO: Import directly from common library once released +from cairo_programs.mod_builtin_feature.common.modulo import ModBuiltin, UInt384, run_mod_p_circuit_with_large_batch_size +// from starkware.common.cairo_builtins import ModBuiltin, UInt384 +// from starkware.cairo.common.modulo import run_mod_p_circuit_with_large_batch_size +from starkware.cairo.common.registers import get_label_location +from starkware.cairo.common.alloc import alloc + +func main{range_check_ptr, add_mod_ptr: ModBuiltin*, mul_mod_ptr: ModBuiltin*}() { + alloc_locals; + + let p = UInt384(d0=1, d1=1, d2=0, d3=0); + let x1 = UInt384(d0=1, d1=0, d2=0, d3=0); + let x2 = UInt384(d0=2, d1=1, d2=0, d3=0); + let x3 = UInt384(d0=2, d1=0, d2=0, d3=0); + let res = UInt384(d0=1, d1=0, d2=0, d3=0); + + let (local values_arr: UInt384*) = alloc(); + assert values_arr[0] = x1; + assert values_arr[1] = x2; + assert values_arr[2] = x3; + assert values_arr[7] = res; + + let (local add_mod_offsets_arr: felt*) = alloc(); + assert add_mod_offsets_arr[0] = 0; // x1 + assert add_mod_offsets_arr[1] = 12; // x2 - x1 + assert add_mod_offsets_arr[2] = 4; // x2 + assert add_mod_offsets_arr[3] = 16; // (x2 - x1) * x3 + assert add_mod_offsets_arr[4] = 20; // x1 * x3 + assert add_mod_offsets_arr[5] = 24; // (x2 - x1) * x3 + x1 * x3 + + let (local mul_mod_offsets_arr: felt*) = alloc(); + assert mul_mod_offsets_arr[0] = 12; // x2 - x1 + assert mul_mod_offsets_arr[1] = 8; // x3 + assert mul_mod_offsets_arr[2] = 16; // (x2 - x1) * x3 + assert mul_mod_offsets_arr[3] = 0; // x1 + assert mul_mod_offsets_arr[4] = 8; // x3 + assert mul_mod_offsets_arr[5] = 20; // x1 * x3 + assert mul_mod_offsets_arr[6] = 8; // x3 + assert mul_mod_offsets_arr[7] = 28; // ((x2 - x1) * x3 + x1 * x3) / x3 = x2 mod p + assert mul_mod_offsets_arr[8] = 24; // (x2 - x1) * x3 + x1 * x3 + + run_mod_p_circuit_with_large_batch_size( + p=p, + values_ptr=values_arr, + add_mod_offsets_ptr=add_mod_offsets_arr, + add_mod_n=2, + mul_mod_offsets_ptr=mul_mod_offsets_arr, + mul_mod_n=3, + ); + + return (); +} diff --git a/cairo_programs/mod_builtin_feature/mod_builtin_large_batch_size_benchmark.cairo b/cairo_programs/mod_builtin_feature/mod_builtin_large_batch_size_benchmark.cairo new file mode 100644 index 0000000000..21d643af34 --- /dev/null +++ b/cairo_programs/mod_builtin_feature/mod_builtin_large_batch_size_benchmark.cairo @@ -0,0 +1,65 @@ +%builtins range_check add_mod mul_mod +// TODO: Import directly from common library once released +from cairo_programs.mod_builtin_feature.common.modulo import ModBuiltin, UInt384, run_mod_p_circuit_with_large_batch_size +// from starkware.common.cairo_builtins import ModBuiltin, UInt384 +// from starkware.cairo.common.modulo import run_mod_p_circuit_with_large_batch_size +from starkware.cairo.common.registers import get_label_location +from starkware.cairo.common.alloc import alloc + +func run_circuit{range_check_ptr, add_mod_ptr: ModBuiltin*, mul_mod_ptr: ModBuiltin*}() { + alloc_locals; + + let p = UInt384(d0=1, d1=1, d2=0, d3=0); + let x1 = UInt384(d0=1, d1=0, d2=0, d3=0); + let x2 = UInt384(d0=2, d1=1, d2=0, d3=0); + let x3 = UInt384(d0=2, d1=0, d2=0, d3=0); + let res = UInt384(d0=1, d1=0, d2=0, d3=0); + + let (local values_arr: UInt384*) = alloc(); + assert values_arr[0] = x1; + assert values_arr[1] = x2; + assert values_arr[2] = x3; + assert values_arr[7] = res; + + let (local add_mod_offsets_arr: felt*) = alloc(); + assert add_mod_offsets_arr[0] = 0; // x1 + assert add_mod_offsets_arr[1] = 12; // x2 - x1 + assert add_mod_offsets_arr[2] = 4; // x2 + assert add_mod_offsets_arr[3] = 16; // (x2 - x1) * x3 + assert add_mod_offsets_arr[4] = 20; // x1 * x3 + assert add_mod_offsets_arr[5] = 24; // (x2 - x1) * x3 + x1 * x3 + + let (local mul_mod_offsets_arr: felt*) = alloc(); + assert mul_mod_offsets_arr[0] = 12; // x2 - x1 + assert mul_mod_offsets_arr[1] = 8; // x3 + assert mul_mod_offsets_arr[2] = 16; // (x2 - x1) * x3 + assert mul_mod_offsets_arr[3] = 0; // x1 + assert mul_mod_offsets_arr[4] = 8; // x3 + assert mul_mod_offsets_arr[5] = 20; // x1 * x3 + assert mul_mod_offsets_arr[6] = 8; // x3 + assert mul_mod_offsets_arr[7] = 28; // ((x2 - x1) * x3 + x1 * x3) / x3 = x2 mod p + assert mul_mod_offsets_arr[8] = 24; // (x2 - x1) * x3 + x1 * x3 + + run_mod_p_circuit_with_large_batch_size( + p=p, + values_ptr=values_arr, + add_mod_offsets_ptr=add_mod_offsets_arr, + add_mod_n=2, + mul_mod_offsets_ptr=mul_mod_offsets_arr, + mul_mod_n=3, + ); + + return (); +} + +func run_loop{range_check_ptr, add_mod_ptr: ModBuiltin*, mul_mod_ptr: ModBuiltin*}(n: felt) { + if (n == 0) { + return (); + } + run_circuit(); + return run_loop(n - 1); +} + +func main{range_check_ptr, add_mod_ptr: ModBuiltin*, mul_mod_ptr: ModBuiltin*}() { + return run_loop(100); +} diff --git a/cairo_programs/mod_builtin_feature/mod_builtin_large_batch_size_failure.cairo b/cairo_programs/mod_builtin_feature/mod_builtin_large_batch_size_failure.cairo new file mode 100644 index 0000000000..9605de779a --- /dev/null +++ b/cairo_programs/mod_builtin_feature/mod_builtin_large_batch_size_failure.cairo @@ -0,0 +1,53 @@ +%builtins range_check add_mod mul_mod +// TODO: Import directly from common library once released +from cairo_programs.mod_builtin_feature.common.modulo import ModBuiltin, UInt384, run_mod_p_circuit_with_large_batch_size +// from starkware.common.cairo_builtins import ModBuiltin, UInt384 +// from starkware.cairo.common.modulo import run_mod_p_circuit_with_large_batch_size +from starkware.cairo.common.registers import get_label_location +from starkware.cairo.common.alloc import alloc + +func main{range_check_ptr, add_mod_ptr: ModBuiltin*, mul_mod_ptr: ModBuiltin*}() { + alloc_locals; + + let p = UInt384(d0=1, d1=1, d2=0, d3=0); + let x1 = UInt384(d0=1, d1=0, d2=0, d3=0); + let x2 = UInt384(d0=2, d1=1, d2=0, d3=0); + let x3 = UInt384(d0=2, d1=0, d2=0, d3=0); + let res = UInt384(d0=2, d1=0, d2=0, d3=0); + + let (local values_arr: UInt384*) = alloc(); + assert values_arr[0] = x1; + assert values_arr[1] = x2; + assert values_arr[2] = x3; + assert values_arr[7] = res; + + let (local add_mod_offsets_arr: felt*) = alloc(); + assert add_mod_offsets_arr[0] = 0; // x1 + assert add_mod_offsets_arr[1] = 12; // x2 - x1 + assert add_mod_offsets_arr[2] = 4; // x2 + assert add_mod_offsets_arr[3] = 16; // (x2 - x1) * x3 + assert add_mod_offsets_arr[4] = 20; // x1 * x3 + assert add_mod_offsets_arr[5] = 24; // (x2 - x1) * x3 + x1 * x3 + + let (local mul_mod_offsets_arr: felt*) = alloc(); + assert mul_mod_offsets_arr[0] = 12; // x2 - x1 + assert mul_mod_offsets_arr[1] = 8; // x3 + assert mul_mod_offsets_arr[2] = 16; // (x2 - x1) * x3 + assert mul_mod_offsets_arr[3] = 0; // x1 + assert mul_mod_offsets_arr[4] = 8; // x3 + assert mul_mod_offsets_arr[5] = 20; // x1 * x3 + assert mul_mod_offsets_arr[6] = 8; // x3 + assert mul_mod_offsets_arr[7] = 28; // ((x2 - x1) * x3 + x1 * x3) / x3 = x2 mod p + assert mul_mod_offsets_arr[8] = 24; // (x2 - x1) * x3 + x1 * x3 + + run_mod_p_circuit_with_large_batch_size( + p=p, + values_ptr=values_arr, + add_mod_offsets_ptr=add_mod_offsets_arr, + add_mod_n=2, + mul_mod_offsets_ptr=mul_mod_offsets_arr, + mul_mod_n=3, + ); + + return (); +} diff --git a/cairo_programs/mod_builtin_feature/proof/apply_poly.cairo b/cairo_programs/mod_builtin_feature/proof/apply_poly.cairo new file mode 120000 index 0000000000..70d9ba18e4 --- /dev/null +++ b/cairo_programs/mod_builtin_feature/proof/apply_poly.cairo @@ -0,0 +1 @@ +../apply_poly.cairo \ No newline at end of file diff --git a/cairo_programs/mod_builtin_feature/proof/mod_builtin.cairo b/cairo_programs/mod_builtin_feature/proof/mod_builtin.cairo new file mode 120000 index 0000000000..754dd814da --- /dev/null +++ b/cairo_programs/mod_builtin_feature/proof/mod_builtin.cairo @@ -0,0 +1 @@ +../mod_builtin.cairo \ No newline at end of file diff --git a/cairo_programs/mod_builtin_feature/proof/mod_builtin_failure.cairo b/cairo_programs/mod_builtin_feature/proof/mod_builtin_failure.cairo new file mode 120000 index 0000000000..11df1c30ef --- /dev/null +++ b/cairo_programs/mod_builtin_feature/proof/mod_builtin_failure.cairo @@ -0,0 +1 @@ +../mod_builtin_failure.cairo \ No newline at end of file diff --git a/cairo_programs/mod_builtin_feature/proof/mod_builtin_large_batch_size.cairo b/cairo_programs/mod_builtin_feature/proof/mod_builtin_large_batch_size.cairo new file mode 120000 index 0000000000..edcda419b0 --- /dev/null +++ b/cairo_programs/mod_builtin_feature/proof/mod_builtin_large_batch_size.cairo @@ -0,0 +1 @@ +../mod_builtin_large_batch_size.cairo \ No newline at end of file diff --git a/cairo_programs/mod_builtin_feature/proof/mod_builtin_large_batch_size_benchmark.cairo b/cairo_programs/mod_builtin_feature/proof/mod_builtin_large_batch_size_benchmark.cairo new file mode 120000 index 0000000000..07b4071b7e --- /dev/null +++ b/cairo_programs/mod_builtin_feature/proof/mod_builtin_large_batch_size_benchmark.cairo @@ -0,0 +1 @@ +../mod_builtin_large_batch_size_benchmark.cairo \ No newline at end of file diff --git a/cairo_programs/mod_builtin_feature/proof/mod_builtin_large_batch_size_failure.cairo b/cairo_programs/mod_builtin_feature/proof/mod_builtin_large_batch_size_failure.cairo new file mode 120000 index 0000000000..1882352a93 --- /dev/null +++ b/cairo_programs/mod_builtin_feature/proof/mod_builtin_large_batch_size_failure.cairo @@ -0,0 +1 @@ +../mod_builtin_large_batch_size_failure.cairo \ No newline at end of file diff --git a/cairo_programs/pedersen_extra_builtins.cairo b/cairo_programs/pedersen_extra_builtins.cairo new file mode 100644 index 0000000000..513ce93ddb --- /dev/null +++ b/cairo_programs/pedersen_extra_builtins.cairo @@ -0,0 +1,11 @@ +%builtins output pedersen range_check ecdsa bitwise ec_op + +from starkware.cairo.common.cairo_builtins import HashBuiltin +from starkware.cairo.common.hash import hash2 + +func main{output_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr, ecdsa_ptr, bitwise_ptr, ec_op_ptr}() { + let (seed) = hash2{hash_ptr=pedersen_ptr}(0, 0); + assert [output_ptr] = seed; + let output_ptr = output_ptr + 1; + return (); +} diff --git a/cairo_programs/secp.cairo b/cairo_programs/secp.cairo index 747d8c7421..c7e9018eb0 100644 --- a/cairo_programs/secp.cairo +++ b/cairo_programs/secp.cairo @@ -1,6 +1,6 @@ %builtins range_check -from starkware.cairo.common.cairo_secp.bigint import nondet_bigint3, BigInt3, bigint_to_uint256 - +from starkware.cairo.common.cairo_secp.bigint3 import BigInt3, SumBigInt3 +from starkware.cairo.common.cairo_secp.bigint import nondet_bigint3, bigint_to_uint256 from starkware.cairo.common.cairo_secp.field import verify_zero, UnreducedBigInt3, reduce, is_zero func main{range_check_ptr: felt}() { @@ -43,17 +43,17 @@ func main{range_check_ptr: felt}() { ); // is_zero - let (u) = is_zero(BigInt3(0, 0, 0)); + let (u) = is_zero(SumBigInt3(0, 0, 0)); assert u = 1; let (v) = is_zero( - BigInt3(232113757366008801543585, 232113757366008801543585, 232113757366008801543585) + SumBigInt3(232113757366008801543585, 232113757366008801543585, 232113757366008801543585) ); assert v = 0; - let (w) = is_zero(BigInt3(-10, -10, -10)); + let (w) = is_zero(SumBigInt3(-10, -10, -10)); assert w = 0; - let (z) = is_zero(BigInt3(1833312543, 67523423, 8790312)); + let (z) = is_zero(SumBigInt3(1833312543, 67523423, 8790312)); assert z = 0; return (); diff --git a/cairo_programs/secp_integration_tests.cairo b/cairo_programs/secp_integration_tests.cairo index 7eeaae615b..45ec410248 100644 --- a/cairo_programs/secp_integration_tests.cairo +++ b/cairo_programs/secp_integration_tests.cairo @@ -1,12 +1,12 @@ %builtins range_check from starkware.cairo.common.cairo_secp.bigint import ( - BigInt3, bigint_mul, nondet_bigint3, bigint_to_uint256, uint256_to_bigint, ) +from starkware.cairo.common.cairo_secp.bigint3 import BigInt3, SumBigInt3 from starkware.cairo.common.cairo_secp.signature import ( get_generator_point, validate_signature_entry, @@ -86,7 +86,7 @@ func test_operations{range_check_ptr}(point: EcPoint) { let (zero_uint, _) = uint256_add(slope_uint, neg_slope); let (zero) = uint256_to_bigint(zero_uint); - let (is_z) = is_zero(zero); + let (is_z) = is_zero(SumBigInt3(d0=zero.d0, d1=zero.d1, d2=zero.d2)); assert is_z = 1; let (pow2, scaled) = ec_mul_inner(point, 0, 0); diff --git a/docs/README.md b/docs/README.md index dd1d40c420..f25a1c762a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,3 +4,4 @@ * [Custom Hint Processor](./hint_processor/) * [How to run a cairo program with custom hints](./hint_processor/builtin_hint_processor) * [References parsing](./references_parsing/) +* [Tracer](./tracer/) diff --git a/docs/tracer/README.md b/docs/tracer/README.md new file mode 100644 index 0000000000..5e5e3b6404 --- /dev/null +++ b/docs/tracer/README.md @@ -0,0 +1,39 @@ +# Cairo Tracer + +Cairo-vm offers a tracer which gives you a visualization of how your memory and registers change line after line as the VM executes the code. + +## Setting up the tracer + +To use the tracer, you need to build your the VM using the `with_tracer` feature flag. + +```bash +cargo build --release --features with_tracer +``` + +## Running the tracer + +Once the build in the above step is complete, you can use the `cairo-vm-cli` as you do normally with an additional argument `--tracer true`. This tells the VM to start a server where you can visualize the exection of your cairo program. + +```bash +target/release/cairo-vm-cli --layout --memory_file --trace_file --tracer true +``` + +### NOTE +> The `--memory_file` and `--trace_file` arguments are compulsory at the moment as the current code relocates the memory and trace only when these arguments are supplied. However, edits can be made in future versions to relocate if only the `--tracer` option is specified. + + +## Using the tracer + +![tracer](tracer.png) + +You can go to [http://127.0.0.1:8100/static/index.html](http://127.0.0.1:8100/static/index.html) to see the tracer. + +Use the following commands to visualize the code execution + +- `s` to go to the next step +- `Shift + s` to go to the previous step +- `n` to step over +- `N` to previous step over +- `o` to step out +- `b` to run until you reach the next breakpoint +- `B` to run until you reach the previous breakpoint diff --git a/docs/tracer/tracer.png b/docs/tracer/tracer.png new file mode 100644 index 0000000000..9cb29af0d9 Binary files /dev/null and b/docs/tracer/tracer.png differ diff --git a/examples/hyper_threading/Cargo.toml b/examples/hyper_threading/Cargo.toml new file mode 100644 index 0000000000..854e2c3fde --- /dev/null +++ b/examples/hyper_threading/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "hyper_threading" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +readme.workspace = true +keywords.workspace = true + + +[dependencies] +cairo-vm = { workspace = true, features = ["std"] } +rayon = "1.9.0" +tracing = "0.1.40" diff --git a/examples/hyper_threading/README.md b/examples/hyper_threading/README.md new file mode 100644 index 0000000000..e69152ef2a --- /dev/null +++ b/examples/hyper_threading/README.md @@ -0,0 +1,11 @@ +# Hyper-Threading Benchmarks for Cairo-VM + +## Overview +This crate is designed to benchmark the performance of Cairo-VM in a hyper-threaded environment. By leveraging the [Rayon library](https://docs.rs/rayon/latest/rayon/), we can transform sequential computations into parallel ones, maximizing the utilization of available CPU cores. + +### Running Benchmarks +To execute the benchmarks, navigate to the project's root directory and run the following command: + +```bash +make hyper-threading-benchmarks +``` diff --git a/examples/hyper_threading/hyper-threading-workflow.sh b/examples/hyper_threading/hyper-threading-workflow.sh new file mode 100644 index 0000000000..5ab1cb1019 --- /dev/null +++ b/examples/hyper_threading/hyper-threading-workflow.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# Define a list of RAYON_NUM_THREADS +thread_counts=(1 2 4 6 8 16 ) + +# Define binary names +binaries=("hyper_threading_main" "hyper_threading_pr") + +echo -e "**Hyper Thereading Benchmark results**" >> result.md +printf "\n\n" >> result.md + +# Iter over thread_counts +for threads in "${thread_counts[@]}"; do + # Initialize hyperfine command + cmd="hyperfine -r 2" + + # Add each binary to the command with the current threads value + for binary in "${binaries[@]}"; do + cmd+=" -n \"${binary} threads: ${threads}\" 'RAYON_NUM_THREADS=${threads} ./${binary}'" + done + + # Execute + echo "Running benchmark for ${threads} threads" + printf "\n\n" >> result.md + echo -e $cmd >> result.md + eval $cmd >> result.md + printf "\n\n" >> result.md +done + +{ + echo -e '```' + cat result.md + echo -e '```' +} > temp_result.md && mv temp_result.md result.md diff --git a/examples/hyper_threading/hyper-threading.sh b/examples/hyper_threading/hyper-threading.sh new file mode 100644 index 0000000000..912b6cac75 --- /dev/null +++ b/examples/hyper_threading/hyper-threading.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +thread_counts=(1 2 4 6 8 10 12 16 24 32 ) +binary="target/release/hyper_threading" + + +cmd="hyperfine -r 1" + +# Build the command string with all thread counts +for threads in "${thread_counts[@]}"; do + # For hyperfine, wrap each command in 'sh -c' to correctly handle the environment variable + cmd+=" -n \"threads: ${threads}\" 'sh -c \"RAYON_NUM_THREADS=${threads} ${binary}\"'" +done + +# Execute the hyperfine command +echo "Executing benchmark for all thread counts" +eval $cmd diff --git a/examples/hyper_threading/src/main.rs b/examples/hyper_threading/src/main.rs new file mode 100644 index 0000000000..eb6fbf47c9 --- /dev/null +++ b/examples/hyper_threading/src/main.rs @@ -0,0 +1,68 @@ +use cairo_vm::{ + cairo_run::{cairo_run_program, CairoRunConfig}, + hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor, + types::program::Program, +}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use std::path::Path; + +// Define build_filename macro to prepend a relative path to the file names +macro_rules! build_filename { + ($fname:expr) => { + format!("cairo_programs/benchmarks/{}", $fname) + }; +} + +fn main() { + let mut programs = Vec::new(); + + let program_filenames: [String; 18] = [ + build_filename!("big_factorial.json"), + build_filename!("big_fibonacci.json"), + build_filename!("blake2s_integration_benchmark.json"), + build_filename!("compare_arrays_200000.json"), + build_filename!("dict_integration_benchmark.json"), + build_filename!("field_arithmetic_get_square_benchmark.json"), + build_filename!("integration_builtins.json"), + build_filename!("keccak_integration_benchmark.json"), + build_filename!("linear_search.json"), + build_filename!("math_cmp_and_pow_integration_benchmark.json"), + build_filename!("math_integration_benchmark.json"), + build_filename!("memory_integration_benchmark.json"), + build_filename!("operations_with_data_structures_benchmarks.json"), + build_filename!("pedersen.json"), + build_filename!("poseidon_integration_benchmark.json"), + build_filename!("secp_integration_benchmark.json"), + build_filename!("set_integration_benchmark.json"), + build_filename!("uint256_integration_benchmark.json"), + ]; + + let n_programs = &program_filenames.len(); + + for filename in program_filenames { + programs.push(Program::from_file(Path::new(&filename), Some("main")).unwrap()) + } + + let start_time = std::time::Instant::now(); + + // Parallel execution of the program processing + programs.into_par_iter().for_each(|program| { + let cairo_run_config = CairoRunConfig { + entrypoint: "main", + trace_enabled: false, + relocate_mem: false, + layout: "all_cairo", + proof_mode: true, + secure_run: Some(false), + ..Default::default() + }; + let mut hint_executor = BuiltinHintProcessor::new_empty(); + + // Execute each program in parallel + let _result = cairo_run_program(&program, &cairo_run_config, &mut hint_executor) + .expect("Couldn't run program"); + }); + let elapsed = start_time.elapsed(); + + tracing::info!(%n_programs, ?elapsed, "Finished"); +} diff --git a/fuzzer/Cargo.lock b/fuzzer/Cargo.lock index 7d61c3263c..b48dfec845 100644 --- a/fuzzer/Cargo.lock +++ b/fuzzer/Cargo.lock @@ -210,7 +210,7 @@ dependencies = [ [[package]] name = "cairo-vm" -version = "1.0.0-rc0" +version = "1.0.0-rc1" dependencies = [ "anyhow", "arbitrary", @@ -221,7 +221,6 @@ dependencies = [ "hex", "keccak", "lazy_static", - "mimalloc", "nom", "num-bigint", "num-integer", @@ -520,13 +519,26 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "lambdaworks-crypto" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d4c222d5b2fdc0faf702d3ab361d14589b097f40eac9dc550e27083483edc65" +dependencies = [ + "lambdaworks-math", + "serde", + "sha2", + "sha3", +] + [[package]] name = "lambdaworks-math" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6c4d0ddd1fcd235be5196b1bcc404f89ad3e911f4c190fa01459e05dbf40f8" +checksum = "9ee7dcab3968c71896b8ee4dc829147acc918cffe897af6265b1894527fe3add" dependencies = [ - "thiserror", + "serde", + "serde_json", ] [[package]] @@ -555,16 +567,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "libmimalloc-sys" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3979b5c37ece694f1f5e51e7ecc871fdb0f517ed04ee45f88d15d6d553cb9664" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "lock_api" version = "0.4.11" @@ -614,15 +616,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "mimalloc" -version = "0.1.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa01922b5ea280a911e323e4d2fd24b7fe5cc4042e0d2cda3c40775cdc4bdc9c" -dependencies = [ - "libmimalloc-sys", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1078,12 +1071,13 @@ dependencies = [ [[package]] name = "starknet-types-core" -version = "0.0.6" +version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b6b868f545d43b474c2c00e9349c489fdeb7ff17eb00cdf339744ac4cae0930" +checksum = "6d53160556d1f23425100f42b3230df747ea05763efee685a2cd939dfb640701" dependencies = [ "arbitrary", "bitvec", + "lambdaworks-crypto", "lambdaworks-math", "lazy_static", "num-bigint", @@ -1132,26 +1126,6 @@ version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" -[[package]] -name = "thiserror" -version = "1.0.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - [[package]] name = "thiserror-impl-no-std" version = "2.0.2" diff --git a/fuzzer/src/fuzz_json.rs b/fuzzer/src/fuzz_json.rs index 09193c512b..4306a29c67 100644 --- a/fuzzer/src/fuzz_json.rs +++ b/fuzzer/src/fuzz_json.rs @@ -29,7 +29,7 @@ const HEX_SYMBOLS: [&str; 16] = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", ]; -const HINTS_CODE: [&str; 184] = [ +const HINTS_CODE: [&str; 186] = [ ADD_SEGMENT, VM_ENTER_SCOPE, VM_EXIT_SCOPE, @@ -139,10 +139,12 @@ const HINTS_CODE: [&str; 184] = [ EC_DOUBLE_SLOPE_V1, EC_DOUBLE_SLOPE_V2, EC_DOUBLE_SLOPE_V3, + EC_DOUBLE_SLOPE_V4, EC_DOUBLE_SLOPE_EXTERNAL_CONSTS, COMPUTE_SLOPE_V1, COMPUTE_SLOPE_V2, - COMPUTE_SLOPE_SECP256R1, + COMPUTE_SLOPE_SECP256R1_V1, + COMPUTE_SLOPE_SECP256R1_V2, IMPORT_SECP256R1_P, COMPUTE_SLOPE_WHITELIST, EC_DOUBLE_ASSIGN_NEW_X_V1, diff --git a/requirements.txt b/requirements.txt index 5eca66949f..41c32affe0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ ecdsa==0.18.0 bitarray==2.7.3 -fastecdsa==2.2.3 +fastecdsa==2.3.0 sympy==1.11.1 typeguard==2.13.3 -cairo-lang==0.12.2 +cairo-lang==0.13.1 diff --git a/vm/Cargo.toml b/vm/Cargo.toml index fca39d4aee..e04782221e 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -9,8 +9,7 @@ readme.workspace = true keywords.workspace = true [features] -default = ["std", "with_mimalloc"] -with_mimalloc = ["dep:mimalloc"] +default = ["std"] std = [ "serde_json/std", "bincode/std", @@ -27,6 +26,8 @@ cairo-1-hints = [ "dep:ark-ff", "dep:ark-std", ] +tracer = [] +mod_builtin = [] # Note that these features are not retro-compatible with the cairo Python VM. test_utils = [ @@ -44,7 +45,6 @@ print = ["std"] [dependencies] zip = {version = "0.6.6", optional = true } -mimalloc = { workspace = true, optional = true } num-bigint = { workspace = true } rand = { workspace = true } num-traits = { workspace = true } @@ -63,7 +63,7 @@ keccak = { workspace = true } hashbrown = { workspace = true } anyhow = { workspace = true } thiserror-no-std = { workspace = true } -starknet-types-core = { version = "0.0.9", default-features = false, features = ["serde", "curve", "num-traits"] } +starknet-types-core = { version = "0.1.0", default-features = false, features = ["serde", "curve", "num-traits", "hash"] } # only for std num-prime = { version = "0.4.3", features = ["big-int"], optional = true } @@ -92,6 +92,7 @@ wasm-bindgen-test = "0.3.34" iai-callgrind = "0.3.1" criterion = { version = "0.5.1", features = ["html_reports"] } proptest = "1.0.0" +mimalloc.workspace = true [[bench]] path = "../bench/iai_benchmark.rs" diff --git a/vm/src/air_private_input.rs b/vm/src/air_private_input.rs index 6429da0673..d1c5f1a8ca 100644 --- a/vm/src/air_private_input.rs +++ b/vm/src/air_private_input.rs @@ -1,11 +1,12 @@ use crate::{ stdlib::{ - collections::HashMap, + collections::{BTreeMap, HashMap}, prelude::{String, Vec}, }, vm::runners::builtin_runner::{ - BITWISE_BUILTIN_NAME, EC_OP_BUILTIN_NAME, HASH_BUILTIN_NAME, KECCAK_BUILTIN_NAME, - POSEIDON_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME, SIGNATURE_BUILTIN_NAME, + ADD_MOD_BUILTIN_NAME, BITWISE_BUILTIN_NAME, EC_OP_BUILTIN_NAME, HASH_BUILTIN_NAME, + KECCAK_BUILTIN_NAME, MUL_MOD_BUILTIN_NAME, POSEIDON_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME, + SIGNATURE_BUILTIN_NAME, }, }; use serde::{Deserialize, Serialize}; @@ -17,13 +18,24 @@ use crate::Felt252; pub struct AirPrivateInputSerializable { trace_path: String, memory_path: String, - pedersen: Vec, - range_check: Vec, - ecdsa: Vec, - bitwise: Vec, - ec_op: Vec, - keccak: Vec, - poseidon: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pedersen: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + range_check: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + ecdsa: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + bitwise: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + ec_op: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + keccak: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + poseidon: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + add_mod: Option, + #[serde(skip_serializing_if = "Option::is_none")] + mul_mod: Option, } // Contains only builtin public inputs, useful for library users @@ -39,6 +51,7 @@ pub enum PrivateInput { PoseidonState(PrivateInputPoseidonState), KeccakState(PrivateInputKeccakState), Signature(PrivateInputSignature), + Mod(ModInput), } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] @@ -99,6 +112,44 @@ pub struct SignatureInput { pub w: Felt252, } +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct ModInput { + pub instances: Vec, + pub zero_value_address: usize, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] +pub struct ModInputInstance { + pub index: usize, + pub p0: Felt252, + pub p1: Felt252, + pub p2: Felt252, + pub p3: Felt252, + pub values_ptr: usize, + pub offsets_ptr: usize, + pub n: usize, + pub batch: BTreeMap, +} + +#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)] +pub struct ModInputMemoryVars { + pub a_offset: usize, + pub a0: Felt252, + pub a1: Felt252, + pub a2: Felt252, + pub a3: Felt252, + pub b_offset: usize, + pub b0: Felt252, + pub b1: Felt252, + pub b2: Felt252, + pub b3: Felt252, + pub c_offset: usize, + pub c0: Felt252, + pub c1: Felt252, + pub c2: Felt252, + pub c3: Felt252, +} + impl AirPrivateInput { pub fn to_serializable( &self, @@ -108,44 +159,44 @@ impl AirPrivateInput { AirPrivateInputSerializable { trace_path, memory_path, - pedersen: self.0.get(HASH_BUILTIN_NAME).cloned().unwrap_or_default(), - range_check: self - .0 - .get(RANGE_CHECK_BUILTIN_NAME) - .cloned() - .unwrap_or_default(), - ecdsa: self + pedersen: self.0.get(HASH_BUILTIN_NAME).cloned(), + range_check: self.0.get(RANGE_CHECK_BUILTIN_NAME).cloned(), + ecdsa: self.0.get(SIGNATURE_BUILTIN_NAME).cloned(), + bitwise: self.0.get(BITWISE_BUILTIN_NAME).cloned(), + ec_op: self.0.get(EC_OP_BUILTIN_NAME).cloned(), + keccak: self.0.get(KECCAK_BUILTIN_NAME).cloned(), + poseidon: self.0.get(POSEIDON_BUILTIN_NAME).cloned(), + add_mod: self .0 - .get(SIGNATURE_BUILTIN_NAME) - .cloned() - .unwrap_or_default(), - bitwise: self + .get(ADD_MOD_BUILTIN_NAME) + .and_then(|pi| pi.first()) + .cloned(), + mul_mod: self .0 - .get(BITWISE_BUILTIN_NAME) - .cloned() - .unwrap_or_default(), - ec_op: self.0.get(EC_OP_BUILTIN_NAME).cloned().unwrap_or_default(), - keccak: self.0.get(KECCAK_BUILTIN_NAME).cloned().unwrap_or_default(), - poseidon: self - .0 - .get(POSEIDON_BUILTIN_NAME) - .cloned() - .unwrap_or_default(), + .get(MUL_MOD_BUILTIN_NAME) + .and_then(|pi| pi.first()) + .cloned(), } } } impl From for AirPrivateInput { fn from(private_input: AirPrivateInputSerializable) -> Self { - Self(HashMap::from([ - (HASH_BUILTIN_NAME, private_input.pedersen), - (RANGE_CHECK_BUILTIN_NAME, private_input.range_check), - (SIGNATURE_BUILTIN_NAME, private_input.ecdsa), - (BITWISE_BUILTIN_NAME, private_input.bitwise), - (EC_OP_BUILTIN_NAME, private_input.ec_op), - (KECCAK_BUILTIN_NAME, private_input.keccak), - (POSEIDON_BUILTIN_NAME, private_input.poseidon), - ])) + let mut inputs = HashMap::new(); + let mut insert_input = |input_name, input| { + if let Some(input) = input { + inputs.insert(input_name, input); + } + }; + insert_input(HASH_BUILTIN_NAME, private_input.pedersen); + insert_input(RANGE_CHECK_BUILTIN_NAME, private_input.range_check); + insert_input(SIGNATURE_BUILTIN_NAME, private_input.ecdsa); + insert_input(BITWISE_BUILTIN_NAME, private_input.bitwise); + insert_input(EC_OP_BUILTIN_NAME, private_input.ec_op); + insert_input(KECCAK_BUILTIN_NAME, private_input.keccak); + insert_input(POSEIDON_BUILTIN_NAME, private_input.poseidon); + + Self(inputs) } } @@ -168,22 +219,25 @@ mod tests { assert_matches::assert_matches, }; + #[cfg(any(target_arch = "wasm32", no_std, not(feature = "std")))] + use crate::alloc::string::ToString; + #[cfg(feature = "std")] #[test] fn test_from_serializable() { let serializable_private_input = AirPrivateInputSerializable { trace_path: "trace.bin".to_string(), memory_path: "memory.bin".to_string(), - pedersen: vec![PrivateInput::Pair(PrivateInputPair { + pedersen: Some(vec![PrivateInput::Pair(PrivateInputPair { index: 0, x: Felt252::from(100), y: Felt252::from(200), - })], - range_check: vec![PrivateInput::Value(PrivateInputValue { + })]), + range_check: Some(vec![PrivateInput::Value(PrivateInputValue { index: 10000, value: Felt252::from(8000), - })], - ecdsa: vec![PrivateInput::Signature(PrivateInputSignature { + })]), + ecdsa: Some(vec![PrivateInput::Signature(PrivateInputSignature { index: 0, pubkey: Felt252::from(123), msg: Felt252::from(456), @@ -191,21 +245,21 @@ mod tests { r: Felt252::from(654), w: Felt252::from(321), }, - })], - bitwise: vec![PrivateInput::Pair(PrivateInputPair { + })]), + bitwise: Some(vec![PrivateInput::Pair(PrivateInputPair { index: 4, x: Felt252::from(7), y: Felt252::from(8), - })], - ec_op: vec![PrivateInput::EcOp(PrivateInputEcOp { + })]), + ec_op: Some(vec![PrivateInput::EcOp(PrivateInputEcOp { index: 1, p_x: Felt252::from(10), p_y: Felt252::from(10), m: Felt252::from(100), q_x: Felt252::from(11), q_y: Felt252::from(14), - })], - keccak: vec![PrivateInput::KeccakState(PrivateInputKeccakState { + })]), + keccak: Some(vec![PrivateInput::KeccakState(PrivateInputKeccakState { index: 0, input_s0: Felt252::from(0), input_s1: Felt252::from(1), @@ -215,23 +269,49 @@ mod tests { input_s5: Felt252::from(5), input_s6: Felt252::from(6), input_s7: Felt252::from(7), - })], - poseidon: vec![PrivateInput::PoseidonState(PrivateInputPoseidonState { - index: 42, - input_s0: Felt252::from(1), - input_s1: Felt252::from(2), - input_s2: Felt252::from(3), - })], + })]), + poseidon: Some(vec![PrivateInput::PoseidonState( + PrivateInputPoseidonState { + index: 42, + input_s0: Felt252::from(1), + input_s1: Felt252::from(2), + input_s2: Felt252::from(3), + }, + )]), + add_mod: None, + mul_mod: None, }; let private_input = AirPrivateInput::from(serializable_private_input.clone()); - assert_matches!(private_input.0.get(HASH_BUILTIN_NAME), Some(data) if *data == serializable_private_input.pedersen); - assert_matches!(private_input.0.get(RANGE_CHECK_BUILTIN_NAME), Some(data) if *data == serializable_private_input.range_check); - assert_matches!(private_input.0.get(SIGNATURE_BUILTIN_NAME), Some(data) if *data == serializable_private_input.ecdsa); - assert_matches!(private_input.0.get(BITWISE_BUILTIN_NAME), Some(data) if *data == serializable_private_input.bitwise); - assert_matches!(private_input.0.get(EC_OP_BUILTIN_NAME), Some(data) if *data == serializable_private_input.ec_op); - assert_matches!(private_input.0.get(KECCAK_BUILTIN_NAME), Some(data) if *data == serializable_private_input.keccak); - assert_matches!(private_input.0.get(POSEIDON_BUILTIN_NAME), Some(data) if *data == serializable_private_input.poseidon); + assert_matches!(private_input.0.get(HASH_BUILTIN_NAME), data if data == serializable_private_input.pedersen.as_ref()); + assert_matches!(private_input.0.get(RANGE_CHECK_BUILTIN_NAME), data if data == serializable_private_input.range_check.as_ref()); + assert_matches!(private_input.0.get(SIGNATURE_BUILTIN_NAME), data if data == serializable_private_input.ecdsa.as_ref()); + assert_matches!(private_input.0.get(BITWISE_BUILTIN_NAME), data if data == serializable_private_input.bitwise.as_ref()); + assert_matches!(private_input.0.get(EC_OP_BUILTIN_NAME), data if data == serializable_private_input.ec_op.as_ref()); + assert_matches!(private_input.0.get(KECCAK_BUILTIN_NAME), data if data == serializable_private_input.keccak.as_ref()); + assert_matches!(private_input.0.get(POSEIDON_BUILTIN_NAME), data if data == serializable_private_input.poseidon.as_ref()); + } + + #[test] + fn serialize_air_private_input_small_layout_only_builtins() { + let config = crate::cairo_run::CairoRunConfig { + proof_mode: true, + relocate_mem: true, + trace_enabled: true, + layout: "small", + ..Default::default() + }; + let (runner, vm) = crate::cairo_run::cairo_run(include_bytes!("../../cairo_programs/proof_programs/fibonacci.json"), &config, &mut crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor::new_empty()).unwrap(); + let public_input = runner.get_air_private_input(&vm); + let serialized_public_input = + public_input.to_serializable("/dev/null".to_string(), "/dev/null".to_string()); + assert!(serialized_public_input.pedersen.is_some()); + assert!(serialized_public_input.range_check.is_some()); + assert!(serialized_public_input.ecdsa.is_some()); + assert!(serialized_public_input.bitwise.is_none()); + assert!(serialized_public_input.ec_op.is_none()); + assert!(serialized_public_input.keccak.is_none()); + assert!(serialized_public_input.poseidon.is_none()); } } diff --git a/vm/src/air_public_input.rs b/vm/src/air_public_input.rs index 692f391f2a..3a2bf8bc37 100644 --- a/vm/src/air_public_input.rs +++ b/vm/src/air_public_input.rs @@ -1,5 +1,5 @@ use crate::Felt252; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use thiserror_no_std::Error; use crate::{ @@ -14,18 +14,21 @@ use crate::{ }, }; -#[derive(Serialize, Debug)] +#[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct PublicMemoryEntry { pub address: usize, #[serde(serialize_with = "mem_value_serde::serialize")] + #[serde(deserialize_with = "mem_value_serde::deserialize")] pub value: Option, pub page: usize, } mod mem_value_serde { + use core::fmt; + use super::*; - use serde::Serializer; + use serde::{de, Deserializer, Serializer}; pub(crate) fn serialize( value: &Option, @@ -37,9 +40,41 @@ mod mem_value_serde { serializer.serialize_none() } } + + pub(crate) fn deserialize<'de, D: Deserializer<'de>>( + d: D, + ) -> Result, D::Error> { + d.deserialize_str(Felt252OptionVisitor) + } + + struct Felt252OptionVisitor; + + impl<'de> de::Visitor<'de> for Felt252OptionVisitor { + type Value = Option; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Could not deserialize hexadecimal string") + } + + fn visit_none(self) -> Result + where + E: de::Error, + { + Ok(None) + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + Felt252::from_hex(value) + .map_err(de::Error::custom) + .map(Some) + } + } } -#[derive(Serialize, Debug)] +#[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct MemorySegmentAddresses { pub begin_addr: usize, pub stop_ptr: usize, @@ -55,7 +90,7 @@ impl From<(usize, usize)> for MemorySegmentAddresses { } } -#[derive(Serialize, Debug)] +#[derive(Serialize, Deserialize, Debug)] pub struct PublicInput<'a> { pub layout: &'a str, pub rc_min: isize, @@ -64,6 +99,7 @@ pub struct PublicInput<'a> { pub memory_segments: HashMap<&'a str, MemorySegmentAddresses>, pub public_memory: Vec, #[serde(rename = "dynamic_params")] + #[serde(skip_deserializing)] // This is set to None by default so we can skip it layout_params: Option<&'a CairoLayout>, } @@ -139,3 +175,52 @@ pub enum PublicInputError { #[error(transparent)] Trace(#[from] TraceError), } +#[cfg(test)] +mod tests { + #[cfg(feature = "std")] + use super::*; + #[cfg(feature = "std")] + use rstest::rstest; + + #[cfg(feature = "std")] + #[rstest] + #[case(include_bytes!("../../cairo_programs/proof_programs/fibonacci.json"))] + #[case(include_bytes!("../../cairo_programs/proof_programs/bitwise_output.json"))] + #[case(include_bytes!("../../cairo_programs/proof_programs/keccak_builtin.json"))] + #[case(include_bytes!("../../cairo_programs/proof_programs/poseidon_builtin.json"))] + #[case(include_bytes!("../../cairo_programs/proof_programs/relocate_temporary_segment_append.json"))] + #[case(include_bytes!("../../cairo_programs/proof_programs/pedersen_test.json"))] + #[case(include_bytes!("../../cairo_programs/proof_programs/ec_op.json"))] + fn serialize_and_deserialize_air_public_input(#[case] program_content: &[u8]) { + let config = crate::cairo_run::CairoRunConfig { + proof_mode: true, + relocate_mem: true, + trace_enabled: true, + layout: "all_cairo", + ..Default::default() + }; + let (runner, vm) = crate::cairo_run::cairo_run(program_content, &config, &mut crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor::new_empty()).unwrap(); + let public_input = runner.get_air_public_input(&vm).unwrap(); + // We already know serialization works as expected due to the comparison against python VM + let serialized_public_input = public_input.serialize_json().unwrap(); + let deserialized_public_input: PublicInput = + serde_json::from_str(&serialized_public_input).unwrap(); + // Check that the deserialized public input is equal to the one we obtained from the vm first + assert_eq!(public_input.layout, deserialized_public_input.layout); + assert_eq!(public_input.rc_max, deserialized_public_input.rc_max); + assert_eq!(public_input.rc_min, deserialized_public_input.rc_min); + assert_eq!(public_input.n_steps, deserialized_public_input.n_steps); + assert_eq!( + public_input.memory_segments, + deserialized_public_input.memory_segments + ); + assert_eq!( + public_input.public_memory, + deserialized_public_input.public_memory + ); + assert!( + public_input.layout_params.is_none() + && deserialized_public_input.layout_params.is_none() + ); + } +} diff --git a/vm/src/cairo_run.rs b/vm/src/cairo_run.rs index 8293bc9625..07c3a29c77 100644 --- a/vm/src/cairo_run.rs +++ b/vm/src/cairo_run.rs @@ -88,6 +88,10 @@ pub fn cairo_run_program( cairo_runner .run_until_pc(end, &mut vm, hint_executor) .map_err(|err| VmException::from_vm_error(&cairo_runner, &vm, err))?; + + if cairo_run_config.proof_mode { + cairo_runner.run_for_steps(1, &mut vm, hint_executor)?; + } cairo_runner.end_run( cairo_run_config.disable_trace_padding, false, @@ -96,7 +100,7 @@ pub fn cairo_run_program( )?; vm.verify_auto_deductions()?; - cairo_runner.read_return_values(&mut vm)?; + cairo_runner.read_return_values(&mut vm, allow_missing_builtins)?; if cairo_run_config.proof_mode { cairo_runner.finalize_segments(&mut vm)?; } @@ -131,6 +135,10 @@ pub fn cairo_run_fuzzed_program( .secure_run .unwrap_or(!cairo_run_config.proof_mode); + let allow_missing_builtins = cairo_run_config + .allow_missing_builtins + .unwrap_or(cairo_run_config.proof_mode); + let mut cairo_runner = CairoRunner::new( &program, cairo_run_config.layout, @@ -139,12 +147,7 @@ pub fn cairo_run_fuzzed_program( let mut vm = VirtualMachine::new(cairo_run_config.trace_enabled); - let _end = cairo_runner.initialize( - &mut vm, - cairo_run_config - .allow_missing_builtins - .unwrap_or(cairo_run_config.proof_mode), - )?; + let _end = cairo_runner.initialize(&mut vm, allow_missing_builtins)?; let res = match cairo_runner.run_until_steps(steps_limit, &mut vm, hint_executor) { Err(VirtualMachineError::EndOfProgram(_remaining)) => Ok(()), // program ran OK but ended before steps limit @@ -156,7 +159,7 @@ pub fn cairo_run_fuzzed_program( cairo_runner.end_run(false, false, &mut vm, hint_executor)?; vm.verify_auto_deductions()?; - cairo_runner.read_return_values(&mut vm)?; + cairo_runner.read_return_values(&mut vm, allow_missing_builtins)?; if cairo_run_config.proof_mode { cairo_runner.finalize_segments(&mut vm)?; } diff --git a/vm/src/hint_processor/builtin_hint_processor/bigint.rs b/vm/src/hint_processor/builtin_hint_processor/bigint.rs index 2069446fb7..ff5326bd58 100644 --- a/vm/src/hint_processor/builtin_hint_processor/bigint.rs +++ b/vm/src/hint_processor/builtin_hint_processor/bigint.rs @@ -103,7 +103,6 @@ mod test { use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData; use crate::hint_processor::builtin_hint_processor::hint_code; use crate::hint_processor::hint_processor_definition::{HintProcessorLogic, HintReference}; - use crate::stdlib::collections::HashMap; use crate::types::exec_scope::ExecutionScopes; use crate::utils::test_utils::*; use crate::vm::vm_core::VirtualMachine; diff --git a/vm/src/hint_processor/builtin_hint_processor/blake2s_utils.rs b/vm/src/hint_processor/builtin_hint_processor/blake2s_utils.rs index 5f5fe2ef83..e56258018e 100644 --- a/vm/src/hint_processor/builtin_hint_processor/blake2s_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/blake2s_utils.rs @@ -271,9 +271,8 @@ pub fn example_blake2s_compress( let blake2s_start = get_ptr_from_var_name("blake2s_start", vm, ids_data, ap_tracking)?; let output = get_ptr_from_var_name("output", vm, ids_data, ap_tracking)?; let n_bytes = get_integer_from_var_name("n_bytes", vm, ids_data, ap_tracking).map(|x| { - x.to_u32().ok_or_else(|| { - HintError::Math(MathError::Felt252ToU32Conversion(Box::new(x.into_owned()))) - }) + x.to_u32() + .ok_or_else(|| HintError::Math(MathError::Felt252ToU32Conversion(Box::new(x)))) })??; let message = get_fixed_size_u32_array::<16>(&vm.get_integer_range(blake2s_start, 16)?)?; @@ -302,7 +301,6 @@ mod tests { hint_processor_definition::HintProcessorLogic, }, relocatable, - types::exec_scope::ExecutionScopes, utils::test_utils::*, vm::errors::memory_errors::MemoryError, }; diff --git a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index b23f58635b..3120b91d03 100644 --- a/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/vm/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -5,6 +5,7 @@ use super::{ ec_recover_sub_a_b, }, field_arithmetic::{u256_get_square_root, u384_get_square_root, uint384_div}, + mod_circuit::{run_p_mod_circuit, run_p_mod_circuit_with_large_batch_size}, secp::{ ec_utils::{ compute_doubling_slope_external_consts, compute_slope_and_assing_secp_p, @@ -57,7 +58,7 @@ use crate::{ math_utils::*, memcpy_hint_utils::{add_segment, enter_scope, exit_scope, memcpy_enter_scope}, memset_utils::{memset_enter_scope, memset_step_loop}, - poseidon_utils::{n_greater_than_10, n_greater_than_2}, + poseidon_utils::{elements_over_x, n_greater_than_10, n_greater_than_2}, pow_utils::pow, secp::{ bigint_utils::{bigint_to_uint256, hi_max_bitlen, nondet_bigint3}, @@ -118,6 +119,9 @@ use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_n #[cfg(feature = "print")] use crate::hint_processor::builtin_hint_processor::print::{print_array, print_dict, print_felt}; +use crate::hint_processor::builtin_hint_processor::secp::secp_utils::{ + SECP256R1_ALPHA, SECP256R1_P, +}; use super::blake2s_utils::example_blake2s_compress; @@ -531,6 +535,15 @@ impl HintProcessorLogic for BuiltinHintProcessor { &SECP_P, &ALPHA, ), + hint_code::EC_DOUBLE_SLOPE_V4 => compute_doubling_slope( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + "point", + &SECP256R1_P, + &SECP256R1_ALPHA, + ), hint_code::EC_DOUBLE_SLOPE_EXTERNAL_CONSTS => compute_doubling_slope_external_consts( vm, exec_scopes, @@ -558,13 +571,23 @@ impl HintProcessorLogic for BuiltinHintProcessor { "point1", &SECP_P_V2, ), - hint_code::COMPUTE_SLOPE_SECP256R1 => compute_slope( + hint_code::COMPUTE_SLOPE_SECP256R1_V1 => compute_slope( vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking, "point0", "point1", + "SECP_P", + ), + hint_code::COMPUTE_SLOPE_SECP256R1_V2 => compute_slope( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + "point0", + "point1", + "SECP256R1_P", ), hint_code::IMPORT_SECP256R1_P => import_secp256r1_p(exec_scopes), hint_code::COMPUTE_SLOPE_WHITELIST => compute_slope_and_assing_secp_p( @@ -731,6 +754,12 @@ impl HintProcessorLogic for BuiltinHintProcessor { hint_code::NONDET_N_GREATER_THAN_2 => { n_greater_than_2(vm, &hint_data.ids_data, &hint_data.ap_tracking) } + hint_code::NONDET_ELEMENTS_OVER_TEN => { + elements_over_x(vm, &hint_data.ids_data, &hint_data.ap_tracking, 10) + } + hint_code::NONDET_ELEMENTS_OVER_TWO => { + elements_over_x(vm, &hint_data.ids_data, &hint_data.ap_tracking, 2) + } hint_code::RANDOM_EC_POINT => { random_ec_point_hint(vm, &hint_data.ids_data, &hint_data.ap_tracking) } @@ -816,6 +845,17 @@ impl HintProcessorLogic for BuiltinHintProcessor { } hint_code::EC_RECOVER_PRODUCT_DIV_M => ec_recover_product_div_m(exec_scopes), hint_code::SPLIT_XX => split_xx(vm, &hint_data.ids_data, &hint_data.ap_tracking), + hint_code::RUN_P_CIRCUIT => { + run_p_mod_circuit(vm, &hint_data.ids_data, &hint_data.ap_tracking) + } + hint_code::RUN_P_CIRCUIT_WITH_LARGE_BATCH_SIZE => { + run_p_mod_circuit_with_large_batch_size( + vm, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ) + } #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), #[cfg(feature = "print")] diff --git a/vm/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs b/vm/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs index 0beea39ee9..202cba60a0 100644 --- a/vm/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs +++ b/vm/src/hint_processor/builtin_hint_processor/cairo_keccak/keccak_hints.rs @@ -375,7 +375,7 @@ mod tests { }, hint_processor_definition::{HintProcessorLogic, HintReference}, }, - types::{exec_scope::ExecutionScopes, relocatable::Relocatable}, + types::relocatable::Relocatable, utils::test_utils::*, vm::vm_core::VirtualMachine, }; diff --git a/vm/src/hint_processor/builtin_hint_processor/ec_utils.rs b/vm/src/hint_processor/builtin_hint_processor/ec_utils.rs index e3f29091e8..2b8b0e7685 100644 --- a/vm/src/hint_processor/builtin_hint_processor/ec_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/ec_utils.rs @@ -62,7 +62,7 @@ pub fn random_ec_point_hint( ) -> Result<(), HintError> { let p = EcPoint::from_var_name("p", vm, ids_data, ap_tracking)?; let q = EcPoint::from_var_name("q", vm, ids_data, ap_tracking)?; - let m = get_integer_from_var_name("m", vm, ids_data, ap_tracking)?; + let m = Cow::Owned(get_integer_from_var_name("m", vm, ids_data, ap_tracking)?); let bytes: Vec = [p.x, p.y, m, q.x, q.y] .iter() .flat_map(|x| x.to_bytes_be()) @@ -109,7 +109,7 @@ pub fn chained_ec_op_random_ec_point_hint( ) -> Result<(), HintError> { let n_elms = get_integer_from_var_name("len", vm, ids_data, ap_tracking)?; if n_elms.is_zero() || n_elms.to_usize().is_none() { - return Err(HintError::InvalidLenValue(Box::new(n_elms.into_owned()))); + return Err(HintError::InvalidLenValue(Box::new(n_elms))); } let n_elms = n_elms.to_usize().unwrap(); let p = EcPoint::from_var_name("p", vm, ids_data, ap_tracking)?; @@ -141,7 +141,7 @@ pub fn recover_y_hint( ids_data: &HashMap, ap_tracking: &ApTracking, ) -> Result<(), HintError> { - let p_x = get_integer_from_var_name("x", vm, ids_data, ap_tracking)?.into_owned(); + let p_x = get_integer_from_var_name("x", vm, ids_data, ap_tracking)?; let p_addr = get_relocatable_from_var_name("p", vm, ids_data, ap_tracking)?; vm.insert_value(p_addr, p_x)?; let p_y = Felt252::from( @@ -220,7 +220,6 @@ mod tests { use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData; use crate::hint_processor::hint_processor_definition::HintProcessorLogic; use crate::relocatable; - use crate::types::exec_scope::ExecutionScopes; use crate::types::relocatable::Relocatable; use num_traits::Zero; diff --git a/vm/src/hint_processor/builtin_hint_processor/field_arithmetic.rs b/vm/src/hint_processor/builtin_hint_processor/field_arithmetic.rs index 6f5430a387..9c4ce28fe3 100644 --- a/vm/src/hint_processor/builtin_hint_processor/field_arithmetic.rs +++ b/vm/src/hint_processor/builtin_hint_processor/field_arithmetic.rs @@ -274,7 +274,6 @@ mod tests { }, hint_processor_definition::HintProcessorLogic, }, - types::exec_scope::ExecutionScopes, utils::test_utils::*, vm::vm_core::VirtualMachine, }; diff --git a/vm/src/hint_processor/builtin_hint_processor/find_element_hint.rs b/vm/src/hint_processor/builtin_hint_processor/find_element_hint.rs index ea00247250..b1be97bb82 100644 --- a/vm/src/hint_processor/builtin_hint_processor/find_element_hint.rs +++ b/vm/src/hint_processor/builtin_hint_processor/find_element_hint.rs @@ -30,9 +30,7 @@ pub fn find_element( .to_usize() .ok_or_else(|| HintError::ValueOutOfRange(Box::new(*elm_size_bigint.as_ref())))?; if elm_size == 0 { - return Err(HintError::ValueOutOfRange(Box::new( - elm_size_bigint.into_owned(), - ))); + return Err(HintError::ValueOutOfRange(Box::new(elm_size_bigint))); } if let Some(find_element_index_value) = find_element_index { @@ -44,7 +42,7 @@ pub fn find_element( if found_key.as_ref() != key.as_ref() { return Err(HintError::InvalidIndex(Box::new(( find_element_index_value, - key.into_owned(), + key, found_key.into_owned(), )))); } @@ -56,13 +54,13 @@ pub fn find_element( if n_elms.as_ref() > find_element_max_size { return Err(HintError::FindElemMaxSize(Box::new(( *find_element_max_size, - n_elms.into_owned(), + n_elms, )))); } } let n_elms_iter: u32 = n_elms .to_u32() - .ok_or_else(|| MathError::Felt252ToI32Conversion(Box::new(n_elms.into_owned())))?; + .ok_or_else(|| MathError::Felt252ToI32Conversion(Box::new(n_elms)))?; for i in 0..n_elms_iter { let iter_key = vm @@ -80,9 +78,7 @@ pub fn find_element( } } - Err(HintError::NoValueForKeyFindElement(Box::new( - key.into_owned(), - ))) + Err(HintError::NoValueForKeyFindElement(Box::new(key))) } } @@ -93,10 +89,10 @@ pub fn search_sorted_lower( ap_tracking: &ApTracking, ) -> Result<(), HintError> { let find_element_max_size = exec_scopes.get::("find_element_max_size"); - let n_elms = *get_integer_from_var_name("n_elms", vm, ids_data, ap_tracking)?; + let n_elms = get_integer_from_var_name("n_elms", vm, ids_data, ap_tracking)?; let rel_array_ptr = get_relocatable_from_var_name("array_ptr", vm, ids_data, ap_tracking)?; - let elm_size = *get_integer_from_var_name("elm_size", vm, ids_data, ap_tracking)?; - let key = *get_integer_from_var_name("key", vm, ids_data, ap_tracking)?; + let elm_size = get_integer_from_var_name("elm_size", vm, ids_data, ap_tracking)?; + let key = get_integer_from_var_name("key", vm, ids_data, ap_tracking)?; if elm_size == Felt252::ZERO { return Err(HintError::ValueOutOfRange(Box::new(elm_size))); diff --git a/vm/src/hint_processor/builtin_hint_processor/garaga.rs b/vm/src/hint_processor/builtin_hint_processor/garaga.rs index 37f5a33025..7c1322fff9 100644 --- a/vm/src/hint_processor/builtin_hint_processor/garaga.rs +++ b/vm/src/hint_processor/builtin_hint_processor/garaga.rs @@ -29,7 +29,6 @@ mod tests { use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData; use crate::hint_processor::hint_processor_definition::HintProcessorLogic; - use crate::types::exec_scope::ExecutionScopes; use crate::Felt252; use crate::{hint_processor::builtin_hint_processor::hint_code, utils::test_utils::*}; diff --git a/vm/src/hint_processor/builtin_hint_processor/hint_code.rs b/vm/src/hint_processor/builtin_hint_processor/hint_code.rs index 77365943f8..03bab59189 100644 --- a/vm/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/vm/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -694,6 +694,15 @@ x = pack(ids.pt.x, PRIME) y = pack(ids.pt.y, PRIME) value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P)"#; +pub const EC_DOUBLE_SLOPE_V4: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_ALPHA, SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack +from starkware.python.math_utils import ec_double_slope + +# Compute the slope. +x = pack(ids.point.x, SECP256R1_P) +y = pack(ids.point.y, SECP256R1_P) +value = slope = ec_double_slope(point=(x, y), alpha=SECP256R1_ALPHA, p=SECP256R1_P)"#; + pub const EC_DOUBLE_SLOPE_EXTERNAL_CONSTS: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import ec_double_slope @@ -722,7 +731,7 @@ x1 = pack(ids.point1.x, PRIME) y1 = pack(ids.point1.y, PRIME) value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P)"#; -pub const COMPUTE_SLOPE_SECP256R1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack +pub const COMPUTE_SLOPE_SECP256R1_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack from starkware.python.math_utils import line_slope # Compute the slope. @@ -732,6 +741,17 @@ x1 = pack(ids.point1.x, PRIME) y1 = pack(ids.point1.y, PRIME) value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P)"#; +pub const COMPUTE_SLOPE_SECP256R1_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack +from starkware.python.math_utils import line_slope + +# Compute the slope. +x0 = pack(ids.point0.x, PRIME) +y0 = pack(ids.point0.y, PRIME) +x1 = pack(ids.point1.x, PRIME) +y1 = pack(ids.point1.y, PRIME) +value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP256R1_P)"#; + pub const IMPORT_SECP256R1_P: &str = "from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P as SECP_P"; @@ -1421,3 +1441,12 @@ data = __dict_manager.get_dict(ids.dict_ptr) print( {k: v if isinstance(v, int) else [memory[v + i] for i in range(ids.pointer_size)] for k, v in data.items()} )"#; + +pub const RUN_P_CIRCUIT: &str = "from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner\nassert builtin_runners[\"add_mod_builtin\"].instance_def.batch_size == 1\nassert builtin_runners[\"mul_mod_builtin\"].instance_def.batch_size == 1\n\nModBuiltinRunner.fill_memory(\n memory=memory,\n add_mod=(ids.add_mod_ptr.address_, builtin_runners[\"add_mod_builtin\"], ids.add_mod_n),\n mul_mod=(ids.mul_mod_ptr.address_, builtin_runners[\"mul_mod_builtin\"], ids.mul_mod_n),\n)"; + +pub const RUN_P_CIRCUIT_WITH_LARGE_BATCH_SIZE: &str = "from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner\nassert builtin_runners[\"add_mod_builtin\"].instance_def.batch_size == ids.BATCH_SIZE\nassert builtin_runners[\"mul_mod_builtin\"].instance_def.batch_size == ids.BATCH_SIZE\n\nModBuiltinRunner.fill_memory(\n memory=memory,\n add_mod=(ids.add_mod_ptr.address_, builtin_runners[\"add_mod_builtin\"], ids.add_mod_n),\n mul_mod=(ids.mul_mod_ptr.address_, builtin_runners[\"mul_mod_builtin\"], ids.mul_mod_n),\n)"; + +pub const NONDET_ELEMENTS_OVER_TEN: &str = + "memory[ap] = to_felt_or_relocatable(ids.elements_end - ids.elements >= 10)"; +pub const NONDET_ELEMENTS_OVER_TWO: &str = + "memory[ap] = to_felt_or_relocatable(ids.elements_end - ids.elements >= 2)"; diff --git a/vm/src/hint_processor/builtin_hint_processor/hint_utils.rs b/vm/src/hint_processor/builtin_hint_processor/hint_utils.rs index 3a1808b5a0..07eef72a88 100644 --- a/vm/src/hint_processor/builtin_hint_processor/hint_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/hint_utils.rs @@ -1,4 +1,4 @@ -use crate::stdlib::{borrow::Cow, boxed::Box, collections::HashMap, prelude::*}; +use crate::stdlib::{boxed::Box, collections::HashMap, prelude::*}; use crate::Felt252; @@ -83,12 +83,12 @@ pub fn get_relocatable_from_var_name( //Gets the value of a variable name. //If the value is an MaybeRelocatable::Int(Bigint) return &Bigint //else raises Err -pub fn get_integer_from_var_name<'a>( - var_name: &'a str, - vm: &'a VirtualMachine, - ids_data: &'a HashMap, +pub fn get_integer_from_var_name( + var_name: &str, + vm: &VirtualMachine, + ids_data: &HashMap, ap_tracking: &ApTracking, -) -> Result, HintError> { +) -> Result { let reference = get_reference_from_var_name(var_name, ids_data)?; match get_integer_from_reference(vm, reference, ap_tracking) { // Map internal errors into more descriptive variants @@ -260,7 +260,7 @@ mod tests { assert_matches!( get_integer_from_var_name("value", &vm, &ids_data, &ApTracking::new()), - Ok(Cow::Borrowed(x)) if x == &Felt252::from(1) + Ok(x) if x == Felt252::from(1) ); } diff --git a/vm/src/hint_processor/builtin_hint_processor/keccak_utils.rs b/vm/src/hint_processor/builtin_hint_processor/keccak_utils.rs index 8a22530f9b..69b593e36f 100644 --- a/vm/src/hint_processor/builtin_hint_processor/keccak_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/keccak_utils.rs @@ -56,7 +56,7 @@ pub fn unsafe_keccak( if let Ok(keccak_max_size) = exec_scopes.get::("__keccak_max_size") { if length.as_ref() > &keccak_max_size { return Err(HintError::KeccakMaxSize(Box::new(( - length.into_owned(), + length, keccak_max_size, )))); } @@ -71,7 +71,7 @@ pub fn unsafe_keccak( // transform to u64 to make ranges cleaner in the for loop below let u64_length = length .to_u64() - .ok_or_else(|| HintError::InvalidKeccakInputLength(Box::new(length.into_owned())))?; + .ok_or_else(|| HintError::InvalidKeccakInputLength(Box::new(length)))?; const ZEROES: [u8; 32] = [0u8; 32]; let mut keccak_input = Vec::new(); @@ -243,9 +243,8 @@ pub fn split_n_bytes( ) -> Result<(), HintError> { let n_bytes = get_integer_from_var_name("n_bytes", vm, ids_data, ap_tracking).and_then(|x| { - x.to_u64().ok_or_else(|| { - HintError::Math(MathError::Felt252ToU64Conversion(Box::new(x.into_owned()))) - }) + x.to_u64() + .ok_or_else(|| HintError::Math(MathError::Felt252ToU64Conversion(Box::new(x)))) })?; let bytes_in_word = constants .get(BYTES_IN_WORD) @@ -298,7 +297,6 @@ mod tests { }, hint_processor_definition::{HintProcessorLogic, HintReference}, }, - types::exec_scope::ExecutionScopes, utils::test_utils::*, vm::vm_core::VirtualMachine, }; diff --git a/vm/src/hint_processor/builtin_hint_processor/math_utils.rs b/vm/src/hint_processor/builtin_hint_processor/math_utils.rs index 80c326394b..a0fa71dddb 100644 --- a/vm/src/hint_processor/builtin_hint_processor/math_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/math_utils.rs @@ -45,13 +45,9 @@ pub fn is_nn( ap_tracking: &ApTracking, ) -> Result<(), HintError> { let a = get_integer_from_var_name("a", vm, ids_data, ap_tracking)?; - let range_check_builtin = vm.get_range_check_builtin()?; + let range_check_bound = vm.get_range_check_builtin()?.bound(); //Main logic (assert a is not negative and within the expected range) - let value = match &range_check_builtin._bound { - Some(bound) if a.as_ref() >= bound => Felt252::ONE, - _ => Felt252::ZERO, - }; - insert_value_into_ap(vm, value) + insert_value_into_ap(vm, Felt252::from(a.as_ref() >= range_check_bound)) } //Implements hint: memory[ap] = 0 if 0 <= ((-ids.a - 1) % PRIME) < range_check_builtin.bound else 1 @@ -62,15 +58,9 @@ pub fn is_nn_out_of_range( ) -> Result<(), HintError> { let a = get_integer_from_var_name("a", vm, ids_data, ap_tracking)?; let a = a.as_ref(); - let range_check_builtin = vm.get_range_check_builtin()?; + let range_check_bound = vm.get_range_check_builtin()?.bound(); //Main logic (assert a is not negative and within the expected range) - //let value = if (-a - 1usize).mod_floor(vm.get_prime()) < range_check_builtin._bound { - let value = match &range_check_builtin._bound { - Some(bound) if Felt252::ZERO - (a + 1u64) < *bound => Felt252::ZERO, - None => Felt252::ZERO, - _ => Felt252::ONE, - }; - insert_value_into_ap(vm, value) + insert_value_into_ap(vm, Felt252::from(-(a + 1) >= *range_check_bound)) } /* Implements hint:from starkware.cairo.common.math_utils import assert_integer %{ @@ -161,11 +151,8 @@ pub fn assert_le_felt_v_0_6( let a = &get_integer_from_var_name("a", vm, ids_data, ap_tracking)?; let b = &get_integer_from_var_name("b", vm, ids_data, ap_tracking)?; - if a.as_ref() > b.as_ref() { - return Err(HintError::NonLeFelt252(Box::new(( - a.clone().into_owned(), - b.clone().into_owned(), - )))); + if a > b { + return Err(HintError::NonLeFelt252(Box::new((*a, *b)))); } Ok(()) } @@ -178,15 +165,11 @@ pub fn assert_le_felt_v_0_8( let a = &get_integer_from_var_name("a", vm, ids_data, ap_tracking)?; let b = &get_integer_from_var_name("b", vm, ids_data, ap_tracking)?; - if a.as_ref() > b.as_ref() { - return Err(HintError::NonLeFelt252(Box::new(( - a.clone().into_owned(), - b.clone().into_owned(), - )))); + if a > b { + return Err(HintError::NonLeFelt252(Box::new((*a, *b)))); } - let bound = vm.get_range_check_builtin()?._bound.unwrap_or_default(); - let small_inputs = - Felt252::from((a.as_ref() < &bound && b.as_ref() - a.as_ref() < bound) as u8); + let bound = vm.get_range_check_builtin()?.bound(); + let small_inputs = Felt252::from((a < bound && b - a < *bound) as u8); insert_value_from_var_name("small_inputs", small_inputs, vm, ids_data, ap_tracking) } @@ -299,11 +282,10 @@ pub fn assert_nn( let range_check_builtin = vm.get_range_check_builtin()?; // assert 0 <= ids.a % PRIME < range_check_builtin.bound // as prime > 0, a % prime will always be > 0 - match &range_check_builtin._bound { - Some(bound) if a.as_ref() >= bound => { - Err(HintError::AssertNNValueOutOfRange(Box::new(a.into_owned()))) - } - _ => Ok(()), + if a.as_ref() >= range_check_builtin.bound() { + Err(HintError::AssertNNValueOutOfRange(Box::new(a))) + } else { + Ok(()) } } @@ -321,7 +303,7 @@ pub fn assert_not_zero( let value = get_integer_from_var_name("value", vm, ids_data, ap_tracking)?; if value.is_zero() { return Err(HintError::AssertNotZero(Box::new(( - value.into_owned(), + value, crate::utils::PRIME_STR.to_string(), )))); }; @@ -375,20 +357,15 @@ pub fn is_positive( ap_tracking: &ApTracking, ) -> Result<(), HintError> { let value = get_integer_from_var_name("value", vm, ids_data, ap_tracking)?; - let value_as_int = signed_felt(*value); + let value_as_int = signed_felt(value); let range_check_builtin = vm.get_range_check_builtin()?; // Avoid using abs so we don't allocate a new BigInt let (sign, abs_value) = value_as_int.into_parts(); //Main logic (assert a is positive) - match &range_check_builtin._bound { - Some(bound) if abs_value > bound.to_biguint() => { - return Err(HintError::ValueOutsideValidRange(Box::new( - value.into_owned(), - ))) - } - _ => {} - }; + if abs_value >= range_check_builtin.bound().to_biguint() { + return Err(HintError::ValueOutsideValidRange(Box::new(value))); + } let result = Felt252::from((sign == Sign::Plus) as u8); insert_value_from_var_name("is_positive", result, vm, ids_data, ap_tracking) @@ -447,13 +424,10 @@ pub fn sqrt( ) -> Result<(), HintError> { let mod_value = get_integer_from_var_name("value", vm, ids_data, ap_tracking)?; //This is equal to mod_value > Felt252::from(2).pow(250) - if *mod_value > pow2_const(250) { - return Err(HintError::ValueOutside250BitRange(Box::new( - mod_value.into_owned(), - ))); + if mod_value > pow2_const(250) { + return Err(HintError::ValueOutside250BitRange(Box::new(mod_value))); //This is equal to mod_value > bigint!(2).pow(250) } - #[allow(deprecated)] insert_value_from_var_name( "root", Felt252::from(&isqrt(&mod_value.to_biguint())?), @@ -474,17 +448,14 @@ pub fn signed_div_rem( let bound = get_integer_from_var_name("bound", vm, ids_data, ap_tracking)?; let builtin = vm.get_range_check_builtin()?; - let builtin_bound = &builtin._bound.unwrap_or(Felt252::MAX); + let builtin_bound = builtin.bound(); if div.is_zero() || div.as_ref() > &div_prime_by_bound(*builtin_bound)? { - return Err(HintError::OutOfValidRange(Box::new(( - div.into_owned(), - *builtin_bound, - )))); + return Err(HintError::OutOfValidRange(Box::new((div, *builtin_bound)))); } let builtin_bound_div_2 = builtin_bound.field_div(&Felt252::TWO.try_into().unwrap()); - if *bound > builtin_bound_div_2 { + if bound > builtin_bound_div_2 { return Err(HintError::OutOfValidRange(Box::new(( - bound.into_owned(), + bound, builtin_bound_div_2, )))); } @@ -497,7 +468,7 @@ pub fn signed_div_rem( if int_bound.abs() < q.abs() { return Err(HintError::OutOfValidRange(Box::new(( Felt252::from(&q), - bound.into_owned(), + bound, )))); } @@ -528,28 +499,14 @@ pub fn unsigned_div_rem( ) -> Result<(), HintError> { let div = get_integer_from_var_name("div", vm, ids_data, ap_tracking)?; let value = get_integer_from_var_name("value", vm, ids_data, ap_tracking)?; - let builtin = vm.get_range_check_builtin()?; + let builtin_bound = vm.get_range_check_builtin()?.bound(); // Main logic - match &builtin._bound { - Some(builtin_bound) - if div.is_zero() || div.as_ref() > &div_prime_by_bound(*builtin_bound)? => - { - return Err(HintError::OutOfValidRange(Box::new(( - div.into_owned(), - *builtin_bound, - )))); - } - None if div.is_zero() => { - return Err(HintError::OutOfValidRange(Box::new(( - div.into_owned(), - Felt252::ZERO - Felt252::ONE, - )))); - } - _ => {} + if div.is_zero() || div.as_ref() > &div_prime_by_bound(*builtin_bound)? { + return Err(HintError::OutOfValidRange(Box::new((div, *builtin_bound)))); } - let (q, r) = value.div_rem(&(*div).try_into().map_err(|_| MathError::DividedByZero)?); + let (q, r) = value.div_rem(&(div).try_into().map_err(|_| MathError::DividedByZero)?); insert_value_from_var_name("r", r, vm, ids_data, ap_tracking)?; insert_value_from_var_name("q", q, vm, ids_data, ap_tracking) } @@ -575,7 +532,7 @@ pub fn assert_250_bit( let shift = constants .get(SHIFT) .map_or_else(|| get_constant_from_var_name("SHIFT", constants), Ok)?; - let value = Felt252::from(&signed_felt(*get_integer_from_var_name( + let value = Felt252::from(&signed_felt(get_integer_from_var_name( "value", vm, ids_data, @@ -676,10 +633,7 @@ pub fn assert_lt_felt( // assert (ids.a % PRIME) < (ids.b % PRIME), \ // f'a = {ids.a % PRIME} is not less than b = {ids.b % PRIME}.' if a >= b { - return Err(HintError::AssertLtFelt252(Box::new(( - a.into_owned(), - b.into_owned(), - )))); + return Err(HintError::AssertLtFelt252(Box::new((a, b)))); }; Ok(()) } @@ -689,7 +643,7 @@ pub fn is_quad_residue( ids_data: &HashMap, ap_tracking: &ApTracking, ) -> Result<(), HintError> { - let x = *get_integer_from_var_name("x", vm, ids_data, ap_tracking)?; + let x = get_integer_from_var_name("x", vm, ids_data, ap_tracking)?; if x.is_zero() || x == Felt252::ONE { insert_value_from_var_name("y", *x.as_ref(), vm, ids_data, ap_tracking) @@ -1830,9 +1784,9 @@ mod tests { //Initialize fp vm.run_context.fp = 6; //Insert ids into memory - let bound = vm.get_range_check_builtin().unwrap()._bound; + let bound = vm.get_range_check_builtin().unwrap().bound(); vm.segments = segments![((1, 3), (5)), ((1, 4), 10)]; - vm.insert_value((1, 5).into(), bound.unwrap()).unwrap(); + vm.insert_value((1, 5).into(), bound).unwrap(); //Create ids let ids_data = ids_data!["r", "biased_q", "range_check_ptr", "div", "value", "bound"]; //Execute the hint @@ -1840,7 +1794,7 @@ mod tests { assert_matches!( run_hint!(vm, ids_data, hint_code), Err(HintError::OutOfValidRange(bx)) - if *bx == (bound.unwrap(), builtin_bound.field_div(&Felt252::TWO.try_into().unwrap())) + if *bx == (*bound, builtin_bound.field_div(&Felt252::TWO.try_into().unwrap())) ) } diff --git a/vm/src/hint_processor/builtin_hint_processor/memcpy_hint_utils.rs b/vm/src/hint_processor/builtin_hint_processor/memcpy_hint_utils.rs index 4709545bbc..2e9b4c55e4 100644 --- a/vm/src/hint_processor/builtin_hint_processor/memcpy_hint_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/memcpy_hint_utils.rs @@ -36,8 +36,7 @@ pub fn memcpy_enter_scope( ids_data: &HashMap, ap_tracking: &ApTracking, ) -> Result<(), HintError> { - let len: Box = - Box::new(get_integer_from_var_name("len", vm, ids_data, ap_tracking)?.into_owned()); + let len: Box = Box::new(get_integer_from_var_name("len", vm, ids_data, ap_tracking)?); exec_scopes.enter_scope(HashMap::from([(String::from("n"), len)])); Ok(()) } diff --git a/vm/src/hint_processor/builtin_hint_processor/memset_utils.rs b/vm/src/hint_processor/builtin_hint_processor/memset_utils.rs index 4ad693968c..066884f920 100644 --- a/vm/src/hint_processor/builtin_hint_processor/memset_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/memset_utils.rs @@ -21,8 +21,7 @@ pub fn memset_enter_scope( ids_data: &HashMap, ap_tracking: &ApTracking, ) -> Result<(), HintError> { - let n: Box = - Box::new(get_integer_from_var_name("n", vm, ids_data, ap_tracking)?.into_owned()); + let n: Box = Box::new(get_integer_from_var_name("n", vm, ids_data, ap_tracking)?); exec_scopes.enter_scope(HashMap::from([(String::from("n"), n)])); Ok(()) } diff --git a/vm/src/hint_processor/builtin_hint_processor/mod.rs b/vm/src/hint_processor/builtin_hint_processor/mod.rs index 8236f8a866..a890ad6ebe 100644 --- a/vm/src/hint_processor/builtin_hint_processor/mod.rs +++ b/vm/src/hint_processor/builtin_hint_processor/mod.rs @@ -16,6 +16,7 @@ pub mod keccak_utils; pub mod math_utils; pub mod memcpy_hint_utils; pub mod memset_utils; +mod mod_circuit; pub mod poseidon_utils; pub mod pow_utils; #[cfg(feature = "print")] diff --git a/vm/src/hint_processor/builtin_hint_processor/mod_circuit.rs b/vm/src/hint_processor/builtin_hint_processor/mod_circuit.rs new file mode 100644 index 0000000000..22fbc57d19 --- /dev/null +++ b/vm/src/hint_processor/builtin_hint_processor/mod_circuit.rs @@ -0,0 +1,93 @@ +use crate::stdlib::prelude::String; +use crate::{ + hint_processor::hint_processor_definition::HintReference, + serde::deserialize_program::ApTracking, + stdlib::collections::HashMap, + vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, + Felt252, +}; +#[cfg(not(feature = "mod_builtin"))] +use crate::{stdlib::prelude::Box, types::errors::math_errors::MathError}; +use num_traits::ToPrimitive; + +use super::hint_utils::{get_integer_from_var_name, get_ptr_from_var_name}; +/* Implements Hint: +%{ + from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner + assert builtin_runners["add_mod_builtin"].instance_def.batch_size == 1 + assert builtin_runners["mul_mod_builtin"].instance_def.batch_size == 1 + + ModBuiltinRunner.fill_memory( + memory=memory, + add_mod=(ids.add_mod_ptr.address_, builtin_runners["add_mod_builtin"], ids.add_mod_n), + mul_mod=(ids.mul_mod_ptr.address_, builtin_runners["mul_mod_builtin"], ids.mul_mod_n), + ) +%} +*/ +pub fn run_p_mod_circuit( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + run_p_mod_circuit_inner(vm, ids_data, ap_tracking, 1) +} + +/* Implements Hint: + %{ + from starkware.cairo.lang.builtins.modulo.mod_builtin_runner import ModBuiltinRunner + assert builtin_runners["add_mod_builtin"].instance_def.batch_size == ids.BATCH_SIZE + assert builtin_runners["mul_mod_builtin"].instance_def.batch_size == ids.BATCH_SIZE + + ModBuiltinRunner.fill_memory( + memory=memory, + add_mod=(ids.add_mod_ptr.address_, builtin_runners["add_mod_builtin"], ids.add_mod_n), + mul_mod=(ids.mul_mod_ptr.address_, builtin_runners["mul_mod_builtin"], ids.mul_mod_n), + ) + %} +*/ +#[allow(unused_variables)] +pub fn run_p_mod_circuit_with_large_batch_size( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, + constants: &HashMap, +) -> Result<(), HintError> { + #[cfg(not(feature = "mod_builtin"))] + const LARGE_BATCH_SIZE_PATH: &str = + "starkware.cairo.common.modulo.run_mod_p_circuit_with_large_batch_size.BATCH_SIZE"; + #[cfg(not(feature = "mod_builtin"))] + let batch_size = constants + .get(LARGE_BATCH_SIZE_PATH) + .ok_or_else(|| HintError::MissingConstant(Box::new(LARGE_BATCH_SIZE_PATH)))?; + #[cfg(not(feature = "mod_builtin"))] + let batch_size = batch_size + .to_usize() + .ok_or_else(|| MathError::Felt252ToUsizeConversion(Box::new(*batch_size)))?; + #[cfg(feature = "mod_builtin")] + let batch_size = 8; // Hardcoded here as we are not importing from the common lib yet + run_p_mod_circuit_inner(vm, ids_data, ap_tracking, batch_size) +} + +pub fn run_p_mod_circuit_inner( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, + batch_size: usize, +) -> Result<(), HintError> { + let add_mod_ptr = get_ptr_from_var_name("add_mod_ptr", vm, ids_data, ap_tracking)?; + let mul_mod_ptr = get_ptr_from_var_name("mul_mod_ptr", vm, ids_data, ap_tracking)?; + let add_mod_n = get_integer_from_var_name("add_mod_n", vm, ids_data, ap_tracking)? + .as_ref() + .to_usize() + .unwrap(); + let mul_mod_n = get_integer_from_var_name("mul_mod_n", vm, ids_data, ap_tracking)? + .as_ref() + .to_usize() + .unwrap(); + vm.mod_builtin_fill_memory( + Some((add_mod_ptr, add_mod_n)), + Some((mul_mod_ptr, mul_mod_n)), + Some(batch_size), + ) + .map_err(HintError::Internal) +} diff --git a/vm/src/hint_processor/builtin_hint_processor/poseidon_utils.rs b/vm/src/hint_processor/builtin_hint_processor/poseidon_utils.rs index 7ba388fa7b..1cc19daa4d 100644 --- a/vm/src/hint_processor/builtin_hint_processor/poseidon_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/poseidon_utils.rs @@ -8,7 +8,7 @@ use crate::{ vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; -use super::hint_utils::{get_integer_from_var_name, insert_value_into_ap}; +use super::hint_utils::{get_integer_from_var_name, get_ptr_from_var_name, insert_value_into_ap}; use num_traits::ToPrimitive; // Implements hint: "memory[ap] = to_felt_or_relocatable(ids.n >= 10)" @@ -37,6 +37,19 @@ pub fn n_greater_than_2( insert_value_into_ap(vm, value) } +// Implements hint: "memory[ap] = to_felt_or_relocatable(ids.elements_end - ids.elements >= x)" +pub fn elements_over_x( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, + x: usize, +) -> Result<(), HintError> { + let elements_end = get_ptr_from_var_name("elements_end", vm, ids_data, ap_tracking)?; + let elements = get_ptr_from_var_name("elements", vm, ids_data, ap_tracking)?; + let value = Felt252::from(((elements_end - elements)? >= x) as usize); + insert_value_into_ap(vm, value) +} + #[cfg(test)] mod tests { use crate::any_box; @@ -44,8 +57,6 @@ mod tests { use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData; use crate::hint_processor::hint_processor_definition::HintProcessorLogic; use crate::hint_processor::hint_processor_definition::HintReference; - use crate::stdlib::collections::HashMap; - use crate::types::exec_scope::ExecutionScopes; use crate::vm::vm_core::VirtualMachine; use crate::{hint_processor::builtin_hint_processor::hint_code, utils::test_utils::*}; diff --git a/vm/src/hint_processor/builtin_hint_processor/pow_utils.rs b/vm/src/hint_processor/builtin_hint_processor/pow_utils.rs index a749f4b896..0a31277a18 100644 --- a/vm/src/hint_processor/builtin_hint_processor/pow_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/pow_utils.rs @@ -45,7 +45,7 @@ mod tests { builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData, hint_processor_definition::HintProcessorLogic, }, - types::{exec_scope::ExecutionScopes, relocatable::MaybeRelocatable}, + types::relocatable::MaybeRelocatable, utils::test_utils::*, vm::{errors::memory_errors::MemoryError, vm_core::VirtualMachine}, }; diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs b/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs index d4dbc3b910..fdafd49e07 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -198,6 +198,7 @@ pub fn compute_slope_and_assing_secp_p( ap_tracking, point0_alias, point1_alias, + "SECP_P", ) } @@ -208,13 +209,14 @@ pub fn compute_slope( ap_tracking: &ApTracking, point0_alias: &str, point1_alias: &str, + secp_p_name: &str, ) -> Result<(), HintError> { //ids.point0 let point0 = EcPoint::from_var_name(point0_alias, vm, ids_data, ap_tracking)?; //ids.point1 let point1 = EcPoint::from_var_name(point1_alias, vm, ids_data, ap_tracking)?; - let secp_p: BigInt = exec_scopes.get("SECP_P")?; + let secp_p: BigInt = exec_scopes.get(secp_p_name)?; let value = line_slope( &(point0.x.pack86(), point0.y.pack86()), diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs b/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs index 7f34ea4436..4457c975b3 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/secp_utils.rs @@ -106,20 +106,17 @@ mod tests { constants.insert(BASE_86.to_string(), crate::math_utils::pow2_const(86)); let array_1 = bigint3_split(&BigUint::zero()); - #[allow(deprecated)] let array_2 = bigint3_split( &bigint!(999992) .to_biguint() .expect("Couldn't convert to BigUint"), ); - #[allow(deprecated)] let array_3 = bigint3_split( &bigint_str!("7737125245533626718119526477371252455336267181195264773712524553362") .to_biguint() .expect("Couldn't convert to BigUint"), ); //TODO, Check SecpSplitutOfRange limit - #[allow(deprecated)] let array_4 = bigint3_split( &bigint_str!( "773712524553362671811952647737125245533626718119526477371252455336267181195264" diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs b/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs index aee4450cc9..57d72b5bba 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs @@ -109,7 +109,6 @@ pub fn get_point_from_x( constants: &HashMap, ) -> Result<(), HintError> { exec_scopes.insert_value("SECP_P", SECP_P.clone()); - #[allow(deprecated)] let beta = constants .get(BETA) .ok_or_else(|| HintError::MissingConstant(Box::new(BETA)))? @@ -122,7 +121,6 @@ pub fn get_point_from_x( // Divide by 4 let mut y = y_cube_int.modpow(&(&*SECP_P + 1_u32).shr(2_u32), &SECP_P); - #[allow(deprecated)] let v = get_integer_from_var_name("v", vm, ids_data, ap_tracking)?.to_bigint(); if v.is_even() != y.is_even() { y = &*SECP_P - y; diff --git a/vm/src/hint_processor/builtin_hint_processor/segments.rs b/vm/src/hint_processor/builtin_hint_processor/segments.rs index c23c58d3d4..36576973d9 100644 --- a/vm/src/hint_processor/builtin_hint_processor/segments.rs +++ b/vm/src/hint_processor/builtin_hint_processor/segments.rs @@ -57,7 +57,6 @@ mod tests { }, hint_processor_definition::HintProcessorLogic, }, - types::exec_scope::ExecutionScopes, utils::test_utils::*, vm::vm_core::VirtualMachine, }; diff --git a/vm/src/hint_processor/builtin_hint_processor/set.rs b/vm/src/hint_processor/builtin_hint_processor/set.rs index 9d93590c87..86fc4fdadb 100644 --- a/vm/src/hint_processor/builtin_hint_processor/set.rs +++ b/vm/src/hint_processor/builtin_hint_processor/set.rs @@ -23,7 +23,7 @@ pub fn set_add( let elm_size = get_integer_from_var_name("elm_size", vm, ids_data, ap_tracking).and_then(|x| { x.to_usize() - .ok_or_else(|| MathError::Felt252ToUsizeConversion(Box::new(x.into_owned())).into()) + .ok_or_else(|| MathError::Felt252ToUsizeConversion(Box::new(x)).into()) })?; let elm_ptr = get_ptr_from_var_name("elm_ptr", vm, ids_data, ap_tracking)?; let set_end_ptr = get_ptr_from_var_name("set_end_ptr", vm, ids_data, ap_tracking)?; @@ -66,7 +66,7 @@ mod tests { }, hint_processor_definition::HintProcessorLogic, }, - types::{exec_scope::ExecutionScopes, relocatable::MaybeRelocatable}, + types::relocatable::MaybeRelocatable, utils::test_utils::*, vm::vm_core::VirtualMachine, }; diff --git a/vm/src/hint_processor/builtin_hint_processor/sha256_utils.rs b/vm/src/hint_processor/builtin_hint_processor/sha256_utils.rs index 659c752036..a7e77bd42c 100644 --- a/vm/src/hint_processor/builtin_hint_processor/sha256_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/sha256_utils.rs @@ -223,7 +223,6 @@ mod tests { }, hint_processor_definition::{HintProcessorLogic, HintReference}, }, - types::exec_scope::ExecutionScopes, utils::test_utils::*, vm::vm_core::VirtualMachine, }; diff --git a/vm/src/hint_processor/builtin_hint_processor/signature.rs b/vm/src/hint_processor/builtin_hint_processor/signature.rs index 97b03c764d..878a379b7a 100644 --- a/vm/src/hint_processor/builtin_hint_processor/signature.rs +++ b/vm/src/hint_processor/builtin_hint_processor/signature.rs @@ -20,10 +20,8 @@ pub fn verify_ecdsa_signature( ids_data: &HashMap, ap_tracking: &ApTracking, ) -> Result<(), HintError> { - let signature_r = - get_integer_from_var_name("signature_r", vm, ids_data, ap_tracking)?.into_owned(); - let signature_s = - get_integer_from_var_name("signature_s", vm, ids_data, ap_tracking)?.into_owned(); + let signature_r = get_integer_from_var_name("signature_r", vm, ids_data, ap_tracking)?; + let signature_s = get_integer_from_var_name("signature_s", vm, ids_data, ap_tracking)?; let ecdsa_ptr = get_ptr_from_var_name("ecdsa_ptr", vm, ids_data, ap_tracking)?; let ecdsa_builtin = &mut vm.get_signature_builtin()?; if ecdsa_ptr.segment_index != ecdsa_builtin.base() as isize { @@ -54,9 +52,6 @@ mod tests { }, hint_processor_definition::HintProcessorLogic, }, - types::{ - exec_scope::ExecutionScopes, instance_definitions::ecdsa_instance_def::EcdsaInstanceDef, - }, utils::test_utils::*, vm::runners::builtin_runner::SignatureBuiltinRunner, }; @@ -69,8 +64,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn verify_ecdsa_signature_valid() { let mut vm = vm!(); - vm.builtin_runners = - vec![SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true).into()]; + vm.builtin_runners = vec![SignatureBuiltinRunner::new(Some(512), true).into()]; vm.segments = segments![ ((1, 0), (0, 0)), ( @@ -96,8 +90,7 @@ mod tests { #[test] fn verify_ecdsa_signature_invalid_ecdsa_ptr() { let mut vm = vm!(); - vm.builtin_runners = - vec![SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true).into()]; + vm.builtin_runners = vec![SignatureBuiltinRunner::new(Some(512), true).into()]; vm.segments = segments![ ((1, 0), (3, 0)), ( @@ -123,8 +116,7 @@ mod tests { #[test] fn verify_ecdsa_signature_invalid_input_cell() { let mut vm = vm!(); - vm.builtin_runners = - vec![SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true).into()]; + vm.builtin_runners = vec![SignatureBuiltinRunner::new(Some(512), true).into()]; vm.segments = segments![ ((1, 0), (0, 3)), ( diff --git a/vm/src/hint_processor/builtin_hint_processor/squash_dict_utils.rs b/vm/src/hint_processor/builtin_hint_processor/squash_dict_utils.rs index 32ea9cb253..23dc562dae 100644 --- a/vm/src/hint_processor/builtin_hint_processor/squash_dict_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/squash_dict_utils.rs @@ -176,7 +176,7 @@ pub fn squash_dict_inner_used_accesses_assert( if n_used_accesses.as_ref() != &Felt252::from(access_indices_at_key.len()) { return Err(HintError::NumUsedAccessesAssertFail(Box::new(( - n_used_accesses.into_owned(), + n_used_accesses, access_indices_at_key.len(), key, )))); @@ -247,8 +247,7 @@ pub fn squash_dict( let ptr_diff = get_integer_from_var_name("ptr_diff", vm, ids_data, ap_tracking)?; let n_accesses = get_integer_from_var_name("n_accesses", vm, ids_data, ap_tracking)?; //Get range_check_builtin - let range_check_builtin = vm.get_range_check_builtin()?; - let range_check_bound = range_check_builtin._bound; + let range_check_bound = *vm.get_range_check_builtin()?.bound(); //Main Logic let ptr_diff = ptr_diff .to_usize() @@ -260,14 +259,13 @@ pub fn squash_dict( if let Ok(max_size) = squash_dict_max_size { if n_accesses.as_ref() > &max_size { return Err(HintError::SquashDictMaxSizeExceeded(Box::new(( - max_size, - n_accesses.into_owned(), + max_size, n_accesses, )))); }; }; let n_accesses_usize = n_accesses .to_usize() - .ok_or_else(|| HintError::NAccessesTooBig(Box::new(n_accesses.into_owned())))?; + .ok_or_else(|| HintError::NAccessesTooBig(Box::new(n_accesses)))?; //A map from key to the list of indices accessing it. let mut access_indices = HashMap::>::new(); for i in 0..n_accesses_usize { @@ -285,7 +283,7 @@ pub fn squash_dict( keys.sort(); keys.reverse(); //Are the keys used bigger than the range_check bound. - let big_keys = if keys[0] >= range_check_bound.unwrap() { + let big_keys = if keys[0] >= range_check_bound { Felt252::ONE } else { Felt252::ZERO diff --git a/vm/src/hint_processor/builtin_hint_processor/uint256_utils.rs b/vm/src/hint_processor/builtin_hint_processor/uint256_utils.rs index 30fc0d6396..9c91fafa8a 100644 --- a/vm/src/hint_processor/builtin_hint_processor/uint256_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/uint256_utils.rs @@ -507,10 +507,7 @@ mod tests { }, hint_processor_definition::HintProcessorLogic, }, - types::{ - exec_scope::ExecutionScopes, - relocatable::{MaybeRelocatable, Relocatable}, - }, + types::relocatable::{MaybeRelocatable, Relocatable}, utils::test_utils::*, vm::{errors::memory_errors::MemoryError, vm_core::VirtualMachine}, }; diff --git a/vm/src/hint_processor/builtin_hint_processor/uint384.rs b/vm/src/hint_processor/builtin_hint_processor/uint384.rs index c3a9f35683..a973fb9d3d 100644 --- a/vm/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/vm/src/hint_processor/builtin_hint_processor/uint384.rs @@ -252,10 +252,7 @@ mod tests { }, hint_processor_definition::HintProcessorLogic, }, - types::{ - exec_scope::ExecutionScopes, - relocatable::{MaybeRelocatable, Relocatable}, - }, + types::relocatable::{MaybeRelocatable, Relocatable}, utils::test_utils::*, vm::{errors::memory_errors::MemoryError, vm_core::VirtualMachine}, }; diff --git a/vm/src/hint_processor/builtin_hint_processor/uint384_extension.rs b/vm/src/hint_processor/builtin_hint_processor/uint384_extension.rs index 85c3cf6af8..b7ee55bf7d 100644 --- a/vm/src/hint_processor/builtin_hint_processor/uint384_extension.rs +++ b/vm/src/hint_processor/builtin_hint_processor/uint384_extension.rs @@ -73,7 +73,6 @@ mod tests { use crate::hint_processor::builtin_hint_processor::hint_code; use crate::hint_processor::builtin_hint_processor::secp::bigint_utils::Uint768; use crate::hint_processor::hint_processor_definition::HintProcessorLogic; - use crate::types::exec_scope::ExecutionScopes; use crate::utils::test_utils::*; use assert_matches::assert_matches; diff --git a/vm/src/hint_processor/builtin_hint_processor/usort.rs b/vm/src/hint_processor/builtin_hint_processor/usort.rs index f553866fe6..7c22f94fc7 100644 --- a/vm/src/hint_processor/builtin_hint_processor/usort.rs +++ b/vm/src/hint_processor/builtin_hint_processor/usort.rs @@ -43,7 +43,7 @@ pub fn usort_body( if input_len_u64 > usort_max_size { return Err(HintError::UsortOutOfRange(Box::new(( usort_max_size, - input_len.into_owned(), + input_len, )))); } } @@ -98,7 +98,7 @@ pub fn verify_usort( ids_data: &HashMap, ap_tracking: &ApTracking, ) -> Result<(), HintError> { - let value = get_integer_from_var_name("value", vm, ids_data, ap_tracking)?.clone(); + let value = get_integer_from_var_name("value", vm, ids_data, ap_tracking)?; let mut positions = exec_scopes .get_mut_dict_ref::>("positions_dict")? .remove(value.as_ref()) diff --git a/vm/src/hint_processor/builtin_hint_processor/vrf/fq.rs b/vm/src/hint_processor/builtin_hint_processor/vrf/fq.rs index 91ca9e7f8e..c31047d8e0 100644 --- a/vm/src/hint_processor/builtin_hint_processor/vrf/fq.rs +++ b/vm/src/hint_processor/builtin_hint_processor/vrf/fq.rs @@ -123,7 +123,6 @@ mod tests { use crate::hint_processor::builtin_hint_processor::hint_code; use crate::hint_processor::hint_processor_definition::HintProcessorLogic; use crate::types::errors::math_errors::MathError; - use crate::types::exec_scope::ExecutionScopes; use crate::utils::test_utils::*; use assert_matches::assert_matches; diff --git a/vm/src/hint_processor/cairo_1_hint_processor/dict_manager.rs b/vm/src/hint_processor/cairo_1_hint_processor/dict_manager.rs index fe04c1f71a..354e332f03 100644 --- a/vm/src/hint_processor/cairo_1_hint_processor/dict_manager.rs +++ b/vm/src/hint_processor/cairo_1_hint_processor/dict_manager.rs @@ -13,7 +13,6 @@ pub struct DictTrackerExecScope { /// The data of the dictionary. data: HashMap, /// The index of the dictionary in the dict_infos segment. - #[allow(dead_code)] idx: usize, } diff --git a/vm/src/hint_processor/hint_processor_utils.rs b/vm/src/hint_processor/hint_processor_utils.rs index b78c7c9a79..31c60a5ce0 100644 --- a/vm/src/hint_processor/hint_processor_utils.rs +++ b/vm/src/hint_processor/hint_processor_utils.rs @@ -1,4 +1,4 @@ -use crate::stdlib::{borrow::Cow, boxed::Box}; +use crate::stdlib::boxed::Box; use crate::{ serde::deserialize_program::{ApTracking, OffsetValue}, @@ -29,22 +29,26 @@ pub fn insert_value_from_reference( ///Returns the Integer value stored in the given ids variable /// Returns an internal error, users should map it into a more informative type -pub fn get_integer_from_reference<'a>( - vm: &'a VirtualMachine, - hint_reference: &'a HintReference, +pub fn get_integer_from_reference( + vm: &VirtualMachine, + hint_reference: &HintReference, ap_tracking: &ApTracking, -) -> Result, HintError> { - // if the reference register is none, this means it is an immediate value and we - // should return that value. - - if let (OffsetValue::Immediate(int_1), _) = (&hint_reference.offset1, &hint_reference.offset2) { - return Ok(Cow::Borrowed(int_1)); +) -> Result { + // Compute the initial value + let mut val = if let OffsetValue::Immediate(f) = &hint_reference.offset1 { + *f + } else { + let var_addr = compute_addr_from_reference(hint_reference, vm, ap_tracking) + .ok_or(HintError::UnknownIdentifierInternal)?; + vm.get_integer(var_addr) + .map_err(|_| HintError::WrongIdentifierTypeInternal(Box::new(var_addr)))? + .into_owned() + }; + // If offset2 is an immediate, we need to add it's value to the initial value + if let OffsetValue::Immediate(f) = &hint_reference.offset2 { + val += f; } - - let var_addr = compute_addr_from_reference(hint_reference, vm, ap_tracking) - .ok_or(HintError::UnknownIdentifierInternal)?; - vm.get_integer(var_addr) - .map_err(|_| HintError::WrongIdentifierTypeInternal(Box::new(var_addr))) + Ok(val) } ///Returns the Relocatable value stored in the given ids variable @@ -117,7 +121,7 @@ pub fn compute_addr_from_reference( Some((offset1 + value.get_int_ref()?.to_usize()?).ok()?) } OffsetValue::Value(value) => Some((offset1 + *value).ok()?), - _ => None, + _ => Some(offset1), } } @@ -179,7 +183,6 @@ fn get_offset_value_reference( #[cfg(test)] mod tests { use super::*; - use crate::stdlib::collections::HashMap; use crate::{ relocatable, @@ -201,12 +204,31 @@ mod tests { assert_eq!( get_integer_from_reference(&vm, &hint_ref, &ApTracking::new()) - .expect("Unexpected get integer fail") - .into_owned(), + .expect("Unexpected get integer fail"), Felt252::from(2) ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_integer_from_reference_with_immediate_off2() { + let mut vm = vm!(); + vm.segments = segments![((1, 0), 1)]; + let hint_ref = HintReference { + offset1: OffsetValue::Reference(Register::FP, 0, false), + offset2: OffsetValue::Immediate(Felt252::TWO), + dereference: false, + ap_tracking_data: Default::default(), + cairo_type: None, + }; + + assert_eq!( + get_integer_from_reference(&vm, &hint_ref, &ApTracking::new()) + .expect("Unexpected get integer fail"), + Felt252::THREE + ); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_offset_value_reference_valid() { diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 36509273b3..c4cfbd1334 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -5,7 +5,6 @@ //! - `skip_next_instruction_hint`: Enable the `skip_next_instruction()` hint. Not enabled by default. //! - `hooks`: Enable [`Hooks`](crate::vm::hooks::Hooks) support for the [VirtualMachine](vm::vm_core::VirtualMachine). Not enabled by default. //! - `test_utils`: Enables test utils (`hooks` and `skip_next_instruction` features). Not enabled by default. -//! - `with_mimalloc`: Use [`MiMalloc`](https://crates.io/crates/mimalloc) as the program global allocator. //! - `cairo-1-hints`: Enable hints that were introduced in Cairo 1. Not enabled by default. //! - `arbitrary`: Enables implementations of [`arbitrary::Arbitrary`](https://docs.rs/arbitrary/latest/arbitrary/) for some structs. Not enabled by default. @@ -57,6 +56,7 @@ pub mod air_public_input; pub mod cairo_run; pub mod hint_processor; pub mod math_utils; +pub mod program_hash; pub mod serde; pub mod types; pub mod utils; diff --git a/vm/src/math_utils/mod.rs b/vm/src/math_utils/mod.rs index c464cb7228..bd5ff0935d 100644 --- a/vm/src/math_utils/mod.rs +++ b/vm/src/math_utils/mod.rs @@ -196,6 +196,20 @@ pub fn div_mod(n: &BigInt, m: &BigInt, p: &BigInt) -> Result Ok((n * a).mod_floor(p)) } +pub(crate) fn div_mod_unsigned( + n: &BigUint, + m: &BigUint, + p: &BigUint, +) -> Result { + // BigUint to BigInt conversion cannot fail & div_mod will always return a positive value if all values are positive so we can safely unwrap here + div_mod( + &n.to_bigint().unwrap(), + &m.to_bigint().unwrap(), + &p.to_bigint().unwrap(), + ) + .map(|i| i.to_biguint().unwrap()) +} + pub fn ec_add( point_a: (BigInt, BigInt), point_b: (BigInt, BigInt), diff --git a/vm/src/program_hash.rs b/vm/src/program_hash.rs new file mode 100644 index 0000000000..3ff1fa798c --- /dev/null +++ b/vm/src/program_hash.rs @@ -0,0 +1,204 @@ +use starknet_crypto::{pedersen_hash, FieldElement}; + +use crate::Felt252; + +use crate::serde::deserialize_program::BuiltinName; +use crate::stdlib::vec::Vec; +use crate::types::relocatable::MaybeRelocatable; +use crate::vm::runners::cairo_pie::StrippedProgram; + +type HashFunction = fn(&FieldElement, &FieldElement) -> FieldElement; + +#[derive(thiserror_no_std::Error, Debug)] +pub enum HashChainError { + #[error("Data array must contain at least one element.")] + EmptyData, +} + +#[derive(thiserror_no_std::Error, Debug)] +pub enum ProgramHashError { + #[error(transparent)] + HashChain(#[from] HashChainError), + + #[error( + "Invalid program builtin: builtin name too long to be converted to field element: {0}" + )] + InvalidProgramBuiltin(&'static str), + + #[error("Invalid program data: data contains relocatable(s)")] + InvalidProgramData, + + /// Conversion from Felt252 to FieldElement failed. This is unlikely to happen + /// unless the implementation of Felt252 changes and this code is not updated properly. + #[error("Conversion from Felt252 to FieldElement failed")] + Felt252ToFieldElementConversionFailed, +} + +/// Computes a hash chain over the data, in the following order: +/// h(data[0], h(data[1], h(..., h(data[n-2], data[n-1])))). +/// +/// Reimplements this Python function: +/// def compute_hash_chain(data, hash_func=pedersen_hash): +/// assert len(data) >= 1, f"len(data) for hash chain computation must be >= 1; got: {len(data)}." +/// return functools.reduce(lambda x, y: hash_func(y, x), data[::-1]) +fn compute_hash_chain<'a, I>( + data: I, + hash_func: HashFunction, +) -> Result +where + I: Iterator + DoubleEndedIterator, +{ + match data.copied().rev().reduce(|x, y| hash_func(&y, &x)) { + Some(result) => Ok(result), + None => Err(HashChainError::EmptyData), + } +} + +/// Creates an instance of `FieldElement` from a builtin name. +/// +/// Converts the builtin name to bytes then attempts to create a field element from +/// these bytes. This function will fail if the builtin name is over 31 characters. +fn builtin_to_field_element(builtin: &BuiltinName) -> Result { + // The Python implementation uses the builtin name without suffix + let builtin_name = builtin + .name() + .strip_suffix("_builtin") + .unwrap_or(builtin.name()); + + FieldElement::from_byte_slice_be(builtin_name.as_bytes()) + .map_err(|_| ProgramHashError::InvalidProgramBuiltin(builtin.name())) +} + +/// The `value: FieldElement` is `pub(crate)` and there is no accessor. +/// This function converts a `Felt252` to a `FieldElement` using a safe, albeit inefficient, +/// method. +fn felt_to_field_element(felt: &Felt252) -> Result { + let bytes = felt.to_bytes_be(); + FieldElement::from_bytes_be(&bytes) + .map_err(|_e| ProgramHashError::Felt252ToFieldElementConversionFailed) +} + +/// Converts a `MaybeRelocatable` into a `FieldElement` value. +/// +/// Returns `InvalidProgramData` if `maybe_relocatable` is not an integer +fn maybe_relocatable_to_field_element( + maybe_relocatable: &MaybeRelocatable, +) -> Result { + let felt = maybe_relocatable + .get_int_ref() + .ok_or(ProgramHashError::InvalidProgramData)?; + felt_to_field_element(felt) +} + +/// Computes the Pedersen hash of a program. +/// +/// Reimplements this Python function: +/// def compute_program_hash_chain(program: ProgramBase, bootloader_version=0): +/// builtin_list = [from_bytes(builtin.encode("ascii")) for builtin in program.builtins] +/// # The program header below is missing the data length, which is later added to the data_chain. +/// program_header = [bootloader_version, program.main, len(program.builtins)] + builtin_list +/// data_chain = program_header + program.data +/// +/// return compute_hash_chain([len(data_chain)] + data_chain) +pub fn compute_program_hash_chain( + program: &StrippedProgram, + bootloader_version: usize, +) -> Result { + let program_main = program.main; + let program_main = FieldElement::from(program_main); + + // Convert builtin names to field elements + let builtin_list: Result, _> = program + .builtins + .iter() + .map(builtin_to_field_element) + .collect(); + let builtin_list = builtin_list?; + + let program_header = vec![ + FieldElement::from(bootloader_version), + program_main, + FieldElement::from(program.builtins.len()), + ]; + + let program_data: Result, _> = program + .data + .iter() + .map(maybe_relocatable_to_field_element) + .collect(); + let program_data = program_data?; + + let data_chain_len = program_header.len() + builtin_list.len() + program_data.len(); + let data_chain_len_vec = vec![FieldElement::from(data_chain_len)]; + + // Prepare a chain of iterators to feed to the hash function + let data_chain = [ + &data_chain_len_vec, + &program_header, + &builtin_list, + &program_data, + ]; + + let hash = compute_hash_chain(data_chain.iter().flat_map(|&v| v.iter()), pedersen_hash)?; + Ok(hash) +} + +#[cfg(test)] +mod tests { + #[cfg(feature = "std")] + use {crate::types::program::Program, rstest::rstest, std::path::PathBuf}; + + use starknet_crypto::pedersen_hash; + + use super::*; + + #[test] + fn test_compute_hash_chain() { + let data: Vec = vec![ + FieldElement::from(1u64), + FieldElement::from(2u64), + FieldElement::from(3u64), + ]; + let expected_hash = pedersen_hash( + &FieldElement::from(1u64), + &pedersen_hash(&FieldElement::from(2u64), &FieldElement::from(3u64)), + ); + let computed_hash = compute_hash_chain(data.iter(), pedersen_hash) + .expect("Hash computation failed unexpectedly"); + + assert_eq!(computed_hash, expected_hash); + } + + #[cfg(feature = "std")] + #[rstest] + // Expected hashes generated with `cairo-hash-program` + #[case::fibonacci( + "../cairo_programs/fibonacci.json", + "0x43b17e9592f33142246af4c06cd2b574b460dd1f718d76b51341175a62b220f" + )] + #[case::field_arithmetic( + "../cairo_programs/field_arithmetic.json", + "0x1031772ca86e618b058101af9c9a3277bac90712b750bcea1cc69d6c7cad8a7" + )] + #[case::keccak_copy_inputs( + "../cairo_programs/keccak_copy_inputs.json", + "0x49484fdc8e7a85061f9f21b7e21fe276d8a88c8e96681101a2518809e686c6c" + )] + fn test_compute_program_hash_chain( + #[case] program_path: PathBuf, + #[case] expected_program_hash: String, + ) { + let program = + Program::from_file(program_path.as_path(), Some("main")) + .expect("Could not load program. Did you compile the sample programs? Run `make test` in the root directory."); + let stripped_program = program.get_stripped_program().unwrap(); + let bootloader_version = 0; + + let program_hash = compute_program_hash_chain(&stripped_program, bootloader_version) + .expect("Failed to compute program hash."); + + let program_hash_hex = format!("{:#x}", program_hash); + + assert_eq!(program_hash_hex, expected_program_hash); + } +} diff --git a/vm/src/serde/deserialize_program.rs b/vm/src/serde/deserialize_program.rs index be314ac364..b897223f87 100644 --- a/vm/src/serde/deserialize_program.rs +++ b/vm/src/serde/deserialize_program.rs @@ -14,10 +14,12 @@ use crate::{ sync::Arc, }, utils::CAIRO_PRIME, + vm::runners::builtin_runner::RANGE_CHECK_96_BUILTIN_NAME, }; use crate::utils::PRIME_STR; use crate::vm::runners::builtin_runner::SEGMENT_ARENA_BUILTIN_NAME; +use crate::vm::runners::builtin_runner::{ADD_MOD_BUILTIN_NAME, MUL_MOD_BUILTIN_NAME}; use crate::Felt252; use crate::{ serde::deserialize_utils, @@ -55,6 +57,9 @@ pub enum BuiltinName { ec_op, poseidon, segment_arena, + range_check96, + add_mod, + mul_mod, } impl BuiltinName { @@ -69,6 +74,9 @@ impl BuiltinName { BuiltinName::ec_op => EC_OP_BUILTIN_NAME, BuiltinName::poseidon => POSEIDON_BUILTIN_NAME, BuiltinName::segment_arena => SEGMENT_ARENA_BUILTIN_NAME, + BuiltinName::range_check96 => RANGE_CHECK_96_BUILTIN_NAME, + BuiltinName::add_mod => ADD_MOD_BUILTIN_NAME, + BuiltinName::mul_mod => MUL_MOD_BUILTIN_NAME, } } } @@ -205,6 +213,17 @@ pub struct DebugInfo { pub(crate) instruction_locations: HashMap, } +impl DebugInfo { + pub fn new(instruction_locations: HashMap) -> Self { + Self { + instruction_locations, + } + } + pub fn get_instruction_locations(&self) -> HashMap { + self.instruction_locations.clone() + } +} + #[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(Arbitrary))] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct InstructionLocation { @@ -218,6 +237,17 @@ pub struct InputFile { pub filename: String, } +impl InputFile { + #[cfg(feature = "std")] + pub fn get_content(&self) -> Result { + let content = std::fs::read_to_string(self.filename.clone()); + if let Ok(content) = content { + return Ok(content); + } + Err(format!("Failed to read file {}", self.filename.clone())) + } +} + #[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(Arbitrary))] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct HintLocation { diff --git a/vm/src/tests/cairo_run_test.rs b/vm/src/tests/cairo_run_test.rs index c06a3d4e3a..fc1023d23e 100644 --- a/vm/src/tests/cairo_run_test.rs +++ b/vm/src/tests/cairo_run_test.rs @@ -1,6 +1,15 @@ -use num_traits::Zero; - use crate::tests::*; +#[cfg(feature = "mod_builtin")] +use crate::{ + utils::test_utils::Program, + vm::{ + runners::{builtin_runner::BuiltinRunner, cairo_runner::CairoRunner}, + security::verify_secure_runner, + vm_core::VirtualMachine, + }, +}; + +use num_traits::Zero; #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] @@ -1061,3 +1070,171 @@ fn cairo_run_print_dict_array() { include_bytes!("../../../cairo_programs/print_feature/print_dict_array.json"); run_program_simple(program_data); } + +#[test] +fn run_program_allow_missing_builtins() { + let program_data = include_bytes!("../../../cairo_programs/pedersen_extra_builtins.json"); + let config = CairoRunConfig { + allow_missing_builtins: Some(true), + layout: "small", // The program logic only uses builtins in the small layout but contains builtins outside of it + ..Default::default() + }; + assert!(crate::cairo_run::cairo_run( + program_data, + &config, + &mut BuiltinHintProcessor::new_empty() + ) + .is_ok()) +} + +#[test] +fn run_program_allow_missing_builtins_proof() { + let program_data = + include_bytes!("../../../cairo_programs/proof_programs/pedersen_extra_builtins.json"); + let config = CairoRunConfig { + proof_mode: true, + allow_missing_builtins: Some(true), + layout: "small", // The program logic only uses builtins in the small layout but contains builtins outside of it + ..Default::default() + }; + assert!(crate::cairo_run::cairo_run( + program_data, + &config, + &mut BuiltinHintProcessor::new_empty() + ) + .is_ok()) +} + +#[test] +#[cfg(feature = "mod_builtin")] +fn cairo_run_mod_builtin() { + let program_data = + include_bytes!("../../../cairo_programs/mod_builtin_feature/mod_builtin.json"); + run_program_with_custom_mod_builtin_params(program_data, false, 1, 3, None); +} + +#[test] +#[cfg(feature = "mod_builtin")] +fn cairo_run_mod_builtin_failure() { + let program_data = + include_bytes!("../../../cairo_programs/mod_builtin_feature/mod_builtin_failure.json"); + let error_msg = "mul_mod_builtin: Expected a * b == c (mod p). Got: instance=2, batch=0, p=9, a=2, b=2, c=2."; + run_program_with_custom_mod_builtin_params(program_data, false, 1, 3, Some(error_msg)); +} + +#[test] +#[cfg(feature = "mod_builtin")] +fn cairo_run_mod_builtin_large_batch_size() { + let program_data = include_bytes!( + "../../../cairo_programs/mod_builtin_feature/mod_builtin_large_batch_size.json" + ); + run_program_with_custom_mod_builtin_params(program_data, false, 8, 3, None); +} + +#[test] +#[cfg(feature = "mod_builtin")] +fn cairo_run_mod_builtin_large_batch_size_failure() { + let program_data = include_bytes!( + "../../../cairo_programs/mod_builtin_feature/mod_builtin_large_batch_size_failure.json" + ); + let error_msg = "mul_mod_builtin: Expected a * b == c (mod p). Got: instance=0, batch=2, p=9, a=2, b=2, c=2."; + run_program_with_custom_mod_builtin_params(program_data, false, 8, 3, Some(error_msg)); +} + +#[test] +#[cfg(feature = "mod_builtin")] +fn cairo_run_mod_builtin_proof() { + let program_data = + include_bytes!("../../../cairo_programs/mod_builtin_feature/proof/mod_builtin.json"); + run_program_with_custom_mod_builtin_params(program_data, true, 1, 3, None); +} + +#[test] +#[cfg(feature = "mod_builtin")] +fn cairo_run_mod_builtin_large_batch_size_proof() { + let program_data = include_bytes!( + "../../../cairo_programs/mod_builtin_feature/proof/mod_builtin_large_batch_size.json" + ); + run_program_with_custom_mod_builtin_params(program_data, true, 8, 3, None); +} + +#[cfg(feature = "mod_builtin")] +fn run_program_with_custom_mod_builtin_params( + data: &[u8], + proof_mode: bool, + batch_size: usize, + word_bit_len: u32, + security_error: Option<&str>, +) { + let cairo_run_config = CairoRunConfig { + layout: "all_cairo", + proof_mode, + ..Default::default() + }; + let mut hint_processor = BuiltinHintProcessor::new_empty(); + let program = Program::from_bytes(data, Some(cairo_run_config.entrypoint)).unwrap(); + let mut cairo_runner = CairoRunner::new( + &program, + cairo_run_config.layout, + cairo_run_config.proof_mode, + ) + .unwrap(); + + let mut vm = VirtualMachine::new(cairo_run_config.trace_enabled); + let end = cairo_runner.initialize(&mut vm, false).unwrap(); + // Modify add_mod & mul_mod params + for runner in vm.get_builtin_runners_as_mut() { + if let BuiltinRunner::Mod(runner) = runner { + runner.override_layout_params(batch_size, word_bit_len) + } + } + + cairo_runner + .run_until_pc(end, &mut vm, &mut hint_processor) + .unwrap(); + + if cairo_run_config.proof_mode { + cairo_runner + .run_for_steps(1, &mut vm, &mut hint_processor) + .unwrap(); + } + cairo_runner + .end_run( + cairo_run_config.disable_trace_padding, + false, + &mut vm, + &mut hint_processor, + ) + .unwrap(); + + vm.verify_auto_deductions().unwrap(); + cairo_runner.read_return_values(&mut vm, false).unwrap(); + if cairo_run_config.proof_mode { + cairo_runner.finalize_segments(&mut vm).unwrap(); + } + if !cairo_run_config.proof_mode { + let security_res = verify_secure_runner(&cairo_runner, true, None, &mut vm); + if let Some(error) = security_error { + assert!(security_res.is_err()); + assert!(security_res.err().unwrap().to_string().contains(error)); + return; + } + security_res.unwrap(); + } +} + +#[test] +#[cfg(feature = "mod_builtin")] +fn cairo_run_apply_poly() { + let program_data = + include_bytes!("../../../cairo_programs/mod_builtin_feature/apply_poly.json"); + run_program(program_data, false, Some("all_cairo"), None, None); +} + +#[test] +#[cfg(feature = "mod_builtin")] +fn cairo_run_apply_poly_proof() { + let program_data = + include_bytes!("../../../cairo_programs/mod_builtin_feature/proof/apply_poly.json"); + run_program(program_data, true, Some("all_cairo"), None, None); +} diff --git a/vm/src/tests/compare_factorial_outputs_all_layouts.sh b/vm/src/tests/compare_factorial_outputs_all_layouts.sh new file mode 100755 index 0000000000..1a2067e4c8 --- /dev/null +++ b/vm/src/tests/compare_factorial_outputs_all_layouts.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env sh + +factorial_compiled="cairo_programs/proof_programs/factorial.json" +passed_tests=0 +failed_tests=0 +exit_code=0 + +for layout in "plain" "small" "dex" "recursive" "starknet" "starknet_with_keccak" "recursive_large_output" "all_solidity" "starknet_with_keccak"; do + # Run cairo_vm + echo "Running cairo-vm with layout $layout" + cargo run -p cairo-vm-cli --release -- --layout $layout --proof_mode $factorial_compiled --trace_file factorial_rs.trace --memory_file factorial_rs.memory --air_public_input factorial_rs.air_public_input --air_private_input factorial_rs.air_private_input + # Run cairo_lang + echo "Running cairo_lang with layout $layout" + cairo-run --layout $layout --proof_mode --program $factorial_compiled --trace_file factorial_py.trace --memory_file factorial_py.memory --air_public_input factorial_py.air_public_input --air_private_input factorial_py.air_private_input + # Compare trace + echo "Running trace comparison for layout $layout" + if ! diff -q factorial_rs.trace factorial_py.trace; then + echo "Trace differs for layout $layout" + exit_code=1 + failed_tests=$((failed_tests + 1)) + else + passed_tests=$((passed_tests + 1)) + fi + # Compare memory + echo "Running memory comparison for layout $layout" + if ! ./vm/src/tests/memory_comparator.py factorial_rs.memory factorial_py.memory; then + echo "Memory differs for layout $layout" + exit_code=1 + failed_tests=$((failed_tests + 1)) + else + passed_tests=$((passed_tests + 1)) + fi + # Compare air public input + echo "Running air public input comparison for layout $layout" + if ! ./vm/src/tests/air_public_input_comparator.py factorial_rs.air_public_input factorial_py.air_public_input; then + echo "Air public input differs for layout $layout" + exit_code=1 + failed_tests=$((failed_tests + 1)) + else + passed_tests=$((passed_tests + 1)) + fi + # Compare air private input + echo "Running air private input comparison for layout $layout" + if ! ./vm/src/tests/air_private_input_comparator.py factorial_rs.air_private_input factorial_py.air_private_input; then + echo "Air private input differs for layout $layout" + exit_code=1 + failed_tests=$((failed_tests + 1)) + else + passed_tests=$((passed_tests + 1)) + fi + # Clean files generated by the script + echo "Cleaning files" + rm factorial_rs.* + rm factorial_py.* +done + +if test $failed_tests != 0; then + echo "Comparisons: $failed_tests failed, $passed_tests passed, $((failed_tests + passed_tests)) total" +elif test $passed_tests = 0; then + echo "No tests ran!" + exit_code=2 +else + echo "All $passed_tests tests passed; no discrepancies found" +fi + +exit "${exit_code}" diff --git a/vm/src/tests/mod.rs b/vm/src/tests/mod.rs index bb80958789..d09918726f 100644 --- a/vm/src/tests/mod.rs +++ b/vm/src/tests/mod.rs @@ -48,24 +48,25 @@ mod skip_instruction_test; //For simple programs that should just succeed and have no special needs. //Checks memory holes == 0 fn run_program_simple(data: &[u8]) { - run_program(data, Some("all_cairo"), None, None) + run_program(data, false, Some("all_cairo"), None, None) } //For simple programs that should just succeed but using small layout. fn run_program_small(data: &[u8]) { - run_program(data, Some("small"), None, None) + run_program(data, false, Some("small"), None, None) } fn run_program_with_trace(data: &[u8], trace: &[(usize, usize, usize)]) { - run_program(data, Some("all_cairo"), Some(trace), None) + run_program(data, false, Some("all_cairo"), Some(trace), None) } fn run_program_with_error(data: &[u8], error: &str) { - run_program(data, Some("all_cairo"), None, Some(error)) + run_program(data, false, Some("all_cairo"), None, Some(error)) } fn run_program( data: &[u8], + proof_mode: bool, layout: Option<&str>, trace: Option<&[(usize, usize, usize)]>, error: Option<&str>, @@ -75,6 +76,7 @@ fn run_program( layout: layout.unwrap_or("all_cairo"), relocate_mem: true, trace_enabled: true, + proof_mode, ..Default::default() }; let res = cairo_run(data, &cairo_run_config, &mut hint_executor); diff --git a/vm/src/types/instance_definitions/bitwise_instance_def.rs b/vm/src/types/instance_definitions/bitwise_instance_def.rs index e59c219fd3..f5e3ce4354 100644 --- a/vm/src/types/instance_definitions/bitwise_instance_def.rs +++ b/vm/src/types/instance_definitions/bitwise_instance_def.rs @@ -2,34 +2,22 @@ use serde::Serialize; pub(crate) const CELLS_PER_BITWISE: u32 = 5; pub(crate) const INPUT_CELLS_PER_BITWISE: u32 = 2; +pub(crate) const TOTAL_N_BITS: u32 = 251; #[derive(Serialize, Clone, Debug, PartialEq)] pub(crate) struct BitwiseInstanceDef { pub(crate) ratio: Option, - pub(crate) total_n_bits: u32, } -impl BitwiseInstanceDef { - pub(crate) fn default() -> Self { - BitwiseInstanceDef { - ratio: Some(256), - total_n_bits: 251, - } +impl Default for BitwiseInstanceDef { + fn default() -> Self { + BitwiseInstanceDef { ratio: Some(256) } } +} +impl BitwiseInstanceDef { pub(crate) fn new(ratio: Option) -> Self { - BitwiseInstanceDef { - ratio, - total_n_bits: 251, - } - } - - pub(crate) fn _cells_per_builtin(&self) -> u32 { - CELLS_PER_BITWISE - } - - pub(crate) fn _range_check_units_per_builtin(&self) -> u32 { - 0 + BitwiseInstanceDef { ratio } } } @@ -40,37 +28,17 @@ mod tests { #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_range_check_units_per_builtin() { - let builtin_instance = BitwiseInstanceDef::default(); - assert_eq!(builtin_instance._range_check_units_per_builtin(), 0); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_cells_per_builtin() { - let builtin_instance = BitwiseInstanceDef::default(); - assert_eq!(builtin_instance._cells_per_builtin(), 5); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_new() { - let builtin_instance = BitwiseInstanceDef { - ratio: Some(8), - total_n_bits: 251, - }; + let builtin_instance = BitwiseInstanceDef { ratio: Some(8) }; assert_eq!(BitwiseInstanceDef::new(Some(8)), builtin_instance); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_default() { - let builtin_instance = BitwiseInstanceDef { - ratio: Some(256), - total_n_bits: 251, - }; + let builtin_instance = BitwiseInstanceDef { ratio: Some(256) }; assert_eq!(BitwiseInstanceDef::default(), builtin_instance); } } diff --git a/vm/src/types/instance_definitions/builtins_instance_def.rs b/vm/src/types/instance_definitions/builtins_instance_def.rs index 019a926b8f..a6d0bc44cc 100644 --- a/vm/src/types/instance_definitions/builtins_instance_def.rs +++ b/vm/src/types/instance_definitions/builtins_instance_def.rs @@ -1,9 +1,13 @@ +use super::mod_instance_def::ModInstanceDef; use super::{ bitwise_instance_def::BitwiseInstanceDef, ec_op_instance_def::EcOpInstanceDef, ecdsa_instance_def::EcdsaInstanceDef, keccak_instance_def::KeccakInstanceDef, pedersen_instance_def::PedersenInstanceDef, poseidon_instance_def::PoseidonInstanceDef, range_check_instance_def::RangeCheckInstanceDef, }; + +pub(crate) const BUILTIN_INSTANCES_PER_COMPONENT: u32 = 1; + use serde::Serialize; #[derive(Serialize, Debug, PartialEq)] @@ -16,6 +20,9 @@ pub(crate) struct BuiltinsInstanceDef { pub(crate) ec_op: Option, pub(crate) keccak: Option, pub(crate) poseidon: Option, + pub(crate) range_check96: Option, + pub(crate) add_mod: Option, + pub(crate) mul_mod: Option, } impl BuiltinsInstanceDef { @@ -29,6 +36,9 @@ impl BuiltinsInstanceDef { ec_op: None, keccak: None, poseidon: None, + range_check96: None, + add_mod: None, + mul_mod: None, } } @@ -42,6 +52,9 @@ impl BuiltinsInstanceDef { ec_op: None, keccak: None, poseidon: None, + range_check96: None, + add_mod: None, + mul_mod: None, } } @@ -55,71 +68,95 @@ impl BuiltinsInstanceDef { ec_op: None, keccak: None, poseidon: None, + range_check96: None, + add_mod: None, + mul_mod: None, } } pub(crate) fn recursive() -> BuiltinsInstanceDef { BuiltinsInstanceDef { output: true, - pedersen: Some(PedersenInstanceDef::new(Some(128), 1)), + pedersen: Some(PedersenInstanceDef::new(Some(128))), range_check: Some(RangeCheckInstanceDef::default()), ecdsa: None, bitwise: Some(BitwiseInstanceDef::new(Some(8))), ec_op: None, keccak: None, poseidon: None, + range_check96: None, + add_mod: None, + mul_mod: None, } } pub(crate) fn starknet() -> BuiltinsInstanceDef { BuiltinsInstanceDef { output: true, - pedersen: Some(PedersenInstanceDef::new(Some(32), 1)), - range_check: Some(RangeCheckInstanceDef::new(Some(16), 8)), + pedersen: Some(PedersenInstanceDef::new(Some(32))), + range_check: Some(RangeCheckInstanceDef::new(Some(16))), ecdsa: Some(EcdsaInstanceDef::new(Some(2048))), bitwise: Some(BitwiseInstanceDef::new(Some(64))), ec_op: Some(EcOpInstanceDef::new(Some(1024))), keccak: None, poseidon: Some(PoseidonInstanceDef::default()), + range_check96: None, + add_mod: None, + mul_mod: None, } } pub(crate) fn starknet_with_keccak() -> BuiltinsInstanceDef { BuiltinsInstanceDef { output: true, - pedersen: Some(PedersenInstanceDef::new(Some(32), 1)), - range_check: Some(RangeCheckInstanceDef::new(Some(16), 8)), + pedersen: Some(PedersenInstanceDef::new(Some(32))), + range_check: Some(RangeCheckInstanceDef::new(Some(16))), ecdsa: Some(EcdsaInstanceDef::new(Some(2048))), bitwise: Some(BitwiseInstanceDef::new(Some(64))), ec_op: Some(EcOpInstanceDef::new(Some(1024))), - keccak: Some(KeccakInstanceDef::new(Some(2048), vec![200; 8])), + keccak: Some(KeccakInstanceDef::new(Some(2048))), poseidon: Some(PoseidonInstanceDef::default()), + range_check96: None, + add_mod: None, + mul_mod: None, } } pub(crate) fn recursive_large_output() -> BuiltinsInstanceDef { BuiltinsInstanceDef { output: true, - pedersen: Some(PedersenInstanceDef::new(Some(32), 1)), + pedersen: Some(PedersenInstanceDef::new(Some(128))), range_check: Some(RangeCheckInstanceDef::default()), ecdsa: None, bitwise: Some(BitwiseInstanceDef::new(Some(8))), ec_op: None, keccak: None, - poseidon: None, + poseidon: Some(PoseidonInstanceDef::new(Some(8))), + range_check96: None, + add_mod: None, + mul_mod: None, } } pub(crate) fn all_cairo() -> BuiltinsInstanceDef { BuiltinsInstanceDef { output: true, - pedersen: Some(PedersenInstanceDef::new(Some(256), 1)), + pedersen: Some(PedersenInstanceDef::new(Some(256))), range_check: Some(RangeCheckInstanceDef::default()), ecdsa: Some(EcdsaInstanceDef::new(Some(2048))), bitwise: Some(BitwiseInstanceDef::new(Some(16))), ec_op: Some(EcOpInstanceDef::new(Some(1024))), - keccak: Some(KeccakInstanceDef::new(Some(2048), vec![200; 8])), + keccak: Some(KeccakInstanceDef::new(Some(2048))), poseidon: Some(PoseidonInstanceDef::new(Some(256))), + range_check96: Some(RangeCheckInstanceDef::new(Some(8))), + #[cfg(feature = "mod_builtin")] + add_mod: Some(ModInstanceDef::new(Some(128), 1, 96)), + #[cfg(feature = "mod_builtin")] + mul_mod: Some(ModInstanceDef::new(Some(256), 1, 96)), + #[cfg(not(feature = "mod_builtin"))] + add_mod: None, + #[cfg(not(feature = "mod_builtin"))] + mul_mod: None, } } @@ -133,19 +170,31 @@ impl BuiltinsInstanceDef { ec_op: Some(EcOpInstanceDef::default()), keccak: None, poseidon: None, + range_check96: None, + add_mod: None, + mul_mod: None, } } pub(crate) fn dynamic() -> BuiltinsInstanceDef { BuiltinsInstanceDef { output: true, - pedersen: Some(PedersenInstanceDef::new(None, 4)), - range_check: Some(RangeCheckInstanceDef::new(None, 8)), + pedersen: Some(PedersenInstanceDef::new(None)), + range_check: Some(RangeCheckInstanceDef::new(None)), ecdsa: Some(EcdsaInstanceDef::new(None)), bitwise: Some(BitwiseInstanceDef::new(None)), ec_op: Some(EcOpInstanceDef::new(None)), keccak: None, poseidon: None, + range_check96: None, + #[cfg(feature = "mod_builtin")] + add_mod: Some(ModInstanceDef::new(None, 1, 96)), + #[cfg(feature = "mod_builtin")] + mul_mod: Some(ModInstanceDef::new(None, 1, 96)), + #[cfg(not(feature = "mod_builtin"))] + add_mod: None, + #[cfg(not(feature = "mod_builtin"))] + mul_mod: None, } } } @@ -249,7 +298,7 @@ mod tests { assert!(builtins.bitwise.is_some()); assert!(builtins.ec_op.is_none()); assert!(builtins.keccak.is_none()); - assert!(builtins.poseidon.is_none()); + assert!(builtins.poseidon.is_some()); } #[test] diff --git a/vm/src/types/instance_definitions/cpu_instance_def.rs b/vm/src/types/instance_definitions/cpu_instance_def.rs deleted file mode 100644 index 19d22643a1..0000000000 --- a/vm/src/types/instance_definitions/cpu_instance_def.rs +++ /dev/null @@ -1,27 +0,0 @@ -use serde::Serialize; - -#[derive(Serialize, Debug, PartialEq)] -pub(crate) struct CpuInstanceDef { - pub(crate) _safe_call: bool, -} - -impl CpuInstanceDef { - pub(crate) fn default() -> Self { - CpuInstanceDef { _safe_call: true } - } -} - -#[cfg(test)] -mod tests { - use super::CpuInstanceDef; - - #[cfg(target_arch = "wasm32")] - use wasm_bindgen_test::*; - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn test_default() { - let cpu_instance = CpuInstanceDef::default(); - assert!(cpu_instance._safe_call) - } -} diff --git a/vm/src/types/instance_definitions/ec_op_instance_def.rs b/vm/src/types/instance_definitions/ec_op_instance_def.rs index 75e8039dba..3f4bc6ac2a 100644 --- a/vm/src/types/instance_definitions/ec_op_instance_def.rs +++ b/vm/src/types/instance_definitions/ec_op_instance_def.rs @@ -2,37 +2,22 @@ use serde::Serialize; pub(crate) const CELLS_PER_EC_OP: u32 = 7; pub(crate) const INPUT_CELLS_PER_EC_OP: u32 = 5; +pub(crate) const SCALAR_HEIGHT: u32 = 256; #[derive(Serialize, Clone, Debug, PartialEq)] pub(crate) struct EcOpInstanceDef { pub(crate) ratio: Option, - pub(crate) scalar_height: u32, - pub(crate) _scalar_bits: u32, } -impl EcOpInstanceDef { - pub(crate) fn default() -> Self { - EcOpInstanceDef { - ratio: Some(256), - scalar_height: 256, - _scalar_bits: 252, - } +impl Default for EcOpInstanceDef { + fn default() -> Self { + EcOpInstanceDef { ratio: Some(256) } } +} +impl EcOpInstanceDef { pub(crate) fn new(ratio: Option) -> Self { - EcOpInstanceDef { - ratio, - scalar_height: 256, - _scalar_bits: 252, - } - } - - pub(crate) fn _cells_per_builtin(&self) -> u32 { - CELLS_PER_EC_OP - } - - pub(crate) fn _range_check_units_per_builtin(&self) -> u32 { - 0 + EcOpInstanceDef { ratio } } } @@ -43,39 +28,17 @@ mod tests { #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_range_check_units_per_builtin() { - let builtin_instance = EcOpInstanceDef::default(); - assert_eq!(builtin_instance._range_check_units_per_builtin(), 0); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_cells_per_builtin() { - let builtin_instance = EcOpInstanceDef::default(); - assert_eq!(builtin_instance._cells_per_builtin(), 7); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_new() { - let builtin_instance = EcOpInstanceDef { - ratio: Some(8), - scalar_height: 256, - _scalar_bits: 252, - }; + let builtin_instance = EcOpInstanceDef { ratio: Some(8) }; assert_eq!(EcOpInstanceDef::new(Some(8)), builtin_instance); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_default() { - let builtin_instance = EcOpInstanceDef { - ratio: Some(256), - scalar_height: 256, - _scalar_bits: 252, - }; + let builtin_instance = EcOpInstanceDef { ratio: Some(256) }; assert_eq!(EcOpInstanceDef::default(), builtin_instance); } } diff --git a/vm/src/types/instance_definitions/ecdsa_instance_def.rs b/vm/src/types/instance_definitions/ecdsa_instance_def.rs index 518e5f5288..42f2ad6beb 100644 --- a/vm/src/types/instance_definitions/ecdsa_instance_def.rs +++ b/vm/src/types/instance_definitions/ecdsa_instance_def.rs @@ -1,44 +1,23 @@ use serde::Serialize; pub(crate) const CELLS_PER_SIGNATURE: u32 = 2; -pub(crate) const _INPUTCELLS_PER_SIGNATURE: u32 = 2; -#[derive(Serialize, Debug, PartialEq)] +#[derive(Serialize, Clone, Debug, PartialEq)] pub(crate) struct EcdsaInstanceDef { pub(crate) ratio: Option, - pub(crate) _repetitions: u32, - pub(crate) _height: u32, - pub(crate) _n_hash_bits: u32, } -impl EcdsaInstanceDef { - pub(crate) fn default() -> Self { - EcdsaInstanceDef { - ratio: Some(512), - _repetitions: 1, - _height: 256, - _n_hash_bits: 251, - } +impl Default for EcdsaInstanceDef { + fn default() -> Self { + EcdsaInstanceDef { ratio: Some(512) } } +} +impl EcdsaInstanceDef { pub(crate) fn new(ratio: Option) -> Self { - EcdsaInstanceDef { - ratio, - _repetitions: 1, - _height: 256, - _n_hash_bits: 251, - } - } - - pub(crate) fn _cells_per_builtin(&self) -> u32 { - CELLS_PER_SIGNATURE - } - - pub(crate) fn _range_check_units_per_builtin(&self) -> u32 { - 0 + EcdsaInstanceDef { ratio } } } - #[cfg(test)] mod tests { use super::*; @@ -46,41 +25,17 @@ mod tests { #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_range_check_units_per_builtin() { - let builtin_instance = EcdsaInstanceDef::default(); - assert_eq!(builtin_instance._range_check_units_per_builtin(), 0); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_cells_per_builtin() { - let builtin_instance = EcdsaInstanceDef::default(); - assert_eq!(builtin_instance._cells_per_builtin(), 2); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_new() { - let builtin_instance = EcdsaInstanceDef { - ratio: Some(8), - _repetitions: 1, - _height: 256, - _n_hash_bits: 251, - }; + let builtin_instance = EcdsaInstanceDef { ratio: Some(8) }; assert_eq!(EcdsaInstanceDef::new(Some(8)), builtin_instance); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_default() { - let builtin_instance = EcdsaInstanceDef { - ratio: Some(512), - _repetitions: 1, - _height: 256, - _n_hash_bits: 251, - }; + let builtin_instance = EcdsaInstanceDef { ratio: Some(512) }; assert_eq!(EcdsaInstanceDef::default(), builtin_instance); } } diff --git a/vm/src/types/instance_definitions/keccak_instance_def.rs b/vm/src/types/instance_definitions/keccak_instance_def.rs index 655c361fcc..5746050067 100644 --- a/vm/src/types/instance_definitions/keccak_instance_def.rs +++ b/vm/src/types/instance_definitions/keccak_instance_def.rs @@ -1,39 +1,25 @@ use crate::stdlib::prelude::*; use serde::Serialize; +pub(crate) const INPUT_CELLS_PER_KECCAK: u32 = 8; +pub(crate) const CELLS_PER_KECCAK: u32 = 16; +pub(crate) const KECCAK_INSTANCES_PER_COMPONENT: u32 = 16; + #[derive(Serialize, Clone, Debug, PartialEq)] pub(crate) struct KeccakInstanceDef { pub(crate) ratio: Option, - pub(crate) _state_rep: Vec, - pub(crate) _instance_per_component: u32, } impl Default for KeccakInstanceDef { fn default() -> Self { - Self { - // ratio should be equal to 2 ** 11 -> 2048 - ratio: Some(2048), - _state_rep: vec![200; 8], - _instance_per_component: 16, - } + // ratio should be equal to 2 ** 11 -> 2048 + KeccakInstanceDef { ratio: Some(2048) } } } impl KeccakInstanceDef { - pub(crate) fn new(ratio: Option, _state_rep: Vec) -> Self { - Self { - ratio, - _state_rep, - ..Default::default() - } - } - - pub(crate) fn cells_per_builtin(&self) -> u32 { - 2 * self._state_rep.len() as u32 - } - - pub(crate) fn _range_check_units_per_builtin(&self) -> u32 { - 0 + pub(crate) fn new(ratio: Option) -> Self { + KeccakInstanceDef { ratio } } } @@ -44,42 +30,17 @@ mod tests { #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_range_check_units_per_builtin() { - let builtin_instance = KeccakInstanceDef::default(); - assert_eq!(builtin_instance._range_check_units_per_builtin(), 0); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_cells_per_builtin() { - let builtin_instance = KeccakInstanceDef::default(); - assert_eq!(builtin_instance.cells_per_builtin(), 16); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_new() { - let builtin_instance = KeccakInstanceDef { - ratio: Some(2048), - _state_rep: vec![200; 8], - _instance_per_component: 16, - }; - assert_eq!( - KeccakInstanceDef::new(Some(2048), vec![200; 8]), - builtin_instance - ); + let builtin_instance = KeccakInstanceDef { ratio: Some(2048) }; + assert_eq!(KeccakInstanceDef::new(Some(2048)), builtin_instance); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_default() { - let builtin_instance = KeccakInstanceDef { - ratio: Some(2048), - _state_rep: vec![200; 8], - _instance_per_component: 16, - }; + let builtin_instance = KeccakInstanceDef { ratio: Some(2048) }; assert_eq!(KeccakInstanceDef::default(), builtin_instance); } } diff --git a/vm/src/types/instance_definitions/mod.rs b/vm/src/types/instance_definitions/mod.rs index 53bcc15d1b..8f1bba2198 100644 --- a/vm/src/types/instance_definitions/mod.rs +++ b/vm/src/types/instance_definitions/mod.rs @@ -1,10 +1,11 @@ pub mod bitwise_instance_def; pub mod builtins_instance_def; -pub mod cpu_instance_def; pub mod diluted_pool_instance_def; pub mod ec_op_instance_def; pub mod ecdsa_instance_def; pub mod keccak_instance_def; +#[allow(unused)] +pub mod mod_instance_def; pub mod pedersen_instance_def; pub mod poseidon_instance_def; pub mod range_check_instance_def; diff --git a/vm/src/types/instance_definitions/mod_instance_def.rs b/vm/src/types/instance_definitions/mod_instance_def.rs new file mode 100644 index 0000000000..0b84077d33 --- /dev/null +++ b/vm/src/types/instance_definitions/mod_instance_def.rs @@ -0,0 +1,22 @@ +use serde::Serialize; + +pub(crate) const N_WORDS: usize = 4; + +pub(crate) const CELLS_PER_MOD: u32 = 7; + +#[derive(Serialize, Debug, PartialEq, Clone)] +pub(crate) struct ModInstanceDef { + pub(crate) ratio: Option, + pub(crate) word_bit_len: u32, + pub(crate) batch_size: usize, +} + +impl ModInstanceDef { + pub(crate) fn new(ratio: Option, batch_size: usize, word_bit_len: u32) -> Self { + ModInstanceDef { + ratio, + word_bit_len, + batch_size, + } + } +} diff --git a/vm/src/types/instance_definitions/pedersen_instance_def.rs b/vm/src/types/instance_definitions/pedersen_instance_def.rs index d6273f35f1..5f5bc0e564 100644 --- a/vm/src/types/instance_definitions/pedersen_instance_def.rs +++ b/vm/src/types/instance_definitions/pedersen_instance_def.rs @@ -1,48 +1,22 @@ -use num_bigint::{BigInt, Sign}; use serde::Serialize; pub(crate) const CELLS_PER_HASH: u32 = 3; pub(crate) const INPUT_CELLS_PER_HASH: u32 = 2; -#[derive(Serialize, Debug, PartialEq)] +#[derive(Serialize, Clone, Debug, PartialEq)] pub(crate) struct PedersenInstanceDef { pub(crate) ratio: Option, - pub(crate) _repetitions: u32, - pub(crate) _element_height: u32, - pub(crate) _element_bits: u32, - pub(crate) _n_inputs: u32, - pub(crate) _hash_limit: BigInt, } -impl PedersenInstanceDef { - pub(crate) fn default() -> Self { - PedersenInstanceDef { - ratio: Some(8), - _repetitions: 4, - _element_height: 256, - _element_bits: 252, - _n_inputs: 2, - _hash_limit: BigInt::new(Sign::Plus, vec![1, 0, 0, 0, 0, 0, 17, 134217728]), - } - } - - pub(crate) fn new(ratio: Option, _repetitions: u32) -> Self { - PedersenInstanceDef { - ratio, - _repetitions, - _element_height: 256, - _element_bits: 252, - _n_inputs: 2, - _hash_limit: BigInt::new(Sign::Plus, vec![1, 0, 0, 0, 0, 0, 17, 134217728]), - } - } - - pub(crate) fn _cells_per_builtin(&self) -> u32 { - CELLS_PER_HASH +impl Default for PedersenInstanceDef { + fn default() -> Self { + PedersenInstanceDef { ratio: Some(8) } } +} - pub(crate) fn _range_check_units_per_builtin(&self) -> u32 { - 0 +impl PedersenInstanceDef { + pub(crate) fn new(ratio: Option) -> Self { + PedersenInstanceDef { ratio } } } @@ -53,45 +27,17 @@ mod tests { #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_range_check_units_per_builtin() { - let builtin_instance = PedersenInstanceDef::default(); - assert_eq!(builtin_instance._range_check_units_per_builtin(), 0); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_cells_per_builtin() { - let builtin_instance = PedersenInstanceDef::default(); - assert_eq!(builtin_instance._cells_per_builtin(), 3); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_new() { - let builtin_instance = PedersenInstanceDef { - ratio: Some(10), - _repetitions: 2, - _element_height: 256, - _element_bits: 252, - _n_inputs: 2, - _hash_limit: BigInt::new(Sign::Plus, vec![1, 0, 0, 0, 0, 0, 17, 134217728]), - }; - assert_eq!(PedersenInstanceDef::new(Some(10), 2), builtin_instance); + let builtin_instance = PedersenInstanceDef { ratio: Some(10) }; + assert_eq!(PedersenInstanceDef::new(Some(10)), builtin_instance); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_default() { - let builtin_instance = PedersenInstanceDef { - ratio: Some(8), - _repetitions: 4, - _element_height: 256, - _element_bits: 252, - _n_inputs: 2, - _hash_limit: BigInt::new(Sign::Plus, vec![1, 0, 0, 0, 0, 0, 17, 134217728]), - }; + let builtin_instance = PedersenInstanceDef { ratio: Some(8) }; assert_eq!(PedersenInstanceDef::default(), builtin_instance); } } diff --git a/vm/src/types/instance_definitions/poseidon_instance_def.rs b/vm/src/types/instance_definitions/poseidon_instance_def.rs index fa478c4c3d..ae52df75fe 100644 --- a/vm/src/types/instance_definitions/poseidon_instance_def.rs +++ b/vm/src/types/instance_definitions/poseidon_instance_def.rs @@ -8,11 +8,12 @@ pub(crate) struct PoseidonInstanceDef { pub(crate) ratio: Option, } -impl PoseidonInstanceDef { - pub(crate) fn default() -> Self { +impl Default for PoseidonInstanceDef { + fn default() -> Self { PoseidonInstanceDef { ratio: Some(32) } } - +} +impl PoseidonInstanceDef { pub(crate) fn new(ratio: Option) -> Self { PoseidonInstanceDef { ratio } } diff --git a/vm/src/types/instance_definitions/range_check_instance_def.rs b/vm/src/types/instance_definitions/range_check_instance_def.rs index ad46df54c6..4524fca9bb 100644 --- a/vm/src/types/instance_definitions/range_check_instance_def.rs +++ b/vm/src/types/instance_definitions/range_check_instance_def.rs @@ -4,27 +4,17 @@ pub(crate) const CELLS_PER_RANGE_CHECK: u32 = 1; #[derive(Serialize, Debug, PartialEq)] pub(crate) struct RangeCheckInstanceDef { pub(crate) ratio: Option, - pub(crate) n_parts: u32, } -impl RangeCheckInstanceDef { - pub(crate) fn default() -> Self { - RangeCheckInstanceDef { - ratio: Some(8), - n_parts: 8, - } - } - - pub(crate) fn new(ratio: Option, n_parts: u32) -> Self { - RangeCheckInstanceDef { ratio, n_parts } - } - - pub(crate) fn _cells_per_builtin(&self) -> u32 { - CELLS_PER_RANGE_CHECK +impl Default for RangeCheckInstanceDef { + fn default() -> Self { + RangeCheckInstanceDef { ratio: Some(8) } } +} - pub(crate) fn _range_check_units_per_builtin(&self) -> u32 { - self.n_parts +impl RangeCheckInstanceDef { + pub(crate) fn new(ratio: Option) -> Self { + RangeCheckInstanceDef { ratio } } } @@ -35,37 +25,17 @@ mod tests { #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_range_check_units_per_builtin() { - let builtin_instance = RangeCheckInstanceDef::default(); - assert_eq!(builtin_instance._range_check_units_per_builtin(), 8); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_cells_per_builtin() { - let builtin_instance = RangeCheckInstanceDef::default(); - assert_eq!(builtin_instance._cells_per_builtin(), 1); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_new() { - let builtin_instance = RangeCheckInstanceDef { - ratio: Some(10), - n_parts: 10, - }; - assert_eq!(RangeCheckInstanceDef::new(Some(10), 10), builtin_instance); + let builtin_instance = RangeCheckInstanceDef { ratio: Some(10) }; + assert_eq!(RangeCheckInstanceDef::new(Some(10)), builtin_instance); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_default() { - let builtin_instance = RangeCheckInstanceDef { - ratio: Some(8), - n_parts: 8, - }; + let builtin_instance = RangeCheckInstanceDef { ratio: Some(8) }; assert_eq!(RangeCheckInstanceDef::default(), builtin_instance); } } diff --git a/vm/src/types/layout.rs b/vm/src/types/layout.rs index 82eba1bece..3533f817fe 100644 --- a/vm/src/types/layout.rs +++ b/vm/src/types/layout.rs @@ -1,165 +1,120 @@ use crate::stdlib::prelude::*; use super::instance_definitions::{ - builtins_instance_def::BuiltinsInstanceDef, cpu_instance_def::CpuInstanceDef, - diluted_pool_instance_def::DilutedPoolInstanceDef, + builtins_instance_def::BuiltinsInstanceDef, diluted_pool_instance_def::DilutedPoolInstanceDef, }; +pub(crate) const MEMORY_UNITS_PER_STEP: u32 = 8; + use serde::Serialize; #[derive(Serialize, Debug)] pub struct CairoLayout { - pub(crate) _name: String, - pub(crate) _cpu_component_step: u32, + pub(crate) name: String, pub(crate) rc_units: u32, pub(crate) builtins: BuiltinsInstanceDef, - pub(crate) _public_memory_fraction: u32, - pub(crate) _memory_units_per_step: u32, + pub(crate) public_memory_fraction: u32, pub(crate) diluted_pool_instance_def: Option, - pub(crate) _n_trace_colums: u32, - pub(crate) _cpu_instance_def: CpuInstanceDef, } impl CairoLayout { pub(crate) fn plain_instance() -> CairoLayout { CairoLayout { - _name: String::from("plain"), - _cpu_component_step: 1, + name: String::from("plain"), rc_units: 16, builtins: BuiltinsInstanceDef::plain(), - _public_memory_fraction: 4, - _memory_units_per_step: 8, + public_memory_fraction: 4, diluted_pool_instance_def: None, - _n_trace_colums: 8, - _cpu_instance_def: CpuInstanceDef::default(), } } pub(crate) fn small_instance() -> CairoLayout { CairoLayout { - _name: String::from("small"), - _cpu_component_step: 1, + name: String::from("small"), rc_units: 16, builtins: BuiltinsInstanceDef::small(), - _public_memory_fraction: 4, - _memory_units_per_step: 8, + public_memory_fraction: 4, diluted_pool_instance_def: None, - _n_trace_colums: 25, - _cpu_instance_def: CpuInstanceDef::default(), } } pub(crate) fn dex_instance() -> CairoLayout { CairoLayout { - _name: String::from("dex"), - _cpu_component_step: 1, + name: String::from("dex"), rc_units: 4, builtins: BuiltinsInstanceDef::dex(), - _public_memory_fraction: 4, - _memory_units_per_step: 8, + public_memory_fraction: 4, diluted_pool_instance_def: None, - _n_trace_colums: 22, - _cpu_instance_def: CpuInstanceDef::default(), } } - #[allow(dead_code)] pub(crate) fn recursive_instance() -> CairoLayout { CairoLayout { - _name: String::from("recursive"), - _cpu_component_step: 1, + name: String::from("recursive"), rc_units: 4, builtins: BuiltinsInstanceDef::recursive(), - _public_memory_fraction: 8, - _memory_units_per_step: 8, + public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), - _n_trace_colums: 10, - _cpu_instance_def: CpuInstanceDef::default(), } } pub(crate) fn starknet_instance() -> CairoLayout { CairoLayout { - _name: String::from("starknet"), - _cpu_component_step: 1, + name: String::from("starknet"), rc_units: 4, builtins: BuiltinsInstanceDef::starknet(), - _public_memory_fraction: 8, - _memory_units_per_step: 8, + public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::new(2, 4, 16)), - _n_trace_colums: 10, - _cpu_instance_def: CpuInstanceDef::default(), } } pub(crate) fn starknet_with_keccak_instance() -> CairoLayout { CairoLayout { - _name: String::from("starknet_with_keccak"), - _cpu_component_step: 1, + name: String::from("starknet_with_keccak"), rc_units: 4, builtins: BuiltinsInstanceDef::starknet_with_keccak(), - _public_memory_fraction: 8, - _memory_units_per_step: 8, + public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), - _n_trace_colums: 15, - _cpu_instance_def: CpuInstanceDef::default(), } } pub(crate) fn recursive_large_output_instance() -> CairoLayout { CairoLayout { - _name: String::from("recursive_large_output"), - _cpu_component_step: 1, + name: String::from("recursive_large_output"), rc_units: 4, builtins: BuiltinsInstanceDef::recursive_large_output(), - _public_memory_fraction: 8, - _memory_units_per_step: 8, + public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), - _n_trace_colums: 13, - _cpu_instance_def: CpuInstanceDef::default(), } } - #[allow(dead_code)] pub(crate) fn all_cairo_instance() -> CairoLayout { CairoLayout { - _name: String::from("all_cairo"), - _cpu_component_step: 1, + name: String::from("all_cairo"), rc_units: 4, builtins: BuiltinsInstanceDef::all_cairo(), - _public_memory_fraction: 8, - _memory_units_per_step: 8, + public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), - _n_trace_colums: 11, - _cpu_instance_def: CpuInstanceDef::default(), } } pub(crate) fn all_solidity_instance() -> CairoLayout { CairoLayout { - _name: String::from("all_solidity"), - _cpu_component_step: 1, + name: String::from("all_solidity"), rc_units: 8, builtins: BuiltinsInstanceDef::all_solidity(), - _public_memory_fraction: 8, - _memory_units_per_step: 8, + public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), - _n_trace_colums: 27, - _cpu_instance_def: CpuInstanceDef::default(), } } pub(crate) fn dynamic_instance() -> CairoLayout { CairoLayout { - _name: String::from("dynamic"), - _cpu_component_step: 1, + name: String::from("dynamic"), rc_units: 16, builtins: BuiltinsInstanceDef::dynamic(), - _public_memory_fraction: 8, - _memory_units_per_step: 8, + public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), - _n_trace_colums: 73, - _cpu_instance_def: CpuInstanceDef::default(), } } } @@ -176,15 +131,11 @@ mod tests { fn get_plain_instance() { let layout = CairoLayout::plain_instance(); let builtins = BuiltinsInstanceDef::plain(); - assert_eq!(&layout._name, "plain"); - assert_eq!(layout._cpu_component_step, 1); + assert_eq!(&layout.name, "plain"); assert_eq!(layout.rc_units, 16); assert_eq!(layout.builtins, builtins); - assert_eq!(layout._public_memory_fraction, 4); - assert_eq!(layout._memory_units_per_step, 8); + assert_eq!(layout.public_memory_fraction, 4); assert_eq!(layout.diluted_pool_instance_def, None); - assert_eq!(layout._n_trace_colums, 8); - assert_eq!(layout._cpu_instance_def, CpuInstanceDef::default()); } #[test] @@ -192,15 +143,11 @@ mod tests { fn get_small_instance() { let layout = CairoLayout::small_instance(); let builtins = BuiltinsInstanceDef::small(); - assert_eq!(&layout._name, "small"); - assert_eq!(layout._cpu_component_step, 1); + assert_eq!(&layout.name, "small"); assert_eq!(layout.rc_units, 16); assert_eq!(layout.builtins, builtins); - assert_eq!(layout._public_memory_fraction, 4); - assert_eq!(layout._memory_units_per_step, 8); + assert_eq!(layout.public_memory_fraction, 4); assert_eq!(layout.diluted_pool_instance_def, None); - assert_eq!(layout._n_trace_colums, 25); - assert_eq!(layout._cpu_instance_def, CpuInstanceDef::default()); } #[test] @@ -208,140 +155,108 @@ mod tests { fn get_dex_instance() { let layout = CairoLayout::dex_instance(); let builtins = BuiltinsInstanceDef::dex(); - assert_eq!(&layout._name, "dex"); - assert_eq!(layout._cpu_component_step, 1); + assert_eq!(&layout.name, "dex"); assert_eq!(layout.rc_units, 4); assert_eq!(layout.builtins, builtins); - assert_eq!(layout._public_memory_fraction, 4); - assert_eq!(layout._memory_units_per_step, 8); + assert_eq!(layout.public_memory_fraction, 4); assert_eq!(layout.diluted_pool_instance_def, None); - assert_eq!(layout._n_trace_colums, 22); - assert_eq!(layout._cpu_instance_def, CpuInstanceDef::default()); } #[test] fn get_recursive_instance() { let layout = CairoLayout::recursive_instance(); let builtins = BuiltinsInstanceDef::recursive(); - assert_eq!(&layout._name, "recursive"); - assert_eq!(layout._cpu_component_step, 1); + assert_eq!(&layout.name, "recursive"); assert_eq!(layout.rc_units, 4); assert_eq!(layout.builtins, builtins); - assert_eq!(layout._public_memory_fraction, 8); - assert_eq!(layout._memory_units_per_step, 8); + assert_eq!(layout.public_memory_fraction, 8); assert_eq!( layout.diluted_pool_instance_def, Some(DilutedPoolInstanceDef::default()) ); - assert_eq!(layout._n_trace_colums, 10); - assert_eq!(layout._cpu_instance_def, CpuInstanceDef::default()); } #[test] fn get_starknet_instance() { let layout = CairoLayout::starknet_instance(); let builtins = BuiltinsInstanceDef::starknet(); - assert_eq!(&layout._name, "starknet"); - assert_eq!(layout._cpu_component_step, 1); + assert_eq!(&layout.name, "starknet"); assert_eq!(layout.rc_units, 4); assert_eq!(layout.builtins, builtins); - assert_eq!(layout._public_memory_fraction, 8); - assert_eq!(layout._memory_units_per_step, 8); + assert_eq!(layout.public_memory_fraction, 8); assert_eq!( layout.diluted_pool_instance_def, Some(DilutedPoolInstanceDef::new(2, 4, 16)) ); - assert_eq!(layout._n_trace_colums, 10); - assert_eq!(layout._cpu_instance_def, CpuInstanceDef::default()); } #[test] fn get_starknet_with_keccak_instance() { let layout = CairoLayout::starknet_with_keccak_instance(); let builtins = BuiltinsInstanceDef::starknet_with_keccak(); - assert_eq!(&layout._name, "starknet_with_keccak"); - assert_eq!(layout._cpu_component_step, 1); + assert_eq!(&layout.name, "starknet_with_keccak"); assert_eq!(layout.rc_units, 4); assert_eq!(layout.builtins, builtins); - assert_eq!(layout._public_memory_fraction, 8); - assert_eq!(layout._memory_units_per_step, 8); + assert_eq!(layout.public_memory_fraction, 8); assert_eq!( layout.diluted_pool_instance_def, Some(DilutedPoolInstanceDef::default()) ); - assert_eq!(layout._n_trace_colums, 15); - assert_eq!(layout._cpu_instance_def, CpuInstanceDef::default()); } #[test] fn get_recursive_large_output_instance() { let layout = CairoLayout::recursive_large_output_instance(); let builtins = BuiltinsInstanceDef::recursive_large_output(); - assert_eq!(&layout._name, "recursive_large_output"); - assert_eq!(layout._cpu_component_step, 1); + assert_eq!(&layout.name, "recursive_large_output"); assert_eq!(layout.rc_units, 4); assert_eq!(layout.builtins, builtins); - assert_eq!(layout._public_memory_fraction, 8); - assert_eq!(layout._memory_units_per_step, 8); + assert_eq!(layout.public_memory_fraction, 8); assert_eq!( layout.diluted_pool_instance_def, Some(DilutedPoolInstanceDef::default()) ); - assert_eq!(layout._n_trace_colums, 13); - assert_eq!(layout._cpu_instance_def, CpuInstanceDef::default()); } #[test] fn get_all_cairo_instance() { let layout = CairoLayout::all_cairo_instance(); let builtins = BuiltinsInstanceDef::all_cairo(); - assert_eq!(&layout._name, "all_cairo"); - assert_eq!(layout._cpu_component_step, 1); + assert_eq!(&layout.name, "all_cairo"); assert_eq!(layout.rc_units, 4); assert_eq!(layout.builtins, builtins); - assert_eq!(layout._public_memory_fraction, 8); - assert_eq!(layout._memory_units_per_step, 8); + assert_eq!(layout.public_memory_fraction, 8); assert_eq!( layout.diluted_pool_instance_def, Some(DilutedPoolInstanceDef::default()) ); - assert_eq!(layout._n_trace_colums, 11); - assert_eq!(layout._cpu_instance_def, CpuInstanceDef::default()); } #[test] fn get_all_solidity_instance() { let layout = CairoLayout::all_solidity_instance(); let builtins = BuiltinsInstanceDef::all_solidity(); - assert_eq!(&layout._name, "all_solidity"); - assert_eq!(layout._cpu_component_step, 1); + assert_eq!(&layout.name, "all_solidity"); assert_eq!(layout.rc_units, 8); assert_eq!(layout.builtins, builtins); - assert_eq!(layout._public_memory_fraction, 8); - assert_eq!(layout._memory_units_per_step, 8); + assert_eq!(layout.public_memory_fraction, 8); assert_eq!( layout.diluted_pool_instance_def, Some(DilutedPoolInstanceDef::default()) ); - assert_eq!(layout._n_trace_colums, 27); - assert_eq!(layout._cpu_instance_def, CpuInstanceDef::default()); } #[test] fn get_dynamic_instance() { let layout = CairoLayout::dynamic_instance(); let builtins = BuiltinsInstanceDef::dynamic(); - assert_eq!(&layout._name, "dynamic"); - assert_eq!(layout._cpu_component_step, 1); + assert_eq!(&layout.name, "dynamic"); assert_eq!(layout.rc_units, 16); assert_eq!(layout.builtins, builtins); - assert_eq!(layout._public_memory_fraction, 8); - assert_eq!(layout._memory_units_per_step, 8); + assert_eq!(layout.public_memory_fraction, 8); assert_eq!( layout.diluted_pool_instance_def, Some(DilutedPoolInstanceDef::default()) ); - assert_eq!(layout._n_trace_colums, 73); - assert_eq!(layout._cpu_instance_def, CpuInstanceDef::default()); } } diff --git a/vm/src/types/program.rs b/vm/src/types/program.rs index 80747d80cf..f5399d059a 100644 --- a/vm/src/types/program.rs +++ b/vm/src/types/program.rs @@ -308,6 +308,22 @@ impl Program { self.shared_program_data.identifiers.get(id) } + pub fn get_relocated_instruction_locations( + &self, + relocation_table: &[usize], + ) -> Option> { + self.shared_program_data.instruction_locations.as_ref()?; + let relocated_instructions = self + .shared_program_data + .instruction_locations + .as_ref() + .unwrap() + .iter() + .map(|(k, v)| (k + relocation_table[0], v.clone())) + .collect(); + Some(relocated_instructions) + } + pub fn iter_identifiers(&self) -> impl Iterator { self.shared_program_data .identifiers @@ -479,7 +495,7 @@ mod tests { use super::*; use crate::felt_hex; - use crate::serde::deserialize_program::{ApTracking, FlowTrackingData}; + use crate::serde::deserialize_program::{ApTracking, FlowTrackingData, InputFile, Location}; use crate::utils::test_utils::*; use assert_matches::assert_matches; @@ -945,6 +961,61 @@ mod tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_relocated_instruction_locations() { + fn build_instruction_location_for_test(start_line: u32) -> InstructionLocation { + InstructionLocation { + inst: Location { + end_line: 0, + end_col: 0, + input_file: InputFile { + filename: String::from("test"), + }, + parent_location: None, + start_line, + start_col: 0, + }, + hints: vec![], + } + } + + let reference_manager = ReferenceManager { + references: Vec::new(), + }; + let builtins: Vec = Vec::new(); + let data: Vec = vec![]; + let identifiers: HashMap = HashMap::new(); + let mut instruction_locations: HashMap = HashMap::new(); + + let il_1 = build_instruction_location_for_test(0); + let il_2 = build_instruction_location_for_test(2); + let il_3 = build_instruction_location_for_test(3); + instruction_locations.insert(5, il_1.clone()); + instruction_locations.insert(10, il_2.clone()); + instruction_locations.insert(12, il_3.clone()); + + let program = Program::new( + builtins, + data, + None, + HashMap::new(), + reference_manager, + identifiers, + Vec::new(), + Some(instruction_locations), + ) + .unwrap(); + + let relocated_instructions = program.get_relocated_instruction_locations(&[2]); + assert!(relocated_instructions.is_some()); + let relocated_instructions = relocated_instructions.unwrap(); + assert_eq!(relocated_instructions.len(), 3); + assert_eq!(relocated_instructions.get(&7), Some(&il_1)); + assert_eq!(relocated_instructions.get(&12), Some(&il_2)); + assert_eq!(relocated_instructions.get(&14), Some(&il_3)); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn iter_identifiers() { diff --git a/vm/src/types/relocatable.rs b/vm/src/types/relocatable.rs index 4bac0bba46..e569ce7707 100644 --- a/vm/src/types/relocatable.rs +++ b/vm/src/types/relocatable.rs @@ -127,6 +127,13 @@ impl AddAssign for Relocatable { } } +impl Add for Relocatable { + type Output = Result; + fn add(self, other: u32) -> Result { + self + other as usize + } +} + impl Add for Relocatable { type Output = Result; fn add(self, other: i32) -> Result { @@ -225,15 +232,8 @@ impl MaybeRelocatable { pub fn add_int(&self, other: &Felt252) -> Result { match *self { MaybeRelocatable::Int(ref value) => Ok(MaybeRelocatable::Int(value + other)), - MaybeRelocatable::RelocatableValue(ref rel) => { - let big_offset = other + rel.offset as u64; - let new_offset = big_offset.to_usize().ok_or_else(|| { - MathError::RelocatableAddFelt252OffsetExceeded(Box::new((*rel, *other))) - })?; - Ok(MaybeRelocatable::RelocatableValue(Relocatable { - segment_index: rel.segment_index, - offset: new_offset, - })) + MaybeRelocatable::RelocatableValue(rel) => { + Ok(MaybeRelocatable::RelocatableValue((rel + other)?)) } } } @@ -264,6 +264,14 @@ impl MaybeRelocatable { } } + /// Subs a usize from self + pub fn sub_usize(&self, other: usize) -> Result { + Ok(match *self { + MaybeRelocatable::Int(ref value) => MaybeRelocatable::Int(value - other as u64), + MaybeRelocatable::RelocatableValue(rel) => (rel - other)?.into(), + }) + } + /// Substracts two MaybeRelocatable values and returns the result as a MaybeRelocatable value. /// Only values of the same type may be substracted. /// Relocatable values can only be substracted if they belong to the same segment. diff --git a/vm/src/utils.rs b/vm/src/utils.rs index eeeec33894..adb68cbc07 100644 --- a/vm/src/utils.rs +++ b/vm/src/utils.rs @@ -121,14 +121,9 @@ pub mod test_utils { macro_rules! segments { ($( (($si:expr, $off:expr), $val:tt) ),* $(,)? ) => { { - let memory = memory!($( (($si, $off), $val) ),*); - $crate::vm::vm_memory::memory_segments::MemorySegmentManager { - memory, - segment_sizes: HashMap::new(), - segment_used_sizes: None, - public_memory_offsets: HashMap::new(), - } - + let mut segments = $crate::vm::vm_memory::memory_segments::MemorySegmentManager::new(); + segments.memory = memory!($( (($si, $off), $val) ),*); + segments } }; @@ -241,8 +236,11 @@ pub mod test_utils { () => {{ let mut vm = VirtualMachine::new(false); vm.builtin_runners = vec![ - $crate::vm::runners::builtin_runner::RangeCheckBuiltinRunner::new(Some(8), 8, true) - .into(), + $crate::vm::runners::builtin_runner::RangeCheckBuiltinRunner::<8>::new( + Some(8), + true, + ) + .into(), ]; vm }}; @@ -442,7 +440,7 @@ pub mod test_utils { macro_rules! exec_scopes_ref { () => { - &mut ExecutionScopes::new() + &mut crate::types::exec_scope::ExecutionScopes::new() }; } pub(crate) use exec_scopes_ref; diff --git a/vm/src/vm/context/run_context.rs b/vm/src/vm/context/run_context.rs index 32bad1fec8..d5de5d41f5 100644 --- a/vm/src/vm/context/run_context.rs +++ b/vm/src/vm/context/run_context.rs @@ -26,6 +26,10 @@ impl RunContext { self.pc } + pub fn new(pc: Relocatable, ap: usize, fp: usize) -> Self { + RunContext { pc, ap, fp } + } + pub fn compute_dst_addr( &self, instruction: &Instruction, diff --git a/vm/src/vm/errors/runner_errors.rs b/vm/src/vm/errors/runner_errors.rs index dc1533f509..b0961d69df 100644 --- a/vm/src/vm/errors/runner_errors.rs +++ b/vm/src/vm/errors/runner_errors.rs @@ -102,6 +102,30 @@ pub enum RunnerError { Trace(#[from] TraceError), #[error("EcOp builtin: Invalid Point")] InvalidPoint, + #[error("Page ({0}) is not on the expected segment {1}")] + PageNotOnSegment(Relocatable, usize), + #[error("Expected integer at address {} to be smaller than 2^{}. Got: {}.", (*.0).0, (*.0).1, (*.0).2)] + WordExceedsModBuiltinWordBitLen(Box<(Relocatable, u32, Felt252)>), + #[error("{}: Expected n >= 1. Got: {}.", (*.0).0, (*.0).1)] + ModBuiltinNLessThanOne(Box<(&'static str, usize)>), + #[error("{}: Missing value at address {}.", (*.0).0, (*.0).1)] + ModBuiltinMissingValue(Box<(&'static str, Relocatable)>), + #[error("{}: n must be <= {}", (*.0).0, (*.0).1)] + FillMemoryMaxExceeded(Box<(&'static str, usize)>), + #[error("{0}: write_n_words value must be 0 after loop")] + WriteNWordsValueNotZero(&'static str), + #[error("add_mod and mul_mod builtins must have the same n_words and word_bit_len.")] + ModBuiltinsMismatchedInstanceDef, + #[error("At least one of add_mod and mul_mod must be given.")] + FillMemoryNoBuiltinSet, + #[error("Could not fill the values table, add_mod_index={0}, mul_mod_index={1}")] + FillMemoryCoudNotFillTable(usize, usize), + #[error("{}: {}", (*.0).0, (*.0).1)] + ModBuiltinSecurityCheck(Box<(&'static str, String)>), + #[error("{0} is missing")] + MissingBuiltin(&'static str), + #[error("The stop pointer of the missing builtin {0} must be 0")] + MissingBuiltinStopPtrNotZero(&'static str), } #[cfg(test)] diff --git a/vm/src/vm/errors/vm_errors.rs b/vm/src/vm/errors/vm_errors.rs index db8f0278e4..68b8ceed45 100644 --- a/vm/src/vm/errors/vm_errors.rs +++ b/vm/src/vm/errors/vm_errors.rs @@ -81,10 +81,14 @@ pub enum VirtualMachineError { InconsistentAutoDeduction(Box<(&'static str, MaybeRelocatable, Option)>), #[error("Invalid hint encoding at pc: {0}")] InvalidHintEncoding(Box), + #[error("Expected output builtin to be present")] + NoOutputBuiltin, #[error("Expected range_check builtin to be present")] NoRangeCheckBuiltin, #[error("Expected ecdsa builtin to be present")] NoSignatureBuiltin, + #[error("Expected {0} to be present")] + NoModBuiltin(&'static str), #[error("Div out of range: 0 < {} <= {}", (*.0).0, (*.0).1)] OutOfValidRange(Box<(Felt252, Felt252)>), #[error("Failed to compare {} and {}, cant compare a relocatable to an integer value", (*.0).0, (*.0).1)] @@ -129,6 +133,8 @@ pub enum VirtualMachineError { FailedToWriteOutput, #[error("Failed to find index {0} in the vm's relocation table")] RelocationNotFound(usize), + #[error("{} batch size is not {}", (*.0).0, (*.0).1)] + ModBuiltinBatchSize(Box<(&'static str, usize)>), } #[cfg(test)] diff --git a/vm/src/vm/runners/builtin_runner/bitwise.rs b/vm/src/vm/runners/builtin_runner/bitwise.rs index ca9e1e01cc..e5901194db 100644 --- a/vm/src/vm/runners/builtin_runner/bitwise.rs +++ b/vm/src/vm/runners/builtin_runner/bitwise.rs @@ -3,9 +3,7 @@ use crate::stdlib::{boxed::Box, vec::Vec}; use crate::Felt252; use crate::{ types::{ - instance_definitions::bitwise_instance_def::{ - BitwiseInstanceDef, CELLS_PER_BITWISE, INPUT_CELLS_PER_BITWISE, - }, + instance_definitions::bitwise_instance_def::{CELLS_PER_BITWISE, TOTAL_N_BITS}, relocatable::{MaybeRelocatable, Relocatable}, }, vm::{ @@ -15,31 +13,21 @@ use crate::{ }; use num_integer::div_ceil; -use super::BITWISE_BUILTIN_NAME; - #[derive(Debug, Clone)] pub struct BitwiseBuiltinRunner { ratio: Option, pub base: usize, - pub(crate) cells_per_instance: u32, - pub(crate) n_input_cells: u32, - bitwise_builtin: BitwiseInstanceDef, pub(crate) stop_ptr: Option, pub(crate) included: bool, - pub(crate) instances_per_component: u32, } impl BitwiseBuiltinRunner { - pub(crate) fn new(instance_def: &BitwiseInstanceDef, included: bool) -> Self { + pub(crate) fn new(ratio: Option, included: bool) -> Self { BitwiseBuiltinRunner { base: 0, - ratio: instance_def.ratio, - cells_per_instance: CELLS_PER_BITWISE, - n_input_cells: INPUT_CELLS_PER_BITWISE, - bitwise_builtin: instance_def.clone(), + ratio, stop_ptr: None, included, - instances_per_component: 1, } } @@ -63,14 +51,12 @@ impl BitwiseBuiltinRunner { self.ratio } - pub fn add_validation_rule(&self, _memory: &mut Memory) {} - pub fn deduce_memory_cell( &self, address: Relocatable, memory: &Memory, ) -> Result, RunnerError> { - let index = address.offset % self.cells_per_instance as usize; + let index = address.offset % CELLS_PER_BITWISE as usize; if index <= 1 { return Ok(None); } @@ -90,7 +76,7 @@ impl BitwiseBuiltinRunner { if limbs[3] & LEADING_BITS != 0 { return Err(RunnerError::IntegerBiggerThanPowerOfTwo(Box::new(( x_addr, - self.bitwise_builtin.total_n_bits, + TOTAL_N_BITS, *x, )))); } @@ -121,10 +107,6 @@ impl BitwiseBuiltinRunner { )))) } - pub fn get_memory_segment_addresses(&self) -> (usize, Option) { - (self.base, self.stop_ptr) - } - pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result { segments .get_segment_used_size(self.base) @@ -132,7 +114,7 @@ impl BitwiseBuiltinRunner { } pub fn get_used_diluted_check_units(&self, diluted_spacing: u32, diluted_n_bits: u32) -> usize { - let total_n_bits = self.bitwise_builtin.total_n_bits; + let total_n_bits = TOTAL_N_BITS; let mut partition = Vec::with_capacity(total_n_bits as usize); for i in (0..total_n_bits).step_by((diluted_spacing * diluted_n_bits) as usize) { for j in 0..diluted_spacing { @@ -149,49 +131,12 @@ impl BitwiseBuiltinRunner { 4 * partition_lengh + num_trimmed } - pub fn final_stack( - &mut self, - segments: &MemorySegmentManager, - pointer: Relocatable, - ) -> Result { - if self.included { - let stop_pointer_addr = (pointer - 1) - .map_err(|_| RunnerError::NoStopPointer(Box::new(BITWISE_BUILTIN_NAME)))?; - let stop_pointer = segments - .memory - .get_relocatable(stop_pointer_addr) - .map_err(|_| RunnerError::NoStopPointer(Box::new(BITWISE_BUILTIN_NAME)))?; - if self.base as isize != stop_pointer.segment_index { - return Err(RunnerError::InvalidStopPointerIndex(Box::new(( - BITWISE_BUILTIN_NAME, - stop_pointer, - self.base, - )))); - } - let stop_ptr = stop_pointer.offset; - let num_instances = self.get_used_instances(segments)?; - let used = num_instances * self.cells_per_instance as usize; - if stop_ptr != used { - return Err(RunnerError::InvalidStopPointer(Box::new(( - BITWISE_BUILTIN_NAME, - Relocatable::from((self.base as isize, used)), - Relocatable::from((self.base as isize, stop_ptr)), - )))); - } - self.stop_ptr = Some(stop_ptr); - Ok(stop_pointer_addr) - } else { - self.stop_ptr = Some(0); - Ok(pointer) - } - } - pub fn get_used_instances( &self, segments: &MemorySegmentManager, ) -> Result { let used_cells = self.get_used_cells(segments)?; - Ok(div_ceil(used_cells, self.cells_per_instance as usize)) + Ok(div_ceil(used_cells, CELLS_PER_BITWISE as usize)) } pub fn air_private_input(&self, memory: &Memory) -> Vec { @@ -224,9 +169,8 @@ mod tests { use super::*; use crate::relocatable; use crate::serde::deserialize_program::BuiltinName; - use crate::stdlib::collections::HashMap; use crate::vm::errors::memory_errors::MemoryError; - use crate::vm::runners::builtin_runner::BuiltinRunner; + use crate::vm::runners::builtin_runner::{BuiltinRunner, BITWISE_BUILTIN_NAME}; use crate::vm::vm_core::VirtualMachine; use crate::Felt252; use crate::{ @@ -240,7 +184,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_instances() { - let builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::new(Some(10)), true); + let builtin = BitwiseBuiltinRunner::new(Some(10), true); let mut vm = vm!(); @@ -259,7 +203,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack() { - let mut builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::new(Some(10)), true); + let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -283,7 +227,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_stop_pointer() { - let mut builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::new(Some(10)), true); + let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -311,7 +255,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_when_notincluded() { - let mut builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::new(Some(10)), false); + let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), false).into(); let mut vm = vm!(); @@ -335,7 +279,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_non_relocatable() { - let mut builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::new(Some(10)), true); + let mut builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -359,8 +303,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_and_allocated_size_test() { - let builtin: BuiltinRunner = - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::new(Some(10)), true).into(); + let builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -406,8 +349,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units() { - let builtin: BuiltinRunner = - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::new(Some(10)), true).into(); + let builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -452,7 +394,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn deduce_memory_cell_bitwise_for_preset_memory_valid_and() { let memory = memory![((0, 5), 10), ((0, 6), 12), ((0, 7), 0)]; - let builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); + let builtin = BitwiseBuiltinRunner::new(Some(256), true); let result = builtin.deduce_memory_cell(Relocatable::from((0, 7)), &memory); assert_eq!(result, Ok(Some(MaybeRelocatable::from(Felt252::from(8))))); } @@ -461,7 +403,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn deduce_memory_cell_bitwise_for_preset_memory_valid_xor() { let memory = memory![((0, 5), 10), ((0, 6), 12), ((0, 8), 0)]; - let builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); + let builtin = BitwiseBuiltinRunner::new(Some(256), true); let result = builtin.deduce_memory_cell(Relocatable::from((0, 8)), &memory); assert_eq!(result, Ok(Some(MaybeRelocatable::from(Felt252::from(6))))); } @@ -470,7 +412,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn deduce_memory_cell_bitwise_for_preset_memory_valid_or() { let memory = memory![((0, 5), 10), ((0, 6), 12), ((0, 9), 0)]; - let builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); + let builtin = BitwiseBuiltinRunner::new(Some(256), true); let result = builtin.deduce_memory_cell(Relocatable::from((0, 9)), &memory); assert_eq!(result, Ok(Some(MaybeRelocatable::from(Felt252::from(14))))); } @@ -479,7 +421,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn deduce_memory_cell_bitwise_for_preset_memory_incorrect_offset() { let memory = memory![((0, 3), 10), ((0, 4), 12), ((0, 5), 0)]; - let builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); + let builtin = BitwiseBuiltinRunner::new(Some(256), true); let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory); assert_eq!(result, Ok(None)); } @@ -488,75 +430,15 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn deduce_memory_cell_bitwise_for_preset_memory_no_values_to_operate() { let memory = memory![((0, 5), 12), ((0, 7), 0)]; - let builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); + let builtin = BitwiseBuiltinRunner::new(Some(256), true); let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory); assert_eq!(result, Ok(None)); } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_segment_addresses() { - let builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); - - assert_eq!(builtin.get_memory_segment_addresses(), (0, None),); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_missing_segment_used_sizes() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); - let vm = vm!(); - - assert_eq!( - builtin.get_memory_accesses(&vm), - Err(MemoryError::MissingSegmentUsedSizes), - ); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_empty() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![0]); - assert_eq!(builtin.get_memory_accesses(&vm), Ok(vec![])); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![4]); - assert_eq!( - builtin.get_memory_accesses(&vm), - Ok(vec![ - (builtin.base() as isize, 0).into(), - (builtin.base() as isize, 1).into(), - (builtin.base() as isize, 2).into(), - (builtin.base() as isize, 3).into(), - ]), - ); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_missing_segment_used_sizes() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); let vm = vm!(); assert_eq!( @@ -568,10 +450,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_empty() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![0]); @@ -581,10 +460,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![4]); @@ -594,40 +470,30 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_diluted_check_units_a() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); assert_eq!(builtin.get_used_diluted_check_units(12, 2), 535); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_diluted_check_units_b() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); assert_eq!(builtin.get_used_diluted_check_units(30, 56), 150); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_diluted_check_units_c() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); assert_eq!(builtin.get_used_diluted_check_units(50, 25), 250); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_air_private_input() { - let builtin: BuiltinRunner = - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true).into(); + let builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(256), true).into(); - let memory = memory![ + let segments = segments![ ((0, 0), 0), ((0, 1), 1), ((0, 2), 2), @@ -645,7 +511,7 @@ mod tests { ((0, 14), 14) ]; assert_eq!( - builtin.air_private_input(&memory), + builtin.air_private_input(&segments), (vec![ PrivateInput::Pair(PrivateInputPair { index: 0, diff --git a/vm/src/vm/runners/builtin_runner/ec_op.rs b/vm/src/vm/runners/builtin_runner/ec_op.rs index 37ec430e12..8dd50f0ffa 100644 --- a/vm/src/vm/runners/builtin_runner/ec_op.rs +++ b/vm/src/vm/runners/builtin_runner/ec_op.rs @@ -2,7 +2,7 @@ use crate::air_private_input::{PrivateInput, PrivateInputEcOp}; use crate::stdlib::{borrow::Cow, prelude::*}; use crate::stdlib::{cell::RefCell, collections::HashMap}; use crate::types::instance_definitions::ec_op_instance_def::{ - EcOpInstanceDef, CELLS_PER_EC_OP, INPUT_CELLS_PER_EC_OP, + CELLS_PER_EC_OP, INPUT_CELLS_PER_EC_OP, SCALAR_HEIGHT, }; use crate::types::relocatable::{MaybeRelocatable, Relocatable}; use crate::vm::errors::memory_errors::MemoryError; @@ -13,32 +13,22 @@ use crate::Felt252; use num_integer::{div_ceil, Integer}; use starknet_types_core::curve::ProjectivePoint; -use super::EC_OP_BUILTIN_NAME; - #[derive(Debug, Clone)] pub struct EcOpBuiltinRunner { ratio: Option, pub base: usize, - pub(crate) cells_per_instance: u32, - pub(crate) n_input_cells: u32, - ec_op_builtin: EcOpInstanceDef, pub(crate) stop_ptr: Option, pub(crate) included: bool, - pub(crate) instances_per_component: u32, cache: RefCell>, } impl EcOpBuiltinRunner { - pub(crate) fn new(instance_def: &EcOpInstanceDef, included: bool) -> Self { + pub(crate) fn new(ratio: Option, included: bool) -> Self { EcOpBuiltinRunner { base: 0, - ratio: instance_def.ratio, - n_input_cells: INPUT_CELLS_PER_EC_OP, - cells_per_instance: CELLS_PER_EC_OP, - ec_op_builtin: instance_def.clone(), + ratio, stop_ptr: None, included, - instances_per_component: 1, cache: RefCell::new(HashMap::new()), } } @@ -104,8 +94,6 @@ impl EcOpBuiltinRunner { self.ratio } - pub fn add_validation_rule(&self, _memory: &mut Memory) {} - pub fn deduce_memory_cell( &self, address: Relocatable, @@ -119,9 +107,7 @@ impl EcOpBuiltinRunner { let beta_high: Felt252 = Felt252::from(0x6f21413efbe40de150e596d72f7a8c5_u128); let beta: Felt252 = (beta_high * (Felt252::ONE + Felt252::from(u128::MAX))) + beta_low; - let index = address - .offset - .mod_floor(&(self.cells_per_instance as usize)); + let index = address.offset.mod_floor(&(CELLS_PER_EC_OP as usize)); //Index should be an output cell if index != OUTPUT_INDICES.0 && index != OUTPUT_INDICES.1 { return Ok(None); @@ -136,8 +122,8 @@ impl EcOpBuiltinRunner { //All input cells should be filled, and be integer values //If an input cell is not filled, return None - let mut input_cells = Vec::<&Felt252>::with_capacity(self.n_input_cells as usize); - for i in 0..self.n_input_cells as usize { + let mut input_cells = Vec::<&Felt252>::with_capacity(INPUT_CELLS_PER_EC_OP as usize); + for i in 0..INPUT_CELLS_PER_EC_OP as usize { match memory.get(&(instance + i)?) { None => return Ok(None), Some(addr) => { @@ -178,7 +164,7 @@ impl EcOpBuiltinRunner { (input_cells[0].to_owned(), input_cells[1].to_owned()), (input_cells[2].to_owned(), input_cells[3].to_owned()), input_cells[4], - self.ec_op_builtin.scalar_height, + SCALAR_HEIGHT, )?; self.cache.borrow_mut().insert(x_addr, result.0); self.cache.borrow_mut().insert( @@ -186,17 +172,13 @@ impl EcOpBuiltinRunner { .map_err(|_| RunnerError::Memory(MemoryError::ExpectedInteger(Box::new(x_addr))))?, result.1, ); - match index - self.n_input_cells as usize { + match index - INPUT_CELLS_PER_EC_OP as usize { 0 => Ok(Some(MaybeRelocatable::Int(result.0))), _ => Ok(Some(MaybeRelocatable::Int(result.1))), //Default case corresponds to 1, as there are no other possible cases } } - pub fn get_memory_segment_addresses(&self) -> (usize, Option) { - (self.base, self.stop_ptr) - } - pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result { segments .get_segment_used_size(self.base()) @@ -208,44 +190,7 @@ impl EcOpBuiltinRunner { segments: &MemorySegmentManager, ) -> Result { let used_cells = self.get_used_cells(segments)?; - Ok(div_ceil(used_cells, self.cells_per_instance as usize)) - } - - pub fn final_stack( - &mut self, - segments: &MemorySegmentManager, - pointer: Relocatable, - ) -> Result { - if self.included { - let stop_pointer_addr = (pointer - 1) - .map_err(|_| RunnerError::NoStopPointer(Box::new(EC_OP_BUILTIN_NAME)))?; - let stop_pointer = segments - .memory - .get_relocatable(stop_pointer_addr) - .map_err(|_| RunnerError::NoStopPointer(Box::new(EC_OP_BUILTIN_NAME)))?; - if self.base as isize != stop_pointer.segment_index { - return Err(RunnerError::InvalidStopPointerIndex(Box::new(( - EC_OP_BUILTIN_NAME, - stop_pointer, - self.base, - )))); - } - let stop_ptr = stop_pointer.offset; - let num_instances = self.get_used_instances(segments)?; - let used = num_instances * self.cells_per_instance as usize; - if stop_ptr != used { - return Err(RunnerError::InvalidStopPointer(Box::new(( - EC_OP_BUILTIN_NAME, - Relocatable::from((self.base as isize, used)), - Relocatable::from((self.base as isize, stop_ptr)), - )))); - } - self.stop_ptr = Some(stop_ptr); - Ok(stop_pointer_addr) - } else { - self.stop_ptr = Some(0); - Ok(pointer) - } + Ok(div_ceil(used_cells, CELLS_PER_EC_OP as usize)) } pub fn format_ec_op_error( @@ -298,11 +243,11 @@ mod tests { use super::*; use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; use crate::serde::deserialize_program::BuiltinName; - use crate::stdlib::collections::HashMap; use crate::types::program::Program; use crate::utils::test_utils::*; use crate::vm::errors::cairo_run_errors::CairoRunError; use crate::vm::errors::vm_errors::VirtualMachineError; + use crate::vm::runners::builtin_runner::EC_OP_BUILTIN_NAME; use crate::vm::runners::cairo_runner::CairoRunner; use crate::{felt_hex, felt_str, relocatable}; @@ -319,7 +264,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_instances() { - let builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::new(Some(10)), true); + let builtin = EcOpBuiltinRunner::new(Some(10), true); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![1]); @@ -330,7 +275,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack() { - let mut builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::new(Some(10)), true); + let mut builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -354,7 +299,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_stop_pointer() { - let mut builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::new(Some(10)), true); + let mut builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -382,7 +327,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_when_notincluded() { - let mut builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::new(Some(10)), false); + let mut builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), false).into(); let mut vm = vm!(); @@ -406,7 +351,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_non_relocatable() { - let mut builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::new(Some(10)), true); + let mut builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -430,8 +375,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_and_allocated_size_test() { - let builtin: BuiltinRunner = - EcOpBuiltinRunner::new(&EcOpInstanceDef::new(Some(10)), true).into(); + let builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -476,8 +420,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units() { - let builtin: BuiltinRunner = - EcOpBuiltinRunner::new(&EcOpInstanceDef::new(Some(10)), true).into(); + let builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -696,7 +639,7 @@ mod tests { ) ) ]; - let builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let builtin = EcOpBuiltinRunner::new(Some(256), true); let result = builtin.deduce_memory_cell(Relocatable::from((3, 6)), &memory); assert_eq!( @@ -742,7 +685,7 @@ mod tests { ) ]; - let builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let builtin = EcOpBuiltinRunner::new(Some(256), true); let result = builtin.deduce_memory_cell(Relocatable::from((3, 6)), &memory); assert_eq!(result, Ok(None)); } @@ -788,7 +731,7 @@ mod tests { ) ) ]; - let builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let builtin = EcOpBuiltinRunner::new(Some(256), true); let result = builtin.deduce_memory_cell(Relocatable::from((3, 3)), &memory); assert_eq!(result, Ok(None)); @@ -829,7 +772,7 @@ mod tests { ) ) ]; - let builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let builtin = EcOpBuiltinRunner::new(Some(256), true); assert_eq!( builtin.deduce_memory_cell(Relocatable::from((3, 6)), &memory), @@ -839,62 +782,10 @@ mod tests { ); } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_segment_addresses() { - let builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); - - assert_eq!(builtin.get_memory_segment_addresses(), (0, None)); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_missing_segment_used_sizes() { - let builtin = - BuiltinRunner::EcOp(EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true)); - let vm = vm!(); - - assert_eq!( - builtin.get_memory_accesses(&vm), - Err(MemoryError::MissingSegmentUsedSizes), - ); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_empty() { - let builtin = - BuiltinRunner::EcOp(EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true)); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![0]); - assert_eq!(builtin.get_memory_accesses(&vm), Ok(vec![])); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses() { - let builtin = - BuiltinRunner::EcOp(EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true)); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![4]); - assert_eq!( - builtin.get_memory_accesses(&vm), - Ok(vec![ - (builtin.base() as isize, 0).into(), - (builtin.base() as isize, 1).into(), - (builtin.base() as isize, 2).into(), - (builtin.base() as isize, 3).into(), - ]), - ); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_missing_segment_used_sizes() { - let builtin = - BuiltinRunner::EcOp(EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true)); + let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(256), true)); let vm = vm!(); assert_eq!( @@ -906,8 +797,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_empty() { - let builtin = - BuiltinRunner::EcOp(EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true)); + let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(256), true)); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![0]); @@ -917,8 +807,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells() { - let builtin = - BuiltinRunner::EcOp(EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true)); + let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(256), true)); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![4]); @@ -928,15 +817,14 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn initial_stackincluded_test() { - let ec_op_builtin: BuiltinRunner = - EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true).into(); + let ec_op_builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(256), true).into(); assert_eq!(ec_op_builtin.initial_stack(), vec![mayberelocatable!(0, 0)]) } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn initial_stack_notincluded_test() { - let ec_op_builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), false); + let ec_op_builtin = EcOpBuiltinRunner::new(Some(256), false); assert_eq!(ec_op_builtin.initial_stack(), Vec::new()) } @@ -994,10 +882,9 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_air_private_input() { - let builtin: BuiltinRunner = - EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true).into(); + let builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(256), true).into(); - let memory = memory![ + let segments = segments![ ((0, 0), 0), ((0, 1), 1), ((0, 2), 2), @@ -1005,7 +892,7 @@ mod tests { ((0, 4), 4) ]; assert_eq!( - builtin.air_private_input(&memory), + builtin.air_private_input(&segments), (vec![PrivateInput::EcOp(PrivateInputEcOp { index: 0, p_x: 0.into(), diff --git a/vm/src/vm/runners/builtin_runner/hash.rs b/vm/src/vm/runners/builtin_runner/hash.rs index 3ec2aabf27..bd641f2690 100644 --- a/vm/src/vm/runners/builtin_runner/hash.rs +++ b/vm/src/vm/runners/builtin_runner/hash.rs @@ -1,30 +1,21 @@ use crate::air_private_input::{PrivateInput, PrivateInputPair}; use crate::stdlib::{cell::RefCell, prelude::*}; -use crate::types::errors::math_errors::MathError; -use crate::types::instance_definitions::pedersen_instance_def::{ - CELLS_PER_HASH, INPUT_CELLS_PER_HASH, -}; +use crate::types::instance_definitions::pedersen_instance_def::CELLS_PER_HASH; use crate::types::relocatable::{MaybeRelocatable, Relocatable}; use crate::vm::errors::memory_errors::MemoryError; use crate::vm::errors::runner_errors::RunnerError; use crate::vm::runners::cairo_pie::BuiltinAdditionalData; use crate::vm::vm_memory::memory::Memory; use crate::vm::vm_memory::memory_segments::MemorySegmentManager; -use crate::Felt252; use num_integer::{div_ceil, Integer}; -use starknet_crypto::{pedersen_hash, FieldElement}; - -use super::HASH_BUILTIN_NAME; +use starknet_types_core::hash::StarkHash; #[derive(Debug, Clone)] pub struct HashBuiltinRunner { pub base: usize, ratio: Option, - pub(crate) cells_per_instance: u32, - pub(crate) n_input_cells: u32, pub(crate) stop_ptr: Option, pub(crate) included: bool, - pub(crate) instances_per_component: u32, // This act as a cache to optimize calls to deduce_memory_cell // Therefore need interior mutability // 1 at position 'n' means offset 'n' relative to base pointer @@ -37,12 +28,9 @@ impl HashBuiltinRunner { HashBuiltinRunner { base: 0, ratio, - cells_per_instance: CELLS_PER_HASH, - n_input_cells: INPUT_CELLS_PER_HASH, stop_ptr: None, verified_addresses: RefCell::new(Vec::new()), included, - instances_per_component: 1, } } @@ -66,17 +54,12 @@ impl HashBuiltinRunner { self.ratio } - pub fn add_validation_rule(&self, _memory: &mut Memory) {} - pub fn deduce_memory_cell( &self, address: Relocatable, memory: &Memory, ) -> Result, RunnerError> { - if address - .offset - .mod_floor(&(self.cells_per_instance as usize)) - != 2 + if address.offset.mod_floor(&(CELLS_PER_HASH as usize)) != 2 || *self .verified_addresses .borrow() @@ -104,30 +87,13 @@ impl HashBuiltinRunner { .resize(address.offset + 1, false); } self.verified_addresses.borrow_mut()[address.offset] = true; - - //Convert MaybeRelocatable to FieldElement - let a_be_bytes = num_a.to_bytes_be(); - let b_be_bytes = num_b.to_bytes_be(); - let (y, x) = match ( - FieldElement::from_bytes_be(&a_be_bytes), - FieldElement::from_bytes_be(&b_be_bytes), - ) { - (Ok(field_element_a), Ok(field_element_b)) => (field_element_a, field_element_b), - _ => return Err(MathError::ByteConversionError.into()), - }; //Compute pedersen Hash - let fe_result = pedersen_hash(&x, &y); - //Convert result from FieldElement to MaybeRelocatable - let result = Felt252::from_bytes_be(&fe_result.to_bytes_be()); + let result = starknet_types_core::hash::Pedersen::hash(num_b, num_a); return Ok(Some(MaybeRelocatable::from(result))); } Ok(None) } - pub fn get_memory_segment_addresses(&self) -> (usize, Option) { - (self.base, self.stop_ptr) - } - pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result { segments .get_segment_used_size(self.base()) @@ -139,44 +105,7 @@ impl HashBuiltinRunner { segments: &MemorySegmentManager, ) -> Result { let used_cells = self.get_used_cells(segments)?; - Ok(div_ceil(used_cells, self.cells_per_instance as usize)) - } - - pub fn final_stack( - &mut self, - segments: &MemorySegmentManager, - pointer: Relocatable, - ) -> Result { - if self.included { - let stop_pointer_addr = (pointer - 1) - .map_err(|_| RunnerError::NoStopPointer(Box::new(HASH_BUILTIN_NAME)))?; - let stop_pointer = segments - .memory - .get_relocatable(stop_pointer_addr) - .map_err(|_| RunnerError::NoStopPointer(Box::new(HASH_BUILTIN_NAME)))?; - if self.base as isize != stop_pointer.segment_index { - return Err(RunnerError::InvalidStopPointerIndex(Box::new(( - HASH_BUILTIN_NAME, - stop_pointer, - self.base, - )))); - } - let stop_ptr = stop_pointer.offset; - let num_instances = self.get_used_instances(segments)?; - let used = num_instances * self.cells_per_instance as usize; - if stop_ptr != used { - return Err(RunnerError::InvalidStopPointer(Box::new(( - HASH_BUILTIN_NAME, - Relocatable::from((self.base as isize, used)), - Relocatable::from((self.base as isize, stop_ptr)), - )))); - } - self.stop_ptr = Some(stop_ptr); - Ok(stop_pointer_addr) - } else { - self.stop_ptr = Some(0); - Ok(pointer) - } + Ok(div_ceil(used_cells, CELLS_PER_HASH as usize)) } pub fn get_additional_data(&self) -> BuiltinAdditionalData { @@ -219,9 +148,9 @@ mod tests { use super::*; use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; use crate::serde::deserialize_program::BuiltinName; - use crate::stdlib::collections::HashMap; use crate::types::program::Program; use crate::utils::test_utils::*; + use crate::vm::runners::builtin_runner::HASH_BUILTIN_NAME; use crate::vm::runners::cairo_runner::CairoRunner; use crate::{felt_hex, relocatable}; @@ -247,7 +176,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack() { - let mut builtin = HashBuiltinRunner::new(Some(10), true); + let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -271,7 +200,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_stop_pointer() { - let mut builtin = HashBuiltinRunner::new(Some(10), true); + let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -299,7 +228,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_when_not_included() { - let mut builtin = HashBuiltinRunner::new(Some(10), false); + let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), false).into(); let mut vm = vm!(); @@ -323,7 +252,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_non_relocatable() { - let mut builtin = HashBuiltinRunner::new(Some(10), true); + let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -481,54 +410,6 @@ mod tests { assert_eq!(result, Ok(None)); } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_segment_addresses() { - let builtin = HashBuiltinRunner::new(Some(256), true); - - assert_eq!(builtin.get_memory_segment_addresses(), (0, None),); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_missing_segment_used_sizes() { - let builtin = BuiltinRunner::Hash(HashBuiltinRunner::new(Some(256), true)); - let vm = vm!(); - - assert_eq!( - builtin.get_memory_accesses(&vm), - Err(MemoryError::MissingSegmentUsedSizes), - ); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_empty() { - let builtin = BuiltinRunner::Hash(HashBuiltinRunner::new(Some(256), true)); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![0]); - assert_eq!(builtin.get_memory_accesses(&vm), Ok(vec![])); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses() { - let builtin = BuiltinRunner::Hash(HashBuiltinRunner::new(Some(256), true)); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![4]); - assert_eq!( - builtin.get_memory_accesses(&vm), - Ok(vec![ - (builtin.base() as isize, 0).into(), - (builtin.base() as isize, 1).into(), - (builtin.base() as isize, 2).into(), - (builtin.base() as isize, 3).into(), - ]), - ); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_missing_segment_used_sizes() { @@ -578,7 +459,7 @@ mod tests { fn get_air_private_input() { let builtin: BuiltinRunner = HashBuiltinRunner::new(None, true).into(); - let memory = memory![ + let segments = segments![ ((0, 0), 0), ((0, 1), 1), ((0, 2), 2), @@ -591,7 +472,7 @@ mod tests { ((0, 9), 9) ]; assert_eq!( - builtin.air_private_input(&memory), + builtin.air_private_input(&segments), (vec![ PrivateInput::Pair(PrivateInputPair { index: 0, diff --git a/vm/src/vm/runners/builtin_runner/keccak.rs b/vm/src/vm/runners/builtin_runner/keccak.rs index 17e6e3d638..93daf92f82 100644 --- a/vm/src/vm/runners/builtin_runner/keccak.rs +++ b/vm/src/vm/runners/builtin_runner/keccak.rs @@ -1,45 +1,43 @@ use crate::air_private_input::{PrivateInput, PrivateInputKeccakState}; use crate::math_utils::safe_div_usize; use crate::stdlib::{cell::RefCell, collections::HashMap, prelude::*}; -use crate::types::instance_definitions::keccak_instance_def::KeccakInstanceDef; +use crate::types::instance_definitions::keccak_instance_def::{ + CELLS_PER_KECCAK, INPUT_CELLS_PER_KECCAK, +}; use crate::types::relocatable::{MaybeRelocatable, Relocatable}; use crate::vm::errors::memory_errors::MemoryError; use crate::vm::errors::runner_errors::RunnerError; -use crate::vm::vm_core::VirtualMachine; use crate::vm::vm_memory::memory::Memory; use crate::vm::vm_memory::memory_segments::MemorySegmentManager; use crate::Felt252; +use lazy_static::lazy_static; use num_bigint::BigUint; use num_integer::div_ceil; use super::KECCAK_BUILTIN_NAME; const KECCAK_FELT_BYTE_SIZE: usize = 25; // 200 / 8 +const BITS: u32 = 200; +lazy_static! { + static ref KECCAK_INPUT_MAX: Felt252 = Felt252::TWO.pow(BITS); +} #[derive(Debug, Clone)] pub struct KeccakBuiltinRunner { ratio: Option, pub base: usize, - pub(crate) cells_per_instance: u32, - pub(crate) n_input_cells: u32, pub(crate) stop_ptr: Option, pub(crate) included: bool, - state_rep: Vec, - pub(crate) instances_per_component: u32, cache: RefCell>, } impl KeccakBuiltinRunner { - pub(crate) fn new(instance_def: &KeccakInstanceDef, included: bool) -> Self { + pub(crate) fn new(ratio: Option, included: bool) -> Self { KeccakBuiltinRunner { base: 0, - ratio: instance_def.ratio, - n_input_cells: instance_def._state_rep.len() as u32, - cells_per_instance: instance_def.cells_per_builtin(), + ratio, stop_ptr: None, included, - instances_per_component: instance_def._instance_per_component, - state_rep: instance_def._state_rep.clone(), cache: RefCell::new(HashMap::new()), } } @@ -64,26 +62,24 @@ impl KeccakBuiltinRunner { self.ratio } - pub fn add_validation_rule(&self, _memory: &mut Memory) {} - pub fn deduce_memory_cell( &self, address: Relocatable, memory: &Memory, ) -> Result, RunnerError> { - let index = address.offset % self.cells_per_instance as usize; - if index < self.n_input_cells as usize { + let index = address.offset % CELLS_PER_KECCAK as usize; + if index < INPUT_CELLS_PER_KECCAK as usize { return Ok(None); } if let Some(felt) = self.cache.borrow().get(&address) { return Ok(Some(felt.into())); } let first_input_addr = (address - index)?; - let first_output_addr = (first_input_addr + self.n_input_cells as usize)?; + let first_output_addr = (first_input_addr + INPUT_CELLS_PER_KECCAK as usize)?; let mut input_felts = vec![]; - for i in 0..self.n_input_cells as usize { + for i in 0..INPUT_CELLS_PER_KECCAK as usize { let m_index = (first_input_addr + i)?; let val = match memory.get(&m_index) { Some(value) => { @@ -93,10 +89,10 @@ impl KeccakBuiltinRunner { KECCAK_BUILTIN_NAME, (first_input_addr + i)?, ))))?; - if num >= &(Felt252::TWO.pow(self.state_rep[i])) { + if num >= &KECCAK_INPUT_MAX { return Err(RunnerError::IntegerBiggerThanPowerOfTwo(Box::new(( (first_input_addr + i)?, - self.state_rep[i], + BITS, *num, )))); } @@ -117,8 +113,8 @@ impl KeccakBuiltinRunner { let keccak_result = Self::keccak_f(&input_message)?; let mut start_index = 0_usize; - for (i, bits) in self.state_rep.iter().enumerate() { - let end_index = start_index + *bits as usize / 8; + for i in 0..INPUT_CELLS_PER_KECCAK { + let end_index = start_index + BITS as usize / 8; self.cache.borrow_mut().insert((first_output_addr + i)?, { let mut bytes = keccak_result[start_index..end_index].to_vec(); bytes.resize(32, 0); @@ -129,10 +125,6 @@ impl KeccakBuiltinRunner { Ok(self.cache.borrow().get(&address).map(|x| x.into())) } - pub fn get_memory_segment_addresses(&self) -> (usize, Option) { - (self.base, self.stop_ptr) - } - pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result { segments .get_segment_used_size(self.base()) @@ -144,58 +136,7 @@ impl KeccakBuiltinRunner { segments: &MemorySegmentManager, ) -> Result { let used_cells = self.get_used_cells(segments)?; - Ok(div_ceil(used_cells, self.cells_per_instance as usize)) - } - - pub fn final_stack( - &mut self, - segments: &MemorySegmentManager, - pointer: Relocatable, - ) -> Result { - if self.included { - let stop_pointer_addr = (pointer - 1) - .map_err(|_| RunnerError::NoStopPointer(Box::new(KECCAK_BUILTIN_NAME)))?; - let stop_pointer = segments - .memory - .get_relocatable(stop_pointer_addr) - .map_err(|_| RunnerError::NoStopPointer(Box::new(KECCAK_BUILTIN_NAME)))?; - if self.base as isize != stop_pointer.segment_index { - return Err(RunnerError::InvalidStopPointerIndex(Box::new(( - KECCAK_BUILTIN_NAME, - stop_pointer, - self.base, - )))); - } - let stop_ptr = stop_pointer.offset; - let num_instances = self.get_used_instances(segments)?; - let used = num_instances * self.cells_per_instance as usize; - if stop_ptr != used { - return Err(RunnerError::InvalidStopPointer(Box::new(( - KECCAK_BUILTIN_NAME, - Relocatable::from((self.base as isize, used)), - Relocatable::from((self.base as isize, stop_ptr)), - )))); - } - self.stop_ptr = Some(stop_ptr); - Ok(stop_pointer_addr) - } else { - self.stop_ptr = Some(0); - Ok(pointer) - } - } - - pub fn get_memory_accesses( - &self, - vm: &VirtualMachine, - ) -> Result, MemoryError> { - let segment_size = vm - .segments - .get_segment_size(self.base) - .ok_or(MemoryError::MissingSegmentUsedSizes)?; - - Ok((0..segment_size) - .map(|i| (self.base as isize, i).into()) - .collect()) + Ok(div_ceil(used_cells, CELLS_PER_KECCAK as usize)) } pub fn get_used_diluted_check_units(&self, diluted_n_bits: u32) -> usize { @@ -227,7 +168,7 @@ impl KeccakBuiltinRunner { if let Some(segment) = memory.data.get(self.base) { let segment_len = segment.len(); for (index, off) in (0..segment_len) - .step_by(self.cells_per_instance as usize) + .step_by(CELLS_PER_KECCAK as usize) .enumerate() { // Add the input cells of each keccak instance to the private inputs @@ -272,7 +213,6 @@ impl KeccakBuiltinRunner { mod tests { use super::*; use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; - use crate::stdlib::collections::HashMap; use crate::types::program::Program; use crate::utils::test_utils::*; use crate::vm::runners::cairo_runner::CairoRunner; @@ -290,8 +230,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_instances() { - let builtin: BuiltinRunner = - KeccakBuiltinRunner::new(&KeccakInstanceDef::new(Some(10), vec![200; 8]), true).into(); + let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![1]); @@ -302,8 +241,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack() { - let mut builtin = - KeccakBuiltinRunner::new(&KeccakInstanceDef::new(Some(10), vec![200; 8]), true); + let mut builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -327,8 +265,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_stop_pointer() { - let mut builtin = - KeccakBuiltinRunner::new(&KeccakInstanceDef::new(Some(10), vec![200; 8]), true); + let mut builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -355,8 +292,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_when_not_included() { - let mut builtin = - KeccakBuiltinRunner::new(&KeccakInstanceDef::new(Some(10), vec![200; 8]), false); + let mut builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), false).into(); let mut vm = vm!(); @@ -380,8 +316,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_non_relocatable() { - let mut builtin = - KeccakBuiltinRunner::new(&KeccakInstanceDef::new(Some(10), vec![200; 8]), true); + let mut builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -405,8 +340,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_and_allocated_size_test() { - let builtin: BuiltinRunner = - KeccakBuiltinRunner::new(&KeccakInstanceDef::new(Some(10), vec![200; 8]), true).into(); + let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -436,8 +370,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units() { - let builtin: BuiltinRunner = - KeccakBuiltinRunner::new(&KeccakInstanceDef::new(Some(10), vec![200; 8]), true).into(); + let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); vm.current_step = 160; @@ -445,59 +378,10 @@ mod tests { assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(256)); } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_segment_addresses() { - let builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); - - assert_eq!(builtin.get_memory_segment_addresses(), (0, None)); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_missing_segment_used_sizes() { - let builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); - let vm = vm!(); - - assert_eq!( - builtin.get_memory_accesses(&vm), - Err(MemoryError::MissingSegmentUsedSizes), - ); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_empty() { - let builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![0]); - assert_eq!(builtin.get_memory_accesses(&vm), Ok(vec![])); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses() { - let builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![4]); - assert_eq!( - builtin.get_memory_accesses(&vm), - Ok(vec![ - (builtin.base() as isize, 0).into(), - (builtin.base() as isize, 1).into(), - (builtin.base() as isize, 2).into(), - (builtin.base() as isize, 3).into(), - ]), - ); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_missing_segment_used_sizes() { - let builtin: BuiltinRunner = - KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true).into(); + let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(2048), true).into(); let vm = vm!(); assert_eq!( @@ -509,8 +393,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_empty() { - let builtin: BuiltinRunner = - KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true).into(); + let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(2048), true).into(); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![0]); @@ -520,8 +403,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells() { - let builtin: BuiltinRunner = - KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true).into(); + let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(2048), true).into(); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![4]); @@ -531,7 +413,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn initial_stackincluded_test() { - let keccak_builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); + let keccak_builtin = KeccakBuiltinRunner::new(Some(2048), true); assert_eq!( keccak_builtin.initial_stack(), vec![mayberelocatable!(0, 0)] @@ -541,7 +423,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn initial_stack_notincluded_test() { - let keccak_builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), false); + let keccak_builtin = KeccakBuiltinRunner::new(Some(2048), false); assert_eq!(keccak_builtin.initial_stack(), Vec::new()) } @@ -570,7 +452,7 @@ mod tests { ((0, 34), 0), ((0, 35), 0) ]; - let builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); + let builtin = KeccakBuiltinRunner::new(Some(2048), true); let result = builtin.deduce_memory_cell(Relocatable::from((0, 25)), &memory); assert_eq!( @@ -591,7 +473,7 @@ mod tests { ((0, 7), 120), ((0, 8), 52) ]; - let builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); + let builtin = KeccakBuiltinRunner::new(Some(2048), true); let result = builtin.deduce_memory_cell(Relocatable::from((0, 1)), &memory); assert_eq!(result, Ok(None)); } @@ -600,7 +482,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn deduce_memory_cell_offset_lt_input_cell_length_none() { let memory = memory![((0, 4), 32)]; - let builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); + let builtin = KeccakBuiltinRunner::new(Some(2048), true); let result = builtin.deduce_memory_cell(Relocatable::from((0, 2)), &memory); assert_eq!(result, Ok(None)); } @@ -610,12 +492,9 @@ mod tests { fn deduce_memory_cell_expected_integer() { let memory = memory![((0, 0), (1, 2))]; - let mut builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); + let builtin = KeccakBuiltinRunner::new(Some(2048), true); - builtin.n_input_cells = 1; - builtin.cells_per_instance = 100; - - let result = builtin.deduce_memory_cell(Relocatable::from((0, 1)), &memory); + let result = builtin.deduce_memory_cell(Relocatable::from((0, 9)), &memory); assert_eq!( result, @@ -631,37 +510,19 @@ mod tests { fn deduce_memory_cell_missing_input_cells() { let memory = memory![((0, 1), (1, 2))]; - let mut builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); - - builtin.n_input_cells = 1; - builtin.cells_per_instance = 100; + let builtin = KeccakBuiltinRunner::new(Some(2048), true); let result = builtin.deduce_memory_cell(Relocatable::from((0, 1)), &memory); assert_eq!(result, Ok(None)); } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn deduce_memory_cell_input_cell() { - let memory = memory![((0, 0), (1, 2))]; - - let mut builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); - - builtin.n_input_cells = 1; - builtin.cells_per_instance = 100; - - let result = builtin.deduce_memory_cell(Relocatable::from((0, 0)), &memory); - - assert_eq!(result, Ok(None)); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn deduce_memory_cell_get_memory_err() { let memory = memory![((0, 35), 0)]; - let builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); + let builtin = KeccakBuiltinRunner::new(Some(2048), true); let result = builtin.deduce_memory_cell(Relocatable::from((0, 15)), &memory); @@ -671,8 +532,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn deduce_memory_cell_memory_int_larger_than_bits() { - let memory = memory![ - ((0, 16), 43), + let mut memory = memory![ ((0, 17), 199), ((0, 18), 0), ((0, 19), 0), @@ -694,8 +554,9 @@ mod tests { ((0, 35), 0) ]; - let keccak_instance = KeccakInstanceDef::new(Some(2048), vec![1; 8]); - let builtin = KeccakBuiltinRunner::new(&keccak_instance, true); + memory.insert((0, 16).into(), Felt252::MAX).unwrap(); + + let builtin = KeccakBuiltinRunner::new(Some(2048), true); let result = builtin.deduce_memory_cell(Relocatable::from((0, 25)), &memory); @@ -703,8 +564,8 @@ mod tests { result, Err(RunnerError::IntegerBiggerThanPowerOfTwo(Box::new(( (0, 16).into(), - 1, - 43.into() + BITS, + Felt252::MAX )))) ); } @@ -712,7 +573,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_diluted_check_units_result() { - let builtin = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); + let builtin = KeccakBuiltinRunner::new(Some(2048), true); let result: usize = builtin.get_used_diluted_check_units(16); @@ -730,10 +591,9 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_air_private_input() { - let builtin: BuiltinRunner = - KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true).into(); + let builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(2048), true).into(); - let memory = memory![ + let segments = segments![ ((0, 0), 0), ((0, 1), 1), ((0, 2), 2), @@ -744,7 +604,7 @@ mod tests { ((0, 7), 7) ]; assert_eq!( - builtin.air_private_input(&memory), + builtin.air_private_input(&segments), (vec![PrivateInput::KeccakState(PrivateInputKeccakState { index: 0, input_s0: 0.into(), diff --git a/vm/src/vm/runners/builtin_runner/mod.rs b/vm/src/vm/runners/builtin_runner/mod.rs index 78eed172a2..e034ed46e4 100644 --- a/vm/src/vm/runners/builtin_runner/mod.rs +++ b/vm/src/vm/runners/builtin_runner/mod.rs @@ -1,6 +1,26 @@ use crate::air_private_input::PrivateInput; use crate::math_utils::safe_div_usize; +use crate::serde::deserialize_program::BuiltinName; use crate::stdlib::prelude::*; +use crate::types::instance_definitions::bitwise_instance_def::{ + CELLS_PER_BITWISE, INPUT_CELLS_PER_BITWISE, +}; +use crate::types::instance_definitions::builtins_instance_def::BUILTIN_INSTANCES_PER_COMPONENT; +use crate::types::instance_definitions::ec_op_instance_def::{ + CELLS_PER_EC_OP, INPUT_CELLS_PER_EC_OP, +}; +use crate::types::instance_definitions::ecdsa_instance_def::CELLS_PER_SIGNATURE; +use crate::types::instance_definitions::keccak_instance_def::{ + CELLS_PER_KECCAK, INPUT_CELLS_PER_KECCAK, KECCAK_INSTANCES_PER_COMPONENT, +}; +use crate::types::instance_definitions::mod_instance_def::CELLS_PER_MOD; +use crate::types::instance_definitions::pedersen_instance_def::{ + CELLS_PER_HASH, INPUT_CELLS_PER_HASH, +}; +use crate::types::instance_definitions::poseidon_instance_def::{ + CELLS_PER_POSEIDON, INPUT_CELLS_PER_POSEIDON, +}; +use crate::types::instance_definitions::range_check_instance_def::CELLS_PER_RANGE_CHECK; use crate::types::relocatable::{MaybeRelocatable, Relocatable}; use crate::vm::errors::memory_errors::{self, InsufficientAllocatedCellsError, MemoryError}; use crate::vm::errors::runner_errors::RunnerError; @@ -13,21 +33,25 @@ mod bitwise; mod ec_op; mod hash; mod keccak; +mod modulo; mod output; mod poseidon; mod range_check; mod segment_arena; mod signature; -pub use self::keccak::KeccakBuiltinRunner; -pub use self::poseidon::PoseidonBuiltinRunner; -pub use self::segment_arena::SegmentArenaBuiltinRunner; +pub(crate) use self::range_check::{RC_N_PARTS_96, RC_N_PARTS_STANDARD}; +use self::segment_arena::ARENA_BUILTIN_SIZE; pub use bitwise::BitwiseBuiltinRunner; pub use ec_op::EcOpBuiltinRunner; pub use hash::HashBuiltinRunner; +pub use keccak::KeccakBuiltinRunner; +pub use modulo::ModBuiltinRunner; use num_integer::div_floor; pub use output::OutputBuiltinRunner; +pub use poseidon::PoseidonBuiltinRunner; pub use range_check::RangeCheckBuiltinRunner; +pub use segment_arena::SegmentArenaBuiltinRunner; pub use signature::SignatureBuiltinRunner; use super::cairo_pie::BuiltinAdditionalData; @@ -35,12 +59,15 @@ use super::cairo_pie::BuiltinAdditionalData; pub const OUTPUT_BUILTIN_NAME: &str = "output_builtin"; pub const HASH_BUILTIN_NAME: &str = "pedersen_builtin"; pub const RANGE_CHECK_BUILTIN_NAME: &str = "range_check_builtin"; +pub const RANGE_CHECK_96_BUILTIN_NAME: &str = "range_check_96_builtin"; pub const SIGNATURE_BUILTIN_NAME: &str = "ecdsa_builtin"; pub const BITWISE_BUILTIN_NAME: &str = "bitwise_builtin"; pub const EC_OP_BUILTIN_NAME: &str = "ec_op_builtin"; pub const KECCAK_BUILTIN_NAME: &str = "keccak_builtin"; pub const POSEIDON_BUILTIN_NAME: &str = "poseidon_builtin"; pub const SEGMENT_ARENA_BUILTIN_NAME: &str = "segment_arena_builtin"; +pub const ADD_MOD_BUILTIN_NAME: &str = "add_mod_builtin"; +pub const MUL_MOD_BUILTIN_NAME: &str = "mul_mod_builtin"; /* NB: this enum is no accident: we may need (and cairo-vm-py *does* need) * structs containing this to be `Send`. The only two ways to achieve that @@ -56,11 +83,13 @@ pub enum BuiltinRunner { EcOp(EcOpBuiltinRunner), Hash(HashBuiltinRunner), Output(OutputBuiltinRunner), - RangeCheck(RangeCheckBuiltinRunner), + RangeCheck(RangeCheckBuiltinRunner), + RangeCheck96(RangeCheckBuiltinRunner), Keccak(KeccakBuiltinRunner), Signature(SignatureBuiltinRunner), Poseidon(PoseidonBuiltinRunner), SegmentArena(SegmentArenaBuiltinRunner), + Mod(ModBuiltinRunner), } impl BuiltinRunner { @@ -74,12 +103,16 @@ impl BuiltinRunner { BuiltinRunner::RangeCheck(ref mut range_check) => { range_check.initialize_segments(segments) } + BuiltinRunner::RangeCheck96(ref mut range_check) => { + range_check.initialize_segments(segments) + } BuiltinRunner::Keccak(ref mut keccak) => keccak.initialize_segments(segments), BuiltinRunner::Signature(ref mut signature) => signature.initialize_segments(segments), BuiltinRunner::Poseidon(ref mut poseidon) => poseidon.initialize_segments(segments), BuiltinRunner::SegmentArena(ref mut segment_arena) => { segment_arena.initialize_segments(segments) } + BuiltinRunner::Mod(ref mut modulo) => modulo.initialize_segments(segments), } } @@ -90,10 +123,12 @@ impl BuiltinRunner { BuiltinRunner::Hash(ref hash) => hash.initial_stack(), BuiltinRunner::Output(ref output) => output.initial_stack(), BuiltinRunner::RangeCheck(ref range_check) => range_check.initial_stack(), + BuiltinRunner::RangeCheck96(ref range_check) => range_check.initial_stack(), BuiltinRunner::Keccak(ref keccak) => keccak.initial_stack(), BuiltinRunner::Signature(ref signature) => signature.initial_stack(), BuiltinRunner::Poseidon(ref poseidon) => poseidon.initial_stack(), BuiltinRunner::SegmentArena(ref segment_arena) => segment_arena.initial_stack(), + BuiltinRunner::Mod(ref modulo) => modulo.initial_stack(), } } @@ -101,26 +136,40 @@ impl BuiltinRunner { pub fn final_stack( &mut self, segments: &MemorySegmentManager, - stack_pointer: Relocatable, + pointer: Relocatable, ) -> Result { - match self { - BuiltinRunner::Bitwise(ref mut bitwise) => bitwise.final_stack(segments, stack_pointer), - BuiltinRunner::EcOp(ref mut ec) => ec.final_stack(segments, stack_pointer), - BuiltinRunner::Hash(ref mut hash) => hash.final_stack(segments, stack_pointer), - BuiltinRunner::Output(ref mut output) => output.final_stack(segments, stack_pointer), - BuiltinRunner::RangeCheck(ref mut range_check) => { - range_check.final_stack(segments, stack_pointer) - } - BuiltinRunner::Keccak(ref mut keccak) => keccak.final_stack(segments, stack_pointer), - BuiltinRunner::Signature(ref mut signature) => { - signature.final_stack(segments, stack_pointer) - } - BuiltinRunner::Poseidon(ref mut poseidon) => { - poseidon.final_stack(segments, stack_pointer) + if let BuiltinRunner::Output(output) = self { + return output.final_stack(segments, pointer); + } + if self.included() { + let stop_pointer_addr = + (pointer - 1).map_err(|_| RunnerError::NoStopPointer(Box::new(self.name())))?; + let stop_pointer = segments + .memory + .get_relocatable(stop_pointer_addr) + .map_err(|_| RunnerError::NoStopPointer(Box::new(self.name())))?; + if self.base() as isize != stop_pointer.segment_index { + return Err(RunnerError::InvalidStopPointerIndex(Box::new(( + self.name(), + stop_pointer, + self.base(), + )))); } - BuiltinRunner::SegmentArena(ref mut segment_arena) => { - segment_arena.final_stack(segments, stack_pointer) + let stop_ptr = stop_pointer.offset; + let num_instances = self.get_used_instances(segments)?; + let used = num_instances * self.cells_per_instance() as usize; + if stop_ptr != used { + return Err(RunnerError::InvalidStopPointer(Box::new(( + self.name(), + Relocatable::from((self.base() as isize, used)), + Relocatable::from((self.base() as isize, stop_ptr)), + )))); } + self.set_stop_ptr(stop_ptr); + Ok(stop_pointer_addr) + } else { + self.set_stop_ptr(0); + Ok(pointer) } } @@ -160,6 +209,23 @@ impl BuiltinRunner { } } + /// Returns if the builtin is included in the program builtins + fn included(&self) -> bool { + match *self { + BuiltinRunner::Bitwise(ref bitwise) => bitwise.included, + BuiltinRunner::EcOp(ref ec) => ec.included, + BuiltinRunner::Hash(ref hash) => hash.included, + BuiltinRunner::Output(ref output) => output.included, + BuiltinRunner::RangeCheck(ref range_check) => range_check.included, + BuiltinRunner::RangeCheck96(ref range_check) => range_check.included, + BuiltinRunner::Keccak(ref keccak) => keccak.included, + BuiltinRunner::Signature(ref signature) => signature.included, + BuiltinRunner::Poseidon(ref poseidon) => poseidon.included, + BuiltinRunner::SegmentArena(ref segment_arena) => segment_arena.included, + BuiltinRunner::Mod(ref modulo) => modulo.included, + } + } + ///Returns the builtin's base pub fn base(&self) -> usize { match *self { @@ -168,11 +234,13 @@ impl BuiltinRunner { BuiltinRunner::Hash(ref hash) => hash.base(), BuiltinRunner::Output(ref output) => output.base(), BuiltinRunner::RangeCheck(ref range_check) => range_check.base(), + BuiltinRunner::RangeCheck96(ref range_check) => range_check.base(), BuiltinRunner::Keccak(ref keccak) => keccak.base(), BuiltinRunner::Signature(ref signature) => signature.base(), BuiltinRunner::Poseidon(ref poseidon) => poseidon.base(), //Warning, returns only the segment index, base offset will be 3 BuiltinRunner::SegmentArena(ref segment_arena) => segment_arena.base(), + BuiltinRunner::Mod(ref modulo) => modulo.base(), } } @@ -183,25 +251,21 @@ impl BuiltinRunner { BuiltinRunner::Hash(hash) => hash.ratio(), BuiltinRunner::Output(_) | BuiltinRunner::SegmentArena(_) => None, BuiltinRunner::RangeCheck(range_check) => range_check.ratio(), + BuiltinRunner::RangeCheck96(range_check) => range_check.ratio(), BuiltinRunner::Keccak(keccak) => keccak.ratio(), BuiltinRunner::Signature(ref signature) => signature.ratio(), BuiltinRunner::Poseidon(poseidon) => poseidon.ratio(), + BuiltinRunner::Mod(ref modulo) => modulo.ratio(), } } pub fn add_validation_rule(&self, memory: &mut Memory) { match *self { - BuiltinRunner::Bitwise(ref bitwise) => bitwise.add_validation_rule(memory), - BuiltinRunner::EcOp(ref ec) => ec.add_validation_rule(memory), - BuiltinRunner::Hash(ref hash) => hash.add_validation_rule(memory), - BuiltinRunner::Output(ref output) => output.add_validation_rule(memory), BuiltinRunner::RangeCheck(ref range_check) => range_check.add_validation_rule(memory), - BuiltinRunner::Keccak(ref keccak) => keccak.add_validation_rule(memory), + BuiltinRunner::RangeCheck96(ref range_check) => range_check.add_validation_rule(memory), BuiltinRunner::Signature(ref signature) => signature.add_validation_rule(memory), BuiltinRunner::Poseidon(ref poseidon) => poseidon.add_validation_rule(memory), - BuiltinRunner::SegmentArena(ref segment_arena) => { - segment_arena.add_validation_rule(memory) - } + _ => {} } } @@ -214,55 +278,14 @@ impl BuiltinRunner { BuiltinRunner::Bitwise(ref bitwise) => bitwise.deduce_memory_cell(address, memory), BuiltinRunner::EcOp(ref ec) => ec.deduce_memory_cell(address, memory), BuiltinRunner::Hash(ref hash) => hash.deduce_memory_cell(address, memory), - BuiltinRunner::Output(ref output) => output.deduce_memory_cell(address, memory), - BuiltinRunner::RangeCheck(ref range_check) => { - range_check.deduce_memory_cell(address, memory) - } BuiltinRunner::Keccak(ref keccak) => keccak.deduce_memory_cell(address, memory), - BuiltinRunner::Signature(ref signature) => { - signature.deduce_memory_cell(address, memory) - } BuiltinRunner::Poseidon(ref poseidon) => poseidon.deduce_memory_cell(address, memory), - BuiltinRunner::SegmentArena(ref segment_arena) => { - segment_arena.deduce_memory_cell(address, memory) - } + _ => Ok(None), } } - pub fn get_memory_accesses( - &self, - vm: &VirtualMachine, - ) -> Result, MemoryError> { - if let BuiltinRunner::SegmentArena(_) = self { - return Ok(vec![]); - } - let base = self.base(); - let segment_size = vm - .segments - .get_segment_size(base) - .ok_or(MemoryError::MissingSegmentUsedSizes)?; - - Ok((0..segment_size) - .map(|i| (base as isize, i).into()) - .collect()) - } - pub fn get_memory_segment_addresses(&self) -> (usize, Option) { - match self { - BuiltinRunner::Bitwise(ref bitwise) => bitwise.get_memory_segment_addresses(), - BuiltinRunner::EcOp(ref ec) => ec.get_memory_segment_addresses(), - BuiltinRunner::Hash(ref hash) => hash.get_memory_segment_addresses(), - BuiltinRunner::Output(ref output) => output.get_memory_segment_addresses(), - BuiltinRunner::RangeCheck(ref range_check) => { - range_check.get_memory_segment_addresses() - } - BuiltinRunner::Keccak(ref keccak) => keccak.get_memory_segment_addresses(), - BuiltinRunner::Signature(ref signature) => signature.get_memory_segment_addresses(), - BuiltinRunner::Poseidon(ref poseidon) => poseidon.get_memory_segment_addresses(), - BuiltinRunner::SegmentArena(ref segment_arena) => { - segment_arena.get_memory_segment_addresses() - } - } + (self.base(), self.stop_ptr()) } pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result { @@ -272,12 +295,14 @@ impl BuiltinRunner { BuiltinRunner::Hash(ref hash) => hash.get_used_cells(segments), BuiltinRunner::Output(ref output) => output.get_used_cells(segments), BuiltinRunner::RangeCheck(ref range_check) => range_check.get_used_cells(segments), + BuiltinRunner::RangeCheck96(ref range_check) => range_check.get_used_cells(segments), BuiltinRunner::Keccak(ref keccak) => keccak.get_used_cells(segments), BuiltinRunner::Signature(ref signature) => signature.get_used_cells(segments), BuiltinRunner::Poseidon(ref poseidon) => poseidon.get_used_cells(segments), BuiltinRunner::SegmentArena(ref segment_arena) => { segment_arena.get_used_cells(segments) } + BuiltinRunner::Mod(ref modulo) => modulo.get_used_cells(segments), } } @@ -291,18 +316,25 @@ impl BuiltinRunner { BuiltinRunner::Hash(ref hash) => hash.get_used_instances(segments), BuiltinRunner::Output(ref output) => output.get_used_instances(segments), BuiltinRunner::RangeCheck(ref range_check) => range_check.get_used_instances(segments), + BuiltinRunner::RangeCheck96(ref range_check) => { + range_check.get_used_instances(segments) + } BuiltinRunner::Keccak(ref keccak) => keccak.get_used_instances(segments), BuiltinRunner::Signature(ref signature) => signature.get_used_instances(segments), BuiltinRunner::Poseidon(ref poseidon) => poseidon.get_used_instances(segments), BuiltinRunner::SegmentArena(ref segment_arena) => { segment_arena.get_used_instances(segments) } + BuiltinRunner::Mod(modulo) => modulo.get_used_instances(segments), } } pub fn get_range_check_usage(&self, memory: &Memory) -> Option<(usize, usize)> { match self { BuiltinRunner::RangeCheck(ref range_check) => range_check.get_range_check_usage(memory), + BuiltinRunner::RangeCheck96(ref range_check) => { + range_check.get_range_check_usage(memory) + } _ => None, } } @@ -315,7 +347,11 @@ impl BuiltinRunner { match self { BuiltinRunner::RangeCheck(range_check) => { let (used_cells, _) = self.get_used_cells_and_allocated_size(vm)?; - Ok(used_cells * range_check.n_parts as usize) + Ok(used_cells * range_check.n_parts() as usize) + } + BuiltinRunner::RangeCheck96(range_check) => { + let (used_cells, _) = self.get_used_cells_and_allocated_size(vm)?; + Ok(used_cells * range_check.n_parts() as usize) } _ => Ok(0), } @@ -335,42 +371,38 @@ impl BuiltinRunner { fn cells_per_instance(&self) -> u32 { match self { - BuiltinRunner::Bitwise(builtin) => builtin.cells_per_instance, - BuiltinRunner::EcOp(builtin) => builtin.cells_per_instance, - BuiltinRunner::Hash(builtin) => builtin.cells_per_instance, - BuiltinRunner::RangeCheck(builtin) => builtin.cells_per_instance, + BuiltinRunner::Bitwise(_) => CELLS_PER_BITWISE, + BuiltinRunner::EcOp(_) => CELLS_PER_EC_OP, + BuiltinRunner::Hash(_) => CELLS_PER_HASH, + BuiltinRunner::RangeCheck(_) | BuiltinRunner::RangeCheck96(_) => CELLS_PER_RANGE_CHECK, BuiltinRunner::Output(_) => 0, - BuiltinRunner::Keccak(builtin) => builtin.cells_per_instance, - BuiltinRunner::Signature(builtin) => builtin.cells_per_instance, - BuiltinRunner::Poseidon(builtin) => builtin.cells_per_instance, - BuiltinRunner::SegmentArena(builtin) => builtin.cells_per_instance, + BuiltinRunner::Keccak(_) => CELLS_PER_KECCAK, + BuiltinRunner::Signature(_) => CELLS_PER_SIGNATURE, + BuiltinRunner::Poseidon(_) => CELLS_PER_POSEIDON, + BuiltinRunner::SegmentArena(_) => ARENA_BUILTIN_SIZE, + BuiltinRunner::Mod(_) => CELLS_PER_MOD, } } fn n_input_cells(&self) -> u32 { match self { - BuiltinRunner::Bitwise(builtin) => builtin.n_input_cells, - BuiltinRunner::EcOp(builtin) => builtin.n_input_cells, - BuiltinRunner::Hash(builtin) => builtin.n_input_cells, - BuiltinRunner::RangeCheck(builtin) => builtin.n_input_cells, + BuiltinRunner::Bitwise(_) => INPUT_CELLS_PER_BITWISE, + BuiltinRunner::EcOp(_) => INPUT_CELLS_PER_EC_OP, + BuiltinRunner::Hash(_) => INPUT_CELLS_PER_HASH, + BuiltinRunner::RangeCheck(_) | BuiltinRunner::RangeCheck96(_) => CELLS_PER_RANGE_CHECK, BuiltinRunner::Output(_) => 0, - BuiltinRunner::Keccak(builtin) => builtin.n_input_cells, - BuiltinRunner::Signature(builtin) => builtin.n_input_cells, - BuiltinRunner::Poseidon(builtin) => builtin.n_input_cells, - BuiltinRunner::SegmentArena(builtin) => builtin.n_input_cells_per_instance, + BuiltinRunner::Keccak(_) => INPUT_CELLS_PER_KECCAK, + BuiltinRunner::Signature(_) => CELLS_PER_SIGNATURE, + BuiltinRunner::Poseidon(_) => INPUT_CELLS_PER_POSEIDON, + BuiltinRunner::SegmentArena(_) => ARENA_BUILTIN_SIZE, + BuiltinRunner::Mod(_) => CELLS_PER_MOD, } } fn instances_per_component(&self) -> u32 { match self { - BuiltinRunner::Bitwise(builtin) => builtin.instances_per_component, - BuiltinRunner::EcOp(builtin) => builtin.instances_per_component, - BuiltinRunner::Hash(builtin) => builtin.instances_per_component, - BuiltinRunner::RangeCheck(builtin) => builtin.instances_per_component, - BuiltinRunner::Output(_) | BuiltinRunner::SegmentArena(_) => 1, - BuiltinRunner::Keccak(builtin) => builtin.instances_per_component, - BuiltinRunner::Signature(builtin) => builtin.instances_per_component, - BuiltinRunner::Poseidon(builtin) => builtin.instances_per_component, + BuiltinRunner::Keccak(_) => KECCAK_INSTANCES_PER_COMPONENT, + _ => BUILTIN_INSTANCES_PER_COMPONENT, } } @@ -380,11 +412,29 @@ impl BuiltinRunner { BuiltinRunner::EcOp(_) => EC_OP_BUILTIN_NAME, BuiltinRunner::Hash(_) => HASH_BUILTIN_NAME, BuiltinRunner::RangeCheck(_) => RANGE_CHECK_BUILTIN_NAME, + BuiltinRunner::RangeCheck96(_) => RANGE_CHECK_96_BUILTIN_NAME, BuiltinRunner::Output(_) => OUTPUT_BUILTIN_NAME, BuiltinRunner::Keccak(_) => KECCAK_BUILTIN_NAME, BuiltinRunner::Signature(_) => SIGNATURE_BUILTIN_NAME, BuiltinRunner::Poseidon(_) => POSEIDON_BUILTIN_NAME, BuiltinRunner::SegmentArena(_) => SEGMENT_ARENA_BUILTIN_NAME, + BuiltinRunner::Mod(b) => b.name(), + } + } + + pub fn identifier(&self) -> BuiltinName { + match self { + BuiltinRunner::Bitwise(_) => BuiltinName::bitwise, + BuiltinRunner::EcOp(_) => BuiltinName::ec_op, + BuiltinRunner::Hash(_) => BuiltinName::pedersen, + BuiltinRunner::RangeCheck(_) => BuiltinName::range_check, + BuiltinRunner::RangeCheck96(_) => BuiltinName::range_check96, + BuiltinRunner::Output(_) => BuiltinName::output, + BuiltinRunner::Keccak(_) => BuiltinName::keccak, + BuiltinRunner::Signature(_) => BuiltinName::ecdsa, + BuiltinRunner::Poseidon(_) => BuiltinName::poseidon, + BuiltinRunner::SegmentArena(_) => BuiltinName::segment_arena, + BuiltinRunner::Mod(b) => b.identifier(), } } @@ -392,6 +442,9 @@ impl BuiltinRunner { if let BuiltinRunner::Output(_) | BuiltinRunner::SegmentArena(_) = self { return Ok(()); } + if let BuiltinRunner::Mod(modulo) = self { + modulo.run_additional_security_checks(vm)?; + } let cells_per_instance = self.cells_per_instance() as usize; let n_input_cells = self.n_input_cells() as usize; let builtin_segment_index = self.base(); @@ -485,20 +538,21 @@ impl BuiltinRunner { } // Returns information about the builtin that should be added to the AIR private input. - pub fn air_private_input(&self, memory: &Memory) -> Vec { + pub fn air_private_input(&self, segments: &MemorySegmentManager) -> Vec { match self { - BuiltinRunner::RangeCheck(builtin) => builtin.air_private_input(memory), - BuiltinRunner::Bitwise(builtin) => builtin.air_private_input(memory), - BuiltinRunner::Hash(builtin) => builtin.air_private_input(memory), - BuiltinRunner::EcOp(builtin) => builtin.air_private_input(memory), - BuiltinRunner::Poseidon(builtin) => builtin.air_private_input(memory), - BuiltinRunner::Signature(builtin) => builtin.air_private_input(memory), - BuiltinRunner::Keccak(builtin) => builtin.air_private_input(memory), + BuiltinRunner::RangeCheck(builtin) => builtin.air_private_input(&segments.memory), + BuiltinRunner::RangeCheck96(builtin) => builtin.air_private_input(&segments.memory), + BuiltinRunner::Bitwise(builtin) => builtin.air_private_input(&segments.memory), + BuiltinRunner::Hash(builtin) => builtin.air_private_input(&segments.memory), + BuiltinRunner::EcOp(builtin) => builtin.air_private_input(&segments.memory), + BuiltinRunner::Poseidon(builtin) => builtin.air_private_input(&segments.memory), + BuiltinRunner::Signature(builtin) => builtin.air_private_input(&segments.memory), + BuiltinRunner::Keccak(builtin) => builtin.air_private_input(&segments.memory), + BuiltinRunner::Mod(builtin) => builtin.air_private_input(segments), _ => vec![], } } - #[cfg(test)] pub(crate) fn set_stop_ptr(&mut self, stop_ptr: usize) { match self { BuiltinRunner::Bitwise(ref mut bitwise) => bitwise.stop_ptr = Some(stop_ptr), @@ -506,12 +560,32 @@ impl BuiltinRunner { BuiltinRunner::Hash(ref mut hash) => hash.stop_ptr = Some(stop_ptr), BuiltinRunner::Output(ref mut output) => output.stop_ptr = Some(stop_ptr), BuiltinRunner::RangeCheck(ref mut range_check) => range_check.stop_ptr = Some(stop_ptr), + BuiltinRunner::RangeCheck96(ref mut range_check) => { + range_check.stop_ptr = Some(stop_ptr) + } BuiltinRunner::Keccak(ref mut keccak) => keccak.stop_ptr = Some(stop_ptr), BuiltinRunner::Signature(ref mut signature) => signature.stop_ptr = Some(stop_ptr), BuiltinRunner::Poseidon(ref mut poseidon) => poseidon.stop_ptr = Some(stop_ptr), BuiltinRunner::SegmentArena(ref mut segment_arena) => { segment_arena.stop_ptr = Some(stop_ptr) } + BuiltinRunner::Mod(modulo) => modulo.stop_ptr = Some(stop_ptr), + } + } + + pub(crate) fn stop_ptr(&self) -> Option { + match self { + BuiltinRunner::Bitwise(ref bitwise) => bitwise.stop_ptr, + BuiltinRunner::EcOp(ref ec) => ec.stop_ptr, + BuiltinRunner::Hash(ref hash) => hash.stop_ptr, + BuiltinRunner::Output(ref output) => output.stop_ptr, + BuiltinRunner::RangeCheck(ref range_check) => range_check.stop_ptr, + BuiltinRunner::RangeCheck96(ref range_check) => range_check.stop_ptr, + BuiltinRunner::Keccak(ref keccak) => keccak.stop_ptr, + BuiltinRunner::Signature(ref signature) => signature.stop_ptr, + BuiltinRunner::Poseidon(ref poseidon) => poseidon.stop_ptr, + BuiltinRunner::SegmentArena(ref segment_arena) => segment_arena.stop_ptr, + BuiltinRunner::Mod(ref modulo) => modulo.stop_ptr, } } } @@ -546,12 +620,18 @@ impl From for BuiltinRunner { } } -impl From for BuiltinRunner { - fn from(runner: RangeCheckBuiltinRunner) -> Self { +impl From> for BuiltinRunner { + fn from(runner: RangeCheckBuiltinRunner) -> Self { BuiltinRunner::RangeCheck(runner) } } +impl From> for BuiltinRunner { + fn from(runner: RangeCheckBuiltinRunner) -> Self { + BuiltinRunner::RangeCheck96(runner) + } +} + impl From for BuiltinRunner { fn from(runner: SignatureBuiltinRunner) -> Self { BuiltinRunner::Signature(runner) @@ -570,78 +650,33 @@ impl From for BuiltinRunner { } } +impl From for BuiltinRunner { + fn from(runner: ModBuiltinRunner) -> Self { + BuiltinRunner::Mod(runner) + } +} + #[cfg(test)] mod tests { use super::*; use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; use crate::relocatable; use crate::serde::deserialize_program::BuiltinName; - use crate::types::instance_definitions::ecdsa_instance_def::EcdsaInstanceDef; - use crate::types::instance_definitions::keccak_instance_def::KeccakInstanceDef; use crate::types::program::Program; use crate::vm::errors::memory_errors::InsufficientAllocatedCellsError; use crate::vm::runners::cairo_runner::CairoRunner; - use crate::{ - types::instance_definitions::{ - bitwise_instance_def::BitwiseInstanceDef, ec_op_instance_def::EcOpInstanceDef, - }, - utils::test_utils::*, - vm::vm_core::VirtualMachine, - }; + use crate::{utils::test_utils::*, vm::vm_core::VirtualMachine}; use assert_matches::assert_matches; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_missing_segment_used_sizes() { - let builtin: BuiltinRunner = - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true).into(); - let vm = vm!(); - - assert_eq!( - builtin.get_memory_accesses(&vm), - Err(MemoryError::MissingSegmentUsedSizes), - ); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_empty() { - let builtin: BuiltinRunner = - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true).into(); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![0]); - assert_eq!(builtin.get_memory_accesses(&vm), Ok(vec![])); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses() { - let builtin: BuiltinRunner = - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true).into(); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![4]); - assert_eq!( - builtin.get_memory_accesses(&vm), - Ok(vec![ - (builtin.base() as isize, 0).into(), - (builtin.base() as isize, 1).into(), - (builtin.base() as isize, 2).into(), - (builtin.base() as isize, 3).into(), - ]), - ); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_n_input_cells_bitwise() { - let bitwise = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::new(Some(10)), true); + let bitwise = BitwiseBuiltinRunner::new(Some(10), true); let builtin: BuiltinRunner = bitwise.clone().into(); - assert_eq!(bitwise.n_input_cells, builtin.n_input_cells()) + assert_eq!(INPUT_CELLS_PER_BITWISE, builtin.n_input_cells()) } #[test] @@ -649,31 +684,23 @@ mod tests { fn get_n_input_cells_hash() { let hash = HashBuiltinRunner::new(Some(10), true); let builtin: BuiltinRunner = hash.clone().into(); - assert_eq!(hash.n_input_cells, builtin.n_input_cells()) - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_n_input_cells_range_check() { - let range_check = RangeCheckBuiltinRunner::new(Some(10), 10, true); - let builtin: BuiltinRunner = range_check.clone().into(); - assert_eq!(range_check.n_input_cells, builtin.n_input_cells()) + assert_eq!(INPUT_CELLS_PER_HASH, builtin.n_input_cells()) } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_n_input_cells_ec_op() { - let ec_op = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let ec_op = EcOpBuiltinRunner::new(Some(256), true); let builtin: BuiltinRunner = ec_op.clone().into(); - assert_eq!(ec_op.n_input_cells, builtin.n_input_cells()) + assert_eq!(INPUT_CELLS_PER_EC_OP, builtin.n_input_cells()) } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_n_input_cells_ecdsa() { - let signature = SignatureBuiltinRunner::new(&EcdsaInstanceDef::new(Some(10)), true); + let signature = SignatureBuiltinRunner::new(Some(10), true); let builtin: BuiltinRunner = signature.clone().into(); - assert_eq!(signature.n_input_cells, builtin.n_input_cells()) + assert_eq!(CELLS_PER_SIGNATURE, builtin.n_input_cells()) } #[test] @@ -687,9 +714,9 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_cells_per_instance_bitwise() { - let bitwise = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::new(Some(10)), true); + let bitwise = BitwiseBuiltinRunner::new(Some(10), true); let builtin: BuiltinRunner = bitwise.clone().into(); - assert_eq!(bitwise.cells_per_instance, builtin.cells_per_instance()) + assert_eq!(CELLS_PER_BITWISE, builtin.cells_per_instance()) } #[test] @@ -697,31 +724,23 @@ mod tests { fn get_cells_per_instance_hash() { let hash = HashBuiltinRunner::new(Some(10), true); let builtin: BuiltinRunner = hash.clone().into(); - assert_eq!(hash.cells_per_instance, builtin.cells_per_instance()) - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_cells_per_instance_range_check() { - let range_check = RangeCheckBuiltinRunner::new(Some(10), 10, true); - let builtin: BuiltinRunner = range_check.clone().into(); - assert_eq!(range_check.cells_per_instance, builtin.cells_per_instance()) + assert_eq!(CELLS_PER_HASH, builtin.cells_per_instance()) } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_cells_per_instance_ec_op() { - let ec_op = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let ec_op = EcOpBuiltinRunner::new(Some(256), true); let builtin: BuiltinRunner = ec_op.clone().into(); - assert_eq!(ec_op.cells_per_instance, builtin.cells_per_instance()) + assert_eq!(CELLS_PER_EC_OP, builtin.cells_per_instance()) } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_cells_per_instance_ecdsa() { - let signature = SignatureBuiltinRunner::new(&EcdsaInstanceDef::new(Some(10)), true); + let signature = SignatureBuiltinRunner::new(Some(10), true); let builtin: BuiltinRunner = signature.clone().into(); - assert_eq!(signature.cells_per_instance, builtin.cells_per_instance()) + assert_eq!(CELLS_PER_SIGNATURE, builtin.cells_per_instance()) } #[test] @@ -732,18 +751,10 @@ mod tests { assert_eq!(0, builtin.cells_per_instance()) } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_cells_per_instance_keccak() { - let keccak = KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true); - let builtin: BuiltinRunner = keccak.clone().into(); - assert_eq!(keccak.cells_per_instance, builtin.cells_per_instance()) - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_name_bitwise() { - let bitwise = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::new(Some(10)), true); + let bitwise = BitwiseBuiltinRunner::new(Some(10), true); let builtin: BuiltinRunner = bitwise.into(); assert_eq!(BITWISE_BUILTIN_NAME, builtin.name()) } @@ -759,7 +770,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_name_range_check() { - let range_check = RangeCheckBuiltinRunner::new(Some(10), 10, true); + let range_check = RangeCheckBuiltinRunner::::new(Some(10), true); let builtin: BuiltinRunner = range_check.into(); assert_eq!(RANGE_CHECK_BUILTIN_NAME, builtin.name()) } @@ -767,7 +778,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_name_ec_op() { - let ec_op = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let ec_op = EcOpBuiltinRunner::new(Some(256), true); let builtin: BuiltinRunner = ec_op.into(); assert_eq!(EC_OP_BUILTIN_NAME, builtin.name()) } @@ -775,7 +786,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_name_ecdsa() { - let signature = SignatureBuiltinRunner::new(&EcdsaInstanceDef::new(Some(10)), true); + let signature = SignatureBuiltinRunner::new(Some(10), true); let builtin: BuiltinRunner = signature.into(); assert_eq!(SIGNATURE_BUILTIN_NAME, builtin.name()) } @@ -791,10 +802,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units_bitwise_with_items() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::new(Some(10)), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(10), true)); let mut vm = vm!(); @@ -838,10 +846,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units_ec_op_with_items() { - let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new( - &EcOpInstanceDef::new(Some(10)), - true, - )); + let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(10), true)); let mut vm = vm!(); @@ -929,7 +934,9 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units_range_check_with_items() { - let builtin = BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(10), 12, true)); + let builtin = BuiltinRunner::RangeCheck( + RangeCheckBuiltinRunner::::new(Some(10), true), + ); let mut vm = vm!(); @@ -973,10 +980,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units_keccak_with_items() { - let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new( - &KeccakInstanceDef::new(Some(10), vec![200; 8]), - true, - )); + let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new(Some(10), true)); let mut vm = vm!(); vm.current_step = 160; @@ -985,10 +989,7 @@ mod tests { #[test] fn get_allocated_memory_units_keccak_min_steps_not_reached() { - let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new( - &KeccakInstanceDef::new(Some(10), vec![200; 8]), - true, - )); + let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new(Some(10), true)); let mut vm = vm!(); vm.current_step = 10; @@ -1016,7 +1017,9 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units_range_check() { - let builtin = BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(8), 8, true)); + let builtin = BuiltinRunner::RangeCheck( + RangeCheckBuiltinRunner::::new(Some(8), true), + ); let mut vm = vm!(); vm.current_step = 8; assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(1)); @@ -1034,10 +1037,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units_bitwise() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); let mut vm = vm!(); vm.current_step = 256; assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(5)); @@ -1046,8 +1046,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units_ec_op() { - let builtin = - BuiltinRunner::EcOp(EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true)); + let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(256), true)); let mut vm = vm!(); vm.current_step = 256; assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(7)); @@ -1056,10 +1055,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units_keccak() { - let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new( - &KeccakInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new(Some(2048), true)); let mut vm = vm!(); vm.current_step = 32768; assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(256)); @@ -1068,7 +1064,9 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_range_check_usage_range_check() { - let builtin = BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(8), 8, true)); + let builtin = BuiltinRunner::RangeCheck( + RangeCheckBuiltinRunner::::new(Some(8), true), + ); let memory = memory![((0, 0), 1), ((0, 1), 2), ((0, 2), 3), ((0, 3), 4)]; assert_eq!(builtin.get_range_check_usage(&memory), Some((0, 4))); } @@ -1092,8 +1090,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_range_check_usage_ec_op() { - let builtin = - BuiltinRunner::EcOp(EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true)); + let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(256), true)); let memory = memory![((0, 0), 1), ((0, 1), 2), ((0, 2), 3), ((0, 3), 4)]; assert_eq!(builtin.get_range_check_usage(&memory), None); } @@ -1101,10 +1098,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_range_check_usage_bitwise() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); let memory = memory![((0, 0), 1), ((0, 1), 2), ((0, 2), 3), ((0, 3), 4)]; assert_eq!(builtin.get_range_check_usage(&memory), None); } @@ -1112,40 +1106,28 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_diluted_check_units_bitwise() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); assert_eq!(builtin.get_used_diluted_check_units(270, 7), 1255); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_diluted_check_units_keccak_zero_case() { - let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new( - &KeccakInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new(Some(2048), true)); assert_eq!(builtin.get_used_diluted_check_units(270, 7), 0); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_diluted_check_units_keccak_non_zero_case() { - let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new( - &KeccakInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new(Some(2048), true)); assert_eq!(builtin.get_used_diluted_check_units(0, 8), 32768); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_diluted_check_units_ec_op() { - let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new( - &EcOpInstanceDef::new(Some(10)), - true, - )); + let builtin = BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(10), true)); assert_eq!(builtin.get_used_diluted_check_units(270, 7), 0); } @@ -1159,7 +1141,9 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_diluted_check_units_range_check() { - let builtin = BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(8), 8, true)); + let builtin = BuiltinRunner::RangeCheck( + RangeCheckBuiltinRunner::::new(Some(8), true), + ); assert_eq!(builtin.get_used_diluted_check_units(270, 7), 0); } @@ -1173,18 +1157,17 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_memory_segment_addresses_test() { - let bitwise_builtin: BuiltinRunner = - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true).into(); + let bitwise_builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(256), true).into(); assert_eq!(bitwise_builtin.get_memory_segment_addresses(), (0, None),); - let ec_op_builtin: BuiltinRunner = - EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true).into(); + let ec_op_builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(256), true).into(); assert_eq!(ec_op_builtin.get_memory_segment_addresses(), (0, None),); let hash_builtin: BuiltinRunner = HashBuiltinRunner::new(Some(8), true).into(); assert_eq!(hash_builtin.get_memory_segment_addresses(), (0, None),); let output_builtin: BuiltinRunner = OutputBuiltinRunner::new(true).into(); assert_eq!(output_builtin.get_memory_segment_addresses(), (0, None),); - let range_check_builtin: BuiltinRunner = - BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(8), 8, true)); + let range_check_builtin: BuiltinRunner = BuiltinRunner::RangeCheck( + RangeCheckBuiltinRunner::::new(Some(8), true), + ); assert_eq!( range_check_builtin.get_memory_segment_addresses(), (0, None), @@ -1203,10 +1186,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_checks_empty_memory() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); let vm = vm!(); // Unused builtin shouldn't fail security checks assert_matches!(builtin.run_security_checks(&vm), Ok(())); @@ -1215,10 +1195,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_checks_empty_offsets() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); let mut vm = vm!(); vm.segments.memory.data = vec![vec![]]; @@ -1229,10 +1206,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_checks_bitwise_missing_memory_cells_with_offsets() { - let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), true)); let mut vm = vm!(); vm.segments.memory = memory![ ((0, 1), (0, 1)), @@ -1252,22 +1226,13 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_checks_bitwise_missing_memory_cells() { - let mut bitwise_builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); - - bitwise_builtin.cells_per_instance = 2; - bitwise_builtin.n_input_cells = 5; + let bitwise_builtin = BitwiseBuiltinRunner::new(Some(256), true); let builtin: BuiltinRunner = bitwise_builtin.into(); let mut vm = vm!(); - vm.segments.memory = memory![ - ((0, 0), (0, 1)), - ((0, 1), (0, 2)), - ((0, 2), (0, 3)), - ((0, 3), (0, 4)), - ((0, 4), (0, 5)) - ]; + vm.segments.memory = memory![((0, 4), (0, 5))]; assert_matches!( builtin.run_security_checks(&vm), @@ -1320,7 +1285,8 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_checks_range_check_missing_memory_cells_with_offsets() { - let range_check_builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let range_check_builtin = + RangeCheckBuiltinRunner::::new(Some(8), true); let builtin: BuiltinRunner = range_check_builtin.into(); let mut vm = vm!(); @@ -1344,8 +1310,9 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_checks_range_check_missing_memory_cells() { - let builtin: BuiltinRunner = - BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(8), 8, true)); + let builtin: BuiltinRunner = BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::< + RC_N_PARTS_STANDARD, + >::new(Some(8), true)); let mut vm = vm!(); vm.segments.memory = memory![((0, 1), 1)]; @@ -1361,7 +1328,8 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_checks_range_check_empty() { - let range_check_builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let range_check_builtin = + RangeCheckBuiltinRunner::::new(Some(8), true); let builtin: BuiltinRunner = range_check_builtin.into(); @@ -1375,8 +1343,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_checks_validate_auto_deductions() { - let builtin: BuiltinRunner = - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true).into(); + let builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(256), true).into(); let mut vm = vm!(); vm.segments @@ -1398,7 +1365,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_ec_op_check_memory_empty() { - let ec_op_builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let ec_op_builtin = EcOpBuiltinRunner::new(Some(256), true); let builtin: BuiltinRunner = ec_op_builtin.into(); @@ -1412,7 +1379,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_ec_op_check_memory_1_element() { - let ec_op_builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let ec_op_builtin = EcOpBuiltinRunner::new(Some(256), true); let builtin: BuiltinRunner = ec_op_builtin.into(); @@ -1430,7 +1397,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_ec_op_check_memory_3_elements() { - let ec_op_builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let ec_op_builtin = EcOpBuiltinRunner::new(Some(256), true); let builtin: BuiltinRunner = ec_op_builtin.into(); @@ -1449,8 +1416,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_ec_op_missing_memory_cells_with_offsets() { - let builtin: BuiltinRunner = - EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true).into(); + let builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(256), true).into(); let mut vm = vm!(); vm.segments.memory = memory![ ((0, 1), (0, 1)), @@ -1472,7 +1438,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_security_ec_op_check_memory_gap() { - let ec_op_builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let ec_op_builtin = EcOpBuiltinRunner::new(Some(256), true); let builtin: BuiltinRunner = ec_op_builtin.into(); @@ -1505,8 +1471,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_perm_range_check_units_bitwise() { - let builtin_runner: BuiltinRunner = - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true).into(); + let builtin_runner: BuiltinRunner = BitwiseBuiltinRunner::new(Some(256), true).into(); let mut vm = vm!(); vm.current_step = 8; @@ -1519,8 +1484,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_perm_range_check_units_ec_op() { - let builtin_runner: BuiltinRunner = - EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true).into(); + let builtin_runner: BuiltinRunner = EcOpBuiltinRunner::new(Some(256), true).into(); let mut vm = vm!(); vm.current_step = 8; @@ -1559,7 +1523,8 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_perm_range_check_units_range_check() { - let builtin_runner: BuiltinRunner = RangeCheckBuiltinRunner::new(Some(8), 8, true).into(); + let builtin_runner: BuiltinRunner = + RangeCheckBuiltinRunner::::new(Some(8), true).into(); let mut vm = vm!(); vm.current_step = 8; @@ -1570,21 +1535,19 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_ratio_tests() { - let bitwise_builtin: BuiltinRunner = - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true).into(); + let bitwise_builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(256), true).into(); assert_eq!(bitwise_builtin.ratio(), (Some(256)),); - let ec_op_builtin: BuiltinRunner = - EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true).into(); + let ec_op_builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(256), true).into(); assert_eq!(ec_op_builtin.ratio(), (Some(256)),); let hash_builtin: BuiltinRunner = HashBuiltinRunner::new(Some(8), true).into(); assert_eq!(hash_builtin.ratio(), (Some(8)),); let output_builtin: BuiltinRunner = OutputBuiltinRunner::new(true).into(); assert_eq!(output_builtin.ratio(), None,); - let range_check_builtin: BuiltinRunner = - BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(8), 8, true)); + let range_check_builtin: BuiltinRunner = BuiltinRunner::RangeCheck( + RangeCheckBuiltinRunner::::new(Some(8), true), + ); assert_eq!(range_check_builtin.ratio(), (Some(8)),); - let keccak_builtin: BuiltinRunner = - KeccakBuiltinRunner::new(&KeccakInstanceDef::default(), true).into(); + let keccak_builtin: BuiltinRunner = KeccakBuiltinRunner::new(Some(2048), true).into(); assert_eq!(keccak_builtin.ratio(), (Some(2048)),); } @@ -1594,8 +1557,7 @@ mod tests { let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![4]); - let bitwise_builtin: BuiltinRunner = - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true).into(); + let bitwise_builtin: BuiltinRunner = BitwiseBuiltinRunner::new(Some(256), true).into(); assert_eq!(bitwise_builtin.get_used_instances(&vm.segments), Ok(1)); } @@ -1605,8 +1567,7 @@ mod tests { let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![4]); - let ec_op_builtin: BuiltinRunner = - EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true).into(); + let ec_op_builtin: BuiltinRunner = EcOpBuiltinRunner::new(Some(256), true).into(); assert_eq!(ec_op_builtin.get_used_instances(&vm.segments), Ok(1)); } @@ -1635,8 +1596,9 @@ mod tests { let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![4]); - let range_check_builtin: BuiltinRunner = - BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(8), 8, true)); + let range_check_builtin: BuiltinRunner = BuiltinRunner::RangeCheck( + RangeCheckBuiltinRunner::::new(Some(8), true), + ); assert_eq!(range_check_builtin.get_used_instances(&vm.segments), Ok(4)); } @@ -1644,22 +1606,16 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn runners_final_stack() { let mut builtins = vec![ - BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - false, - )), - BuiltinRunner::EcOp(EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), false)), + BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), false)), + BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(256), false)), BuiltinRunner::Hash(HashBuiltinRunner::new(Some(1), false)), BuiltinRunner::Output(OutputBuiltinRunner::new(false)), - BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(8), 8, false)), - BuiltinRunner::Keccak(KeccakBuiltinRunner::new( - &KeccakInstanceDef::default(), - false, - )), - BuiltinRunner::Signature(SignatureBuiltinRunner::new( - &EcdsaInstanceDef::default(), + BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::::new( + Some(8), false, )), + BuiltinRunner::Keccak(KeccakBuiltinRunner::new(Some(2048), false)), + BuiltinRunner::Signature(SignatureBuiltinRunner::new(Some(512), false)), ]; let vm = vm!(); @@ -1672,22 +1628,16 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn runners_set_stop_ptr() { let builtins = vec![ - BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new( - &BitwiseInstanceDef::default(), - false, - )), - BuiltinRunner::EcOp(EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), false)), + BuiltinRunner::Bitwise(BitwiseBuiltinRunner::new(Some(256), false)), + BuiltinRunner::EcOp(EcOpBuiltinRunner::new(Some(256), false)), BuiltinRunner::Hash(HashBuiltinRunner::new(Some(1), false)), BuiltinRunner::Output(OutputBuiltinRunner::new(false)), - BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(8), 8, false)), - BuiltinRunner::Keccak(KeccakBuiltinRunner::new( - &KeccakInstanceDef::default(), - false, - )), - BuiltinRunner::Signature(SignatureBuiltinRunner::new( - &EcdsaInstanceDef::default(), + BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::::new( + Some(8), false, )), + BuiltinRunner::Keccak(KeccakBuiltinRunner::new(Some(2048), false)), + BuiltinRunner::Signature(SignatureBuiltinRunner::new(Some(512), false)), BuiltinRunner::Poseidon(PoseidonBuiltinRunner::new(Some(32), false)), BuiltinRunner::SegmentArena(SegmentArenaBuiltinRunner::new(false)), ]; diff --git a/vm/src/vm/runners/builtin_runner/modulo.rs b/vm/src/vm/runners/builtin_runner/modulo.rs new file mode 100644 index 0000000000..91254d4789 --- /dev/null +++ b/vm/src/vm/runners/builtin_runner/modulo.rs @@ -0,0 +1,900 @@ +use crate::{ + air_private_input::{ModInput, ModInputInstance, ModInputMemoryVars, PrivateInput}, + math_utils::{div_mod_unsigned, safe_div_usize}, + serde::deserialize_program::BuiltinName, + stdlib::{ + borrow::Cow, + collections::BTreeMap, + prelude::{Box, Vec}, + }, + types::{ + errors::math_errors::MathError, + instance_definitions::mod_instance_def::{ModInstanceDef, CELLS_PER_MOD, N_WORDS}, + relocatable::{relocate_address, MaybeRelocatable, Relocatable}, + }, + vm::{ + errors::{ + memory_errors::MemoryError, runner_errors::RunnerError, vm_errors::VirtualMachineError, + }, + vm_core::VirtualMachine, + vm_memory::{memory::Memory, memory_segments::MemorySegmentManager}, + }, + Felt252, +}; +use core::{fmt::Display, ops::Shl}; +use num_bigint::BigUint; +use num_integer::div_ceil; +use num_integer::Integer; +use num_traits::One; +use num_traits::Zero; + +//The maximum n value that the function fill_memory accepts. +const FILL_MEMORY_MAX: usize = 100000; + +const VALUES_PTR_OFFSET: u32 = 4; +const OFFSETS_PTR_OFFSET: u32 = 5; +const N_OFFSET: u32 = 6; + +#[derive(Debug, Clone)] +pub struct ModBuiltinRunner { + builtin_type: ModBuiltinType, + base: usize, + pub(crate) stop_ptr: Option, + instance_def: ModInstanceDef, + pub(crate) included: bool, + zero_segment_index: usize, + zero_segment_size: usize, + // Precomputed powers used for reading and writing values that are represented as n_words words of word_bit_len bits each. + shift: BigUint, + shift_powers: [BigUint; N_WORDS], +} + +#[derive(Debug, Clone)] +pub enum ModBuiltinType { + Mul, + Add, +} + +#[derive(Debug)] +pub enum Operation { + Mul, + Add, + Sub, + DivMod(BigUint), +} + +impl Display for Operation { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Operation::Mul => "*".fmt(f), + Operation::Add => "+".fmt(f), + Operation::Sub => "-".fmt(f), + Operation::DivMod(_) => "/".fmt(f), + } + } +} + +#[derive(Debug, Default)] +struct Inputs { + p: BigUint, + p_values: [Felt252; N_WORDS], + values_ptr: Relocatable, + offsets_ptr: Relocatable, + n: usize, +} + +impl ModBuiltinRunner { + pub(crate) fn new_add_mod(instance_def: &ModInstanceDef, included: bool) -> Self { + Self::new(instance_def.clone(), included, ModBuiltinType::Add) + } + + pub(crate) fn new_mul_mod(instance_def: &ModInstanceDef, included: bool) -> Self { + Self::new(instance_def.clone(), included, ModBuiltinType::Mul) + } + + fn new(instance_def: ModInstanceDef, included: bool, builtin_type: ModBuiltinType) -> Self { + let shift = BigUint::one().shl(instance_def.word_bit_len); + let shift_powers = core::array::from_fn(|i| shift.pow(i as u32)); + let zero_segment_size = core::cmp::max(N_WORDS, instance_def.batch_size * 3); + Self { + builtin_type, + base: 0, + stop_ptr: None, + instance_def, + included, + zero_segment_index: 0, + zero_segment_size, + shift, + shift_powers, + } + } + + pub fn name(&self) -> &'static str { + match self.builtin_type { + ModBuiltinType::Mul => super::MUL_MOD_BUILTIN_NAME, + ModBuiltinType::Add => super::ADD_MOD_BUILTIN_NAME, + } + } + + pub fn identifier(&self) -> BuiltinName { + match self.builtin_type { + ModBuiltinType::Mul => BuiltinName::mul_mod, + ModBuiltinType::Add => BuiltinName::add_mod, + } + } + + pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) { + self.base = segments.add().segment_index as usize; // segments.add() always returns a positive index + self.zero_segment_index = segments.add_zero_segment(self.zero_segment_size) + } + + pub fn initial_stack(&self) -> Vec { + if self.included { + vec![MaybeRelocatable::from((self.base as isize, 0))] + } else { + vec![] + } + } + + pub fn base(&self) -> usize { + self.base + } + + pub fn ratio(&self) -> Option { + self.instance_def.ratio + } + + pub fn batch_size(&self) -> usize { + self.instance_def.batch_size + } + + pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result { + segments + .get_segment_used_size(self.base) + .ok_or(MemoryError::MissingSegmentUsedSizes) + } + + pub fn get_used_instances( + &self, + segments: &MemorySegmentManager, + ) -> Result { + let used_cells = self.get_used_cells(segments)?; + Ok(div_ceil(used_cells, CELLS_PER_MOD as usize)) + } + + pub(crate) fn air_private_input(&self, segments: &MemorySegmentManager) -> Vec { + let segment_index = self.base as isize; + let segment_size = segments + .get_segment_used_size(self.base) + .unwrap_or_default(); + let relocation_table = segments.relocate_segments().unwrap_or_default(); + let mut instances = Vec::::new(); + for instance in 0..segment_size + .checked_div(CELLS_PER_MOD as usize) + .unwrap_or_default() + { + let instance_addr_offset = instance * CELLS_PER_MOD as usize; + let values_ptr = segments + .memory + .get_relocatable( + ( + segment_index, + instance_addr_offset + VALUES_PTR_OFFSET as usize, + ) + .into(), + ) + .unwrap_or_default(); + let offsets_ptr = segments + .memory + .get_relocatable( + ( + segment_index, + instance_addr_offset + OFFSETS_PTR_OFFSET as usize, + ) + .into(), + ) + .unwrap_or_default(); + let n = segments + .memory + .get_usize((segment_index, instance_addr_offset + N_OFFSET as usize).into()) + .unwrap_or_default(); + let p_values: [Felt252; N_WORDS] = core::array::from_fn(|i| { + segments + .memory + .get_integer((segment_index, instance_addr_offset + i).into()) + .unwrap_or_default() + .into_owned() + }); + let mut batch = BTreeMap::::new(); + let fetch_offset_and_words = |var_index: usize, + index_in_batch: usize| + -> (usize, [Felt252; N_WORDS]) { + let offset = segments + .memory + .get_usize((offsets_ptr + (3 * index_in_batch + var_index)).unwrap_or_default()) + .unwrap_or_default(); + let words: [Felt252; N_WORDS] = core::array::from_fn(|i| { + segments + .memory + .get_integer((values_ptr + (offset + i)).unwrap_or_default()) + .unwrap_or_default() + .into_owned() + }); + (offset, words) + }; + for index_in_batch in 0..self.batch_size() { + let (a_offset, a_values) = fetch_offset_and_words(0, index_in_batch); + let (b_offset, b_values) = fetch_offset_and_words(1, index_in_batch); + let (c_offset, c_values) = fetch_offset_and_words(2, index_in_batch); + batch.insert( + index_in_batch, + ModInputMemoryVars { + a_offset, + b_offset, + c_offset, + a0: a_values[0], + a1: a_values[1], + a2: a_values[2], + a3: a_values[3], + b0: b_values[0], + b1: b_values[1], + b2: b_values[2], + b3: b_values[3], + c0: c_values[0], + c1: c_values[1], + c2: c_values[2], + c3: c_values[3], + }, + ); + } + instances.push(ModInputInstance { + index: instance, + p0: p_values[0], + p1: p_values[1], + p2: p_values[2], + p3: p_values[3], + values_ptr: relocate_address(values_ptr, &relocation_table).unwrap_or_default(), + offsets_ptr: relocate_address(offsets_ptr, &relocation_table).unwrap_or_default(), + n, + batch, + }); + } + + vec![PrivateInput::Mod(ModInput { + instances, + zero_value_address: relocation_table + .get(self.zero_segment_index) + .cloned() + .unwrap_or_default(), + })] + } + + // Reads N_WORDS from memory, starting at address=addr. + // Returns the words and the value if all words are in memory. + // Verifies that all words are integers and are bounded by 2**self.instance_def.word_bit_len. + fn read_n_words_value( + &self, + memory: &Memory, + addr: Relocatable, + ) -> Result<([Felt252; N_WORDS], Option), RunnerError> { + let mut words = Default::default(); + let mut value = BigUint::zero(); + for i in 0..N_WORDS { + let addr_i = (addr + i)?; + match memory.get(&addr_i).map(Cow::into_owned) { + None => return Ok((words, None)), + Some(MaybeRelocatable::RelocatableValue(_)) => { + return Err(MemoryError::ExpectedInteger(Box::new(addr_i)).into()) + } + Some(MaybeRelocatable::Int(word)) => { + let biguint_word = word.to_biguint(); + if biguint_word >= self.shift { + return Err(RunnerError::WordExceedsModBuiltinWordBitLen(Box::new(( + addr_i, + self.instance_def.word_bit_len, + word, + )))); + } + words[i] = word; + value += biguint_word * &self.shift_powers[i]; + } + } + } + Ok((words, Some(value))) + } + + // Reads the inputs to the builtin (see Inputs) from the memory at address=addr. + // Returns a struct with the inputs. Asserts that it exists in memory. + // Returns also the value of p, not just its words. + fn read_inputs(&self, memory: &Memory, addr: Relocatable) -> Result { + let values_ptr = memory.get_relocatable((addr + VALUES_PTR_OFFSET)?)?; + let offsets_ptr = memory.get_relocatable((addr + OFFSETS_PTR_OFFSET)?)?; + let n = memory.get_usize((addr + N_OFFSET)?)?; + if n < 1 { + return Err(RunnerError::ModBuiltinNLessThanOne(Box::new(( + self.name(), + n, + )))); + } + let (p_values, p) = self.read_n_words_value(memory, addr)?; + let p = p.ok_or_else(|| { + RunnerError::ModBuiltinMissingValue(Box::new(( + self.name(), + (addr + N_WORDS).unwrap_or_default(), + ))) + })?; + Ok(Inputs { + p, + p_values, + values_ptr, + offsets_ptr, + n, + }) + } + + // Reads the memory variables to the builtin (see MEMORY_VARS) from the memory given + // the inputs (specifically, values_ptr and offsets_ptr). + // Computes and returns the values of a, b, and c. + fn read_memory_vars( + &self, + memory: &Memory, + values_ptr: Relocatable, + offsets_ptr: Relocatable, + index_in_batch: usize, + ) -> Result<(BigUint, BigUint, BigUint), RunnerError> { + let compute_value = |index: usize| -> Result { + let offset = memory.get_usize((offsets_ptr + (index + 3 * index_in_batch))?)?; + let value_addr = (values_ptr + offset)?; + let (_, value) = self.read_n_words_value(memory, value_addr)?; + let value = value.ok_or_else(|| { + RunnerError::ModBuiltinMissingValue(Box::new(( + self.name(), + (value_addr + N_WORDS).unwrap_or_default(), + ))) + })?; + Ok(value) + }; + + let a = compute_value(0)?; + let b = compute_value(1)?; + let c = compute_value(2)?; + Ok((a, b, c)) + } + + fn fill_inputs( + &self, + memory: &mut Memory, + builtin_ptr: Relocatable, + inputs: &Inputs, + ) -> Result<(), RunnerError> { + if inputs.n > FILL_MEMORY_MAX { + return Err(RunnerError::FillMemoryMaxExceeded(Box::new(( + self.name(), + FILL_MEMORY_MAX, + )))); + } + let n_instances = safe_div_usize(inputs.n, self.instance_def.batch_size)?; + for instance in 1..n_instances { + let instance_ptr = (builtin_ptr + instance * CELLS_PER_MOD as usize)?; + for i in 0..N_WORDS { + memory.insert_as_accessed((instance_ptr + i)?, &inputs.p_values[i])?; + } + memory.insert_as_accessed((instance_ptr + VALUES_PTR_OFFSET)?, &inputs.values_ptr)?; + memory.insert_as_accessed( + (instance_ptr + OFFSETS_PTR_OFFSET)?, + (inputs.offsets_ptr + (3 * instance * self.instance_def.batch_size))?, + )?; + memory.insert_as_accessed( + (instance_ptr + N_OFFSET)?, + inputs + .n + .saturating_sub(instance * self.instance_def.batch_size), + )?; + } + Ok(()) + } + + // Copies the first offsets in the offsets table to its end, n_copies times. + fn fill_offsets( + &self, + memory: &mut Memory, + offsets_ptr: Relocatable, + index: usize, + n_copies: usize, + ) -> Result<(), RunnerError> { + if n_copies.is_zero() { + return Ok(()); + } + for i in 0..3_usize { + let addr = (offsets_ptr + i)?; + let offset = memory + .get(&((offsets_ptr + i)?)) + .ok_or_else(|| MemoryError::UnknownMemoryCell(Box::new(addr)))? + .into_owned(); + for copy_i in 0..n_copies { + memory.insert_as_accessed((offsets_ptr + (3 * (index + copy_i) + i))?, &offset)?; + } + } + Ok(()) + } + + // Given a value, writes its n_words to memory, starting at address=addr. + fn write_n_words_value( + &self, + memory: &mut Memory, + addr: Relocatable, + value: BigUint, + ) -> Result<(), RunnerError> { + let mut value = value; + for i in 0..N_WORDS { + let word = value.mod_floor(&self.shift); + memory.insert_as_accessed((addr + i)?, Felt252::from(word))?; + value = value.div_floor(&self.shift) + } + if !value.is_zero() { + return Err(RunnerError::WriteNWordsValueNotZero(self.name())); + } + Ok(()) + } + + // Fills a value in the values table, if exactly one value is missing. + // Returns true on success or if all values are already known. + fn fill_value( + &self, + memory: &mut Memory, + inputs: &Inputs, + index: usize, + op: &Operation, + inv_op: &Operation, + ) -> Result { + let mut addresses = Vec::new(); + let mut values = Vec::new(); + for i in 0..3 { + let addr = (inputs.values_ptr + + memory + .get_integer((inputs.offsets_ptr + (3 * index + i))?)? + .as_ref())?; + addresses.push(addr); + let (_, value) = self.read_n_words_value(memory, addr)?; + values.push(value) + } + let (a, b, c) = (&values[0], &values[1], &values[2]); + match (a, b, c) { + // Deduce c from a and b and write it to memory. + (Some(a), Some(b), None) => { + let value = apply_op(a, b, op)?.mod_floor(&inputs.p); + self.write_n_words_value(memory, addresses[2], value)?; + Ok(true) + } + // Deduce b from a and c and write it to memory. + (Some(a), None, Some(c)) => { + let value = apply_op(c, a, inv_op)?.mod_floor(&inputs.p); + self.write_n_words_value(memory, addresses[1], value)?; + Ok(true) + } + // Deduce a from b and c and write it to memory. + (None, Some(b), Some(c)) => { + let value = apply_op(c, b, inv_op)?.mod_floor(&inputs.p); + self.write_n_words_value(memory, addresses[0], value)?; + Ok(true) + } + // All values are already known. + (Some(_), Some(_), Some(_)) => Ok(true), + _ => Ok(false), + } + } + + /// NOTE: It is advisable to use VirtualMachine::mod_builtin_fill_memory instead of this method directly + /// when implementing hints to avoid cloning the runners + + /// Fills the memory with inputs to the builtin instances based on the inputs to the + /// first instance, pads the offsets table to fit the number of operations writen in the + /// input to the first instance, and caculates missing values in the values table. + + /// For each builtin, the given tuple is of the form (builtin_ptr, builtin_runner, n), + /// where n is the number of operations in the offsets table (i.e., the length of the + /// offsets table is 3*n). + + /// The number of operations written to the input of the first instance n' should be at + /// least n and a multiple of batch_size. Previous offsets are copied to the end of the + /// offsets table to make its length 3n'. + pub fn fill_memory( + memory: &mut Memory, + add_mod: Option<(Relocatable, &ModBuiltinRunner, usize)>, + mul_mod: Option<(Relocatable, &ModBuiltinRunner, usize)>, + ) -> Result<(), RunnerError> { + if add_mod.is_none() && mul_mod.is_none() { + return Err(RunnerError::FillMemoryNoBuiltinSet); + } + // Check that the instance definitions of the builtins are the same. + if let (Some((_, add_mod, _)), Some((_, mul_mod, _))) = (add_mod, mul_mod) { + if add_mod.instance_def.word_bit_len != mul_mod.instance_def.word_bit_len { + return Err(RunnerError::ModBuiltinsMismatchedInstanceDef); + } + } + // Fill the inputs to the builtins. + let (add_mod_inputs, add_mod_n) = + if let Some((add_mod_addr, add_mod, add_mod_index)) = add_mod { + let add_mod_inputs = add_mod.read_inputs(memory, add_mod_addr)?; + add_mod.fill_inputs(memory, add_mod_addr, &add_mod_inputs)?; + add_mod.fill_offsets( + memory, + add_mod_inputs.offsets_ptr, + add_mod_index, + add_mod_inputs.n.saturating_sub(add_mod_index), + )?; + (add_mod_inputs, add_mod_index) + } else { + Default::default() + }; + + let (mul_mod_inputs, mul_mod_n) = + if let Some((mul_mod_addr, mul_mod, mul_mod_index)) = mul_mod { + let mul_mod_inputs = mul_mod.read_inputs(memory, mul_mod_addr)?; + mul_mod.fill_inputs(memory, mul_mod_addr, &mul_mod_inputs)?; + mul_mod.fill_offsets( + memory, + mul_mod_inputs.offsets_ptr, + mul_mod_index, + mul_mod_inputs.n.saturating_sub(mul_mod_index), + )?; + (mul_mod_inputs, mul_mod_index) + } else { + Default::default() + }; + + // Get one of the builtin runners - the rest of this function doesn't depend on batch_size. + let mod_runner = if let Some((_, add_mod, _)) = add_mod { + add_mod + } else { + mul_mod.unwrap().1 + }; + // Fill the values table. + let mut add_mod_index = 0; + let mut mul_mod_index = 0; + // Create operation here to avoid cloning p in the loop + let div_operation = Operation::DivMod(mul_mod_inputs.p.clone()); + while add_mod_index < add_mod_n || mul_mod_index < mul_mod_n { + if add_mod_index < add_mod_n + && mod_runner.fill_value( + memory, + &add_mod_inputs, + add_mod_index, + &Operation::Add, + &Operation::Sub, + )? + { + add_mod_index += 1; + } else if mul_mod_index < mul_mod_n + && mod_runner.fill_value( + memory, + &mul_mod_inputs, + mul_mod_index, + &Operation::Mul, + &div_operation, + )? + { + mul_mod_index += 1; + } else { + return Err(RunnerError::FillMemoryCoudNotFillTable( + add_mod_index, + mul_mod_index, + )); + } + } + Ok(()) + } + + // Additional checks added to the standard builtin runner security checks + pub(crate) fn run_additional_security_checks( + &self, + vm: &VirtualMachine, + ) -> Result<(), VirtualMachineError> { + let segment_size = vm + .get_segment_used_size(self.base) + .ok_or(MemoryError::MissingSegmentUsedSizes)?; + let n_instances = div_ceil(segment_size, CELLS_PER_MOD as usize); + let mut prev_inputs = Inputs::default(); + for instance in 0..n_instances { + let inputs = self.read_inputs( + &vm.segments.memory, + (self.base as isize, instance * CELLS_PER_MOD as usize).into(), + )?; + if !instance.is_zero() && prev_inputs.n > self.instance_def.batch_size { + for i in 0..N_WORDS { + if inputs.p_values[i] != prev_inputs.p_values[i] { + return Err(RunnerError::ModBuiltinSecurityCheck(Box::new((self.name(), format!("inputs.p_values[i] != prev_inputs.p_values[i]. Got: i={}, inputs.p_values[i]={}, prev_inputs.p_values[i]={}", + i, inputs.p_values[i], prev_inputs.p_values[i])))).into()); + } + } + if inputs.values_ptr != prev_inputs.values_ptr { + return Err(RunnerError::ModBuiltinSecurityCheck(Box::new((self.name(), format!("inputs.values_ptr != prev_inputs.values_ptr. Got: inputs.values_ptr={}, prev_inputs.values_ptr={}", + inputs.values_ptr, prev_inputs.values_ptr)))).into()); + } + if inputs.offsets_ptr + != (prev_inputs.offsets_ptr + (3 * self.instance_def.batch_size))? + { + return Err(RunnerError::ModBuiltinSecurityCheck(Box::new((self.name(), format!("inputs.offsets_ptr != prev_inputs.offsets_ptr + 3 * batch_size. Got: inputs.offsets_ptr={}, prev_inputs.offsets_ptr={}, batch_size={}", + inputs.values_ptr, prev_inputs.values_ptr, self.instance_def.batch_size)))).into()); + } + if inputs.n != prev_inputs.n.saturating_sub(self.instance_def.batch_size) { + return Err(RunnerError::ModBuiltinSecurityCheck(Box::new((self.name(), format!("inputs.n != prev_inputs.n - batch_size. Got: inputs.n={}, prev_inputs.n={}, batch_size={}", + inputs.n, prev_inputs.n, self.instance_def.batch_size)))).into()); + } + } + for index_in_batch in 0..self.instance_def.batch_size { + let (a, b, c) = self.read_memory_vars( + &vm.segments.memory, + inputs.values_ptr, + inputs.offsets_ptr, + index_in_batch, + )?; + let op = match self.builtin_type { + ModBuiltinType::Add => Operation::Add, + ModBuiltinType::Mul => Operation::Mul, + }; + let a_op_b = apply_op(&a, &b, &op)?.mod_floor(&inputs.p); + if a_op_b != c.mod_floor(&inputs.p) { + // Build error string + let p = inputs.p; + let error_string = format!("Expected a {op} b == c (mod p). Got: instance={instance}, batch={index_in_batch}, p={p}, a={a}, b={b}, c={c}."); + return Err(RunnerError::ModBuiltinSecurityCheck(Box::new(( + self.name(), + error_string, + ))) + .into()); + } + } + prev_inputs = inputs; + } + if !n_instances.is_zero() && prev_inputs.n != self.instance_def.batch_size { + return Err(RunnerError::ModBuiltinSecurityCheck(Box::new(( + self.name(), + format!( + "prev_inputs.n != batch_size Got: prev_inputs.n={}, batch_size={}", + prev_inputs.n, self.instance_def.batch_size + ), + ))) + .into()); + } + Ok(()) + } + + #[cfg(test)] + #[cfg(feature = "mod_builtin")] + // Testing method used to test programs that use parameters which are not included in any layout + // For example, programs with large batch size + pub(crate) fn override_layout_params(&mut self, batch_size: usize, word_bit_len: u32) { + self.instance_def.batch_size = batch_size; + self.instance_def.word_bit_len = word_bit_len; + self.shift = BigUint::one().shl(word_bit_len); + self.shift_powers = core::array::from_fn(|i| self.shift.pow(i as u32)); + self.zero_segment_size = core::cmp::max(N_WORDS, batch_size * 3); + } +} + +fn apply_op(lhs: &BigUint, rhs: &BigUint, op: &Operation) -> Result { + Ok(match op { + Operation::Mul => lhs * rhs, + Operation::Add => lhs + rhs, + Operation::Sub => lhs - rhs, + Operation::DivMod(ref p) => div_mod_unsigned(lhs, rhs, p)?, + }) +} + +#[cfg(test)] +mod tests { + + #[test] + #[cfg(feature = "mod_builtin")] + fn test_air_private_input_small_batch_size() { + use super::*; + use crate::{ + air_private_input::{ModInput, ModInputInstance, ModInputMemoryVars, PrivateInput}, + hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor, + utils::test_utils::Program, + vm::runners::{ + builtin_runner::{BuiltinRunner, ADD_MOD_BUILTIN_NAME, MUL_MOD_BUILTIN_NAME}, + cairo_runner::CairoRunner, + }, + Felt252, + }; + + let program_data = include_bytes!( + "../../../../../cairo_programs/mod_builtin_feature/proof/mod_builtin.json" + ); + + let mut hint_processor = BuiltinHintProcessor::new_empty(); + let program = Program::from_bytes(program_data, Some("main")).unwrap(); + let mut runner = CairoRunner::new(&program, "all_cairo", true).unwrap(); + + let mut vm = VirtualMachine::new(false); + let end = runner.initialize(&mut vm, false).unwrap(); + // Modify add_mod & mul_mod params + for runner in vm.get_builtin_runners_as_mut() { + if let BuiltinRunner::Mod(runner) = runner { + runner.override_layout_params(1, 3) + } + } + + runner + .run_until_pc(end, &mut vm, &mut hint_processor) + .unwrap(); + runner + .run_for_steps(1, &mut vm, &mut hint_processor) + .unwrap(); + runner + .end_run(false, false, &mut vm, &mut hint_processor) + .unwrap(); + runner.read_return_values(&mut vm, false).unwrap(); + runner.finalize_segments(&mut vm).unwrap(); + + let air_private_input = runner.get_air_private_input(&vm); + assert_eq!( + air_private_input.0.get(ADD_MOD_BUILTIN_NAME).unwrap()[0], + PrivateInput::Mod(ModInput { + instances: vec![ + ModInputInstance { + index: 0, + p0: Felt252::ONE, + p1: Felt252::ONE, + p2: Felt252::ZERO, + p3: Felt252::ZERO, + values_ptr: 23023, + offsets_ptr: 23055, + n: 2, + batch: BTreeMap::from([( + 0, + ModInputMemoryVars { + a_offset: 0, + a0: Felt252::ONE, + a1: Felt252::ZERO, + a2: Felt252::ZERO, + a3: Felt252::ZERO, + b_offset: 12, + b0: Felt252::ZERO, + b1: Felt252::ZERO, + b2: Felt252::ZERO, + b3: Felt252::ZERO, + c_offset: 4, + c0: Felt252::TWO, + c1: Felt252::ONE, + c2: Felt252::ZERO, + c3: Felt252::ZERO + } + ),]) + }, + ModInputInstance { + index: 1, + p0: Felt252::ONE, + p1: Felt252::ONE, + p2: Felt252::ZERO, + p3: Felt252::ZERO, + values_ptr: 23023, + offsets_ptr: 23058, + n: 1, + batch: BTreeMap::from([( + 0, + ModInputMemoryVars { + a_offset: 16, + a0: Felt252::ZERO, + a1: Felt252::ZERO, + a2: Felt252::ZERO, + a3: Felt252::ZERO, + b_offset: 20, + b0: Felt252::TWO, + b1: Felt252::ZERO, + b2: Felt252::ZERO, + b3: Felt252::ZERO, + c_offset: 24, + c0: Felt252::TWO, + c1: Felt252::ZERO, + c2: Felt252::ZERO, + c3: Felt252::ZERO + } + ),]) + } + ], + zero_value_address: 22123 + }) + ); + assert_eq!( + air_private_input.0.get(MUL_MOD_BUILTIN_NAME).unwrap()[0], + PrivateInput::Mod(ModInput { + instances: vec![ + ModInputInstance { + index: 0, + p0: Felt252::ONE, + p1: Felt252::ONE, + p2: Felt252::ZERO, + p3: Felt252::ZERO, + values_ptr: 23023, + offsets_ptr: 23061, + n: 3, + batch: BTreeMap::from([( + 0, + ModInputMemoryVars { + a_offset: 12, + a0: Felt252::ZERO, + a1: Felt252::ZERO, + a2: Felt252::ZERO, + a3: Felt252::ZERO, + b_offset: 8, + b0: Felt252::TWO, + b1: Felt252::ZERO, + b2: Felt252::ZERO, + b3: Felt252::ZERO, + c_offset: 16, + c0: Felt252::ZERO, + c1: Felt252::ZERO, + c2: Felt252::ZERO, + c3: Felt252::ZERO + } + ),]) + }, + ModInputInstance { + index: 1, + p0: Felt252::ONE, + p1: Felt252::ONE, + p2: Felt252::ZERO, + p3: Felt252::ZERO, + values_ptr: 23023, + offsets_ptr: 23064, + n: 2, + batch: BTreeMap::from([( + 0, + ModInputMemoryVars { + a_offset: 0, + a0: Felt252::ONE, + a1: Felt252::ZERO, + a2: Felt252::ZERO, + a3: Felt252::ZERO, + b_offset: 8, + b0: Felt252::TWO, + b1: Felt252::ZERO, + b2: Felt252::ZERO, + b3: Felt252::ZERO, + c_offset: 20, + c0: Felt252::TWO, + c1: Felt252::ZERO, + c2: Felt252::ZERO, + c3: Felt252::ZERO + } + ),]) + }, + ModInputInstance { + index: 2, + p0: Felt252::ONE, + p1: Felt252::ONE, + p2: Felt252::ZERO, + p3: Felt252::ZERO, + values_ptr: 23023, + offsets_ptr: 23067, + n: 1, + batch: BTreeMap::from([( + 0, + ModInputMemoryVars { + a_offset: 8, + a0: Felt252::TWO, + a1: Felt252::ZERO, + a2: Felt252::ZERO, + a3: Felt252::ZERO, + b_offset: 28, + b0: Felt252::ONE, + b1: Felt252::ZERO, + b2: Felt252::ZERO, + b3: Felt252::ZERO, + c_offset: 24, + c0: Felt252::TWO, + c1: Felt252::ZERO, + c2: Felt252::ZERO, + c3: Felt252::ZERO + } + ),]) + } + ], + zero_value_address: 22123 + }) + ) + } +} diff --git a/vm/src/vm/runners/builtin_runner/output.rs b/vm/src/vm/runners/builtin_runner/output.rs index c774bb330d..c6283d3756 100644 --- a/vm/src/vm/runners/builtin_runner/output.rs +++ b/vm/src/vm/runners/builtin_runner/output.rs @@ -2,16 +2,26 @@ use crate::stdlib::{collections::HashMap, prelude::*}; use crate::types::relocatable::{MaybeRelocatable, Relocatable}; use crate::vm::errors::memory_errors::MemoryError; use crate::vm::errors::runner_errors::RunnerError; -use crate::vm::runners::cairo_pie::{BuiltinAdditionalData, OutputBuiltinAdditionalData}; +use crate::vm::runners::cairo_pie::{ + Attributes, BuiltinAdditionalData, OutputBuiltinAdditionalData, Pages, PublicMemoryPage, +}; use crate::vm::vm_core::VirtualMachine; -use crate::vm::vm_memory::memory::Memory; use crate::vm::vm_memory::memory_segments::MemorySegmentManager; use super::OUTPUT_BUILTIN_NAME; +#[derive(Debug, Clone, PartialEq)] +pub struct OutputBuiltinState { + pub base: usize, + pub pages: Pages, + pub attributes: Attributes, +} + #[derive(Debug, Clone)] pub struct OutputBuiltinRunner { base: usize, + pub(crate) pages: Pages, + pub(crate) attributes: Attributes, pub(crate) stop_ptr: Option, pub(crate) included: bool, } @@ -20,11 +30,21 @@ impl OutputBuiltinRunner { pub fn new(included: bool) -> OutputBuiltinRunner { OutputBuiltinRunner { base: 0, + pages: HashMap::default(), + attributes: HashMap::default(), stop_ptr: None, included, } } + pub fn new_state(&mut self, base: usize, included: bool) { + self.base = base; + self.pages = HashMap::default(); + self.attributes = HashMap::default(); + self.stop_ptr = None; + self.included = included; + } + pub fn initialize_segments(&mut self, segments: &mut MemorySegmentManager) { self.base = segments.add().segment_index as usize // segments.add() always returns a positive index } @@ -41,24 +61,10 @@ impl OutputBuiltinRunner { self.base } - pub fn add_validation_rule(&self, _memory: &mut Memory) {} - - pub fn deduce_memory_cell( - &self, - _address: Relocatable, - _memory: &Memory, - ) -> Result, RunnerError> { - Ok(None) - } - pub fn get_allocated_memory_units(&self, _vm: &VirtualMachine) -> Result { Ok(0) } - pub fn get_memory_segment_addresses(&self) -> (usize, Option) { - (self.base, self.stop_ptr) - } - pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result { segments .get_segment_used_size(self.base) @@ -108,16 +114,71 @@ impl OutputBuiltinRunner { } } + pub fn add_attribute(&mut self, name: String, value: Vec) { + self.attributes.insert(name, value); + } + pub fn get_additional_data(&self) -> BuiltinAdditionalData { BuiltinAdditionalData::Output(OutputBuiltinAdditionalData { - pages: HashMap::default(), - attributes: HashMap::default(), + pages: self.pages.clone(), + attributes: self.attributes.clone(), }) } pub(crate) fn set_stop_ptr_offset(&mut self, offset: usize) { self.stop_ptr = Some(offset) } + + pub fn set_state(&mut self, new_state: OutputBuiltinState) { + self.base = new_state.base; + self.pages = new_state.pages; + self.attributes = new_state.attributes; + } + + pub fn get_state(&mut self) -> OutputBuiltinState { + OutputBuiltinState { + base: self.base, + pages: self.pages.clone(), + attributes: self.attributes.clone(), + } + } + + pub fn add_page( + &mut self, + page_id: usize, + page_start: Relocatable, + page_size: usize, + ) -> Result<(), RunnerError> { + if page_start.segment_index as usize != self.base { + return Err(RunnerError::PageNotOnSegment(page_start, self.base)); + } + + self.pages.insert( + page_id, + PublicMemoryPage { + start: page_start.offset, + size: page_size, + }, + ); + + Ok(()) + } + + pub fn get_public_memory( + &self, + segments: &MemorySegmentManager, + ) -> Result, RunnerError> { + let size = self.get_used_cells(segments)?; + + let mut public_memory: Vec<(usize, usize)> = (0..size).map(|i| (i, 0)).collect(); + for (page_id, page) in self.pages.iter() { + for index in 0..page.size { + public_memory[page.start + index].1 = *page_id; + } + } + + Ok(public_memory) + } } impl Default for OutputBuiltinRunner { @@ -301,54 +362,6 @@ mod tests { assert_eq!(initial_stack.len(), 1); } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_segment_addresses() { - let builtin = OutputBuiltinRunner::new(true); - - assert_eq!(builtin.get_memory_segment_addresses(), (0, None),); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_missing_segment_used_sizes() { - let builtin = BuiltinRunner::Output(OutputBuiltinRunner::new(true)); - let vm = vm!(); - - assert_eq!( - builtin.get_memory_accesses(&vm), - Err(MemoryError::MissingSegmentUsedSizes), - ); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_empty() { - let builtin = BuiltinRunner::Output(OutputBuiltinRunner::new(true)); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![0]); - assert_eq!(builtin.get_memory_accesses(&vm), Ok(vec![])); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses() { - let builtin = BuiltinRunner::Output(OutputBuiltinRunner::new(true)); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![4]); - assert_eq!( - builtin.get_memory_accesses(&vm), - Ok(vec![ - (builtin.base() as isize, 0).into(), - (builtin.base() as isize, 1).into(), - (builtin.base() as isize, 2).into(), - (builtin.base() as isize, 3).into(), - ]), - ); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_missing_segment_used_sizes() { @@ -429,7 +442,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_add_validation_rule() { - let builtin = OutputBuiltinRunner::new(true); + let builtin: BuiltinRunner = OutputBuiltinRunner::new(true).into(); let mut vm = vm!(); vm.segments = segments![ @@ -460,7 +473,131 @@ mod tests { fn get_air_private_input() { let builtin: BuiltinRunner = OutputBuiltinRunner::new(true).into(); - let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 2), ((0, 3), 3)]; - assert!(builtin.air_private_input(&memory).is_empty()); + let segments = segments![((0, 0), 0), ((0, 1), 1), ((0, 2), 2), ((0, 3), 3)]; + assert!(builtin.air_private_input(&segments).is_empty()); + } + + #[test] + fn set_state() { + let mut builtin = OutputBuiltinRunner::new(true); + assert_eq!(builtin.base, 0); + + let new_state = OutputBuiltinState { + base: 10, + pages: HashMap::from([(1, PublicMemoryPage { start: 0, size: 3 })]), + attributes: HashMap::from([("gps_fact_topology".to_string(), vec![0, 2, 0])]), + }; + builtin.set_state(new_state.clone()); + + assert_eq!(builtin.base, new_state.base); + assert_eq!(builtin.pages, new_state.pages); + assert_eq!(builtin.attributes, new_state.attributes); + + let state = builtin.get_state(); + assert_eq!(state, new_state); + } + + #[test] + fn new_state() { + let mut builtin = OutputBuiltinRunner { + base: 10, + pages: HashMap::from([(1, PublicMemoryPage { start: 0, size: 3 })]), + attributes: HashMap::from([("gps_fact_topology".to_string(), vec![0, 2, 0])]), + stop_ptr: Some(10), + included: true, + }; + + let new_base = 11; + let new_included = false; + builtin.new_state(new_base, new_included); + + assert_eq!(builtin.base, new_base); + assert!(builtin.pages.is_empty()); + assert!(builtin.attributes.is_empty()); + assert_eq!(builtin.stop_ptr, None); + assert_eq!(builtin.included, new_included); + } + + #[test] + fn add_page() { + let mut builtin = OutputBuiltinRunner::new(true); + assert_eq!( + builtin.add_page( + 1, + Relocatable { + segment_index: builtin.base() as isize, + offset: 0 + }, + 3 + ), + Ok(()) + ); + + assert_eq!( + builtin.pages, + HashMap::from([(1, PublicMemoryPage { start: 0, size: 3 }),]) + ) + } + + #[test] + fn add_page_wrong_segment() { + let mut builtin = OutputBuiltinRunner::new(true); + let page_start = Relocatable { + segment_index: 18, + offset: 0, + }; + + let result = builtin.add_page(1, page_start, 3); + assert!( + matches!(result, Err(RunnerError::PageNotOnSegment(relocatable, base)) if relocatable == page_start && base == builtin.base()) + ) + } + + #[test] + pub fn add_attribute() { + let mut builtin = OutputBuiltinRunner::new(true); + assert!(builtin.attributes.is_empty()); + + let name = "gps_fact_topology".to_string(); + let values = vec![0, 12, 30]; + builtin.add_attribute(name.clone(), values.clone()); + + assert_eq!(builtin.attributes, HashMap::from([(name, values)])); + } + + #[test] + fn get_public_memory() { + let mut builtin = OutputBuiltinRunner::new(true); + + builtin + .add_page( + 1, + Relocatable { + segment_index: builtin.base() as isize, + offset: 2, + }, + 2, + ) + .unwrap(); + + builtin + .add_page( + 2, + Relocatable { + segment_index: builtin.base() as isize, + offset: 4, + }, + 3, + ) + .unwrap(); + + let mut segments = MemorySegmentManager::new(); + segments.segment_used_sizes = Some(vec![7]); + + let public_memory = builtin.get_public_memory(&segments).unwrap(); + assert_eq!( + public_memory, + vec![(0, 0), (1, 0), (2, 1), (3, 1), (4, 2), (5, 2), (6, 2)] + ); } } diff --git a/vm/src/vm/runners/builtin_runner/poseidon.rs b/vm/src/vm/runners/builtin_runner/poseidon.rs index 3bc28ab228..51b12cc73b 100644 --- a/vm/src/vm/runners/builtin_runner/poseidon.rs +++ b/vm/src/vm/runners/builtin_runner/poseidon.rs @@ -19,12 +19,9 @@ use super::POSEIDON_BUILTIN_NAME; pub struct PoseidonBuiltinRunner { pub base: usize, ratio: Option, - pub(crate) cells_per_instance: u32, - pub(crate) n_input_cells: u32, pub(crate) stop_ptr: Option, pub(crate) included: bool, cache: RefCell>, - pub(crate) instances_per_component: u32, } impl PoseidonBuiltinRunner { @@ -32,12 +29,9 @@ impl PoseidonBuiltinRunner { PoseidonBuiltinRunner { base: 0, ratio, - cells_per_instance: CELLS_PER_POSEIDON, - n_input_cells: INPUT_CELLS_PER_POSEIDON, stop_ptr: None, included, cache: RefCell::new(HashMap::new()), - instances_per_component: 1, } } @@ -68,19 +62,19 @@ impl PoseidonBuiltinRunner { address: Relocatable, memory: &Memory, ) -> Result, RunnerError> { - let index = address.offset % self.cells_per_instance as usize; - if index < self.n_input_cells as usize { + let index = address.offset % CELLS_PER_POSEIDON as usize; + if index < INPUT_CELLS_PER_POSEIDON as usize { return Ok(None); } if let Some(felt) = self.cache.borrow().get(&address) { return Ok(Some(felt.into())); } let first_input_addr = (address - index)?; - let first_output_addr = (first_input_addr + self.n_input_cells as usize)?; + let first_output_addr = (first_input_addr + INPUT_CELLS_PER_POSEIDON as usize)?; let mut input_felts = vec![]; - for i in 0..self.n_input_cells as usize { + for i in 0..INPUT_CELLS_PER_POSEIDON as usize { let m_index = (first_input_addr + i)?; let val = match memory.get(&m_index) { Some(value) => { @@ -110,10 +104,6 @@ impl PoseidonBuiltinRunner { Ok(self.cache.borrow().get(&address).map(|x| x.into())) } - pub fn get_memory_segment_addresses(&self) -> (usize, Option) { - (self.base, self.stop_ptr) - } - pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result { segments .get_segment_used_size(self.base()) @@ -125,44 +115,7 @@ impl PoseidonBuiltinRunner { segments: &MemorySegmentManager, ) -> Result { let used_cells = self.get_used_cells(segments)?; - Ok(div_ceil(used_cells, self.cells_per_instance as usize)) - } - - pub fn final_stack( - &mut self, - segments: &MemorySegmentManager, - pointer: Relocatable, - ) -> Result { - if self.included { - let stop_pointer_addr = (pointer - 1) - .map_err(|_| RunnerError::NoStopPointer(Box::new(POSEIDON_BUILTIN_NAME)))?; - let stop_pointer = segments - .memory - .get_relocatable(stop_pointer_addr) - .map_err(|_| RunnerError::NoStopPointer(Box::new(POSEIDON_BUILTIN_NAME)))?; - if self.base as isize != stop_pointer.segment_index { - return Err(RunnerError::InvalidStopPointerIndex(Box::new(( - POSEIDON_BUILTIN_NAME, - stop_pointer, - self.base, - )))); - } - let stop_ptr = stop_pointer.offset; - let num_instances = self.get_used_instances(segments)?; - let used = num_instances * self.cells_per_instance as usize; - if stop_ptr != used { - return Err(RunnerError::InvalidStopPointer(Box::new(( - POSEIDON_BUILTIN_NAME, - Relocatable::from((self.base as isize, used)), - Relocatable::from((self.base as isize, stop_ptr)), - )))); - } - self.stop_ptr = Some(stop_ptr); - Ok(stop_pointer_addr) - } else { - self.stop_ptr = Some(0); - Ok(pointer) - } + Ok(div_ceil(used_cells, CELLS_PER_POSEIDON as usize)) } pub fn air_private_input(&self, memory: &Memory) -> Vec { @@ -231,7 +184,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack() { - let mut builtin = PoseidonBuiltinRunner::new(Some(10), true); + let mut builtin: BuiltinRunner = PoseidonBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -255,7 +208,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_stop_pointer() { - let mut builtin = PoseidonBuiltinRunner::new(Some(10), true); + let mut builtin: BuiltinRunner = PoseidonBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -283,7 +236,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_when_not_included() { - let mut builtin = PoseidonBuiltinRunner::new(Some(10), false); + let mut builtin: BuiltinRunner = PoseidonBuiltinRunner::new(Some(10), false).into(); let mut vm = vm!(); @@ -307,7 +260,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_non_relocatable() { - let mut builtin = PoseidonBuiltinRunner::new(Some(10), true); + let mut builtin: BuiltinRunner = PoseidonBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); @@ -443,7 +396,7 @@ mod tests { fn get_air_private_input() { let builtin: BuiltinRunner = PoseidonBuiltinRunner::new(None, true).into(); - let memory = memory![ + let segments = segments![ ((0, 0), 0), ((0, 1), 1), ((0, 2), 2), @@ -458,7 +411,7 @@ mod tests { ((0, 11), 11) ]; assert_eq!( - builtin.air_private_input(&memory), + builtin.air_private_input(&segments), (vec![ PrivateInput::PoseidonState(PrivateInputPoseidonState { index: 0, diff --git a/vm/src/vm/runners/builtin_runner/range_check.rs b/vm/src/vm/runners/builtin_runner/range_check.rs index 68809b8752..b9b49992ce 100644 --- a/vm/src/vm/runners/builtin_runner/range_check.rs +++ b/vm/src/vm/runners/builtin_runner/range_check.rs @@ -8,12 +8,9 @@ use crate::{ use crate::Felt252; use crate::{ - types::{ - instance_definitions::range_check_instance_def::CELLS_PER_RANGE_CHECK, - relocatable::{MaybeRelocatable, Relocatable}, - }, + types::relocatable::{MaybeRelocatable, Relocatable}, vm::{ - errors::{memory_errors::MemoryError, runner_errors::RunnerError}, + errors::memory_errors::MemoryError, vm_memory::{ memory::{Memory, ValidationRule}, memory_segments::MemorySegmentManager, @@ -21,50 +18,37 @@ use crate::{ }, }; -use num_traits::Zero; +use lazy_static::lazy_static; -use super::RANGE_CHECK_BUILTIN_NAME; +use super::{RANGE_CHECK_96_BUILTIN_NAME, RANGE_CHECK_BUILTIN_NAME}; -// NOTE: the current implementation is based on the bound 0x10000 -const _INNER_RC_BOUND: u64 = 1u64 << INNER_RC_BOUND_SHIFT; const INNER_RC_BOUND_SHIFT: u64 = 16; const INNER_RC_BOUND_MASK: u64 = u16::MAX as u64; -// TODO: use constant instead of receiving as false parameter -const N_PARTS: u64 = 8; +pub const RC_N_PARTS_STANDARD: u64 = 8; +pub const RC_N_PARTS_96: u64 = 6; + +lazy_static! { + pub static ref BOUND_STANDARD: Felt252 = + Felt252::TWO.pow(INNER_RC_BOUND_SHIFT * RC_N_PARTS_STANDARD); + pub static ref BOUND_96: Felt252 = Felt252::TWO.pow(INNER_RC_BOUND_SHIFT * RC_N_PARTS_96); +} #[derive(Debug, Clone)] -pub struct RangeCheckBuiltinRunner { +pub struct RangeCheckBuiltinRunner { ratio: Option, base: usize, pub(crate) stop_ptr: Option, - pub(crate) cells_per_instance: u32, - pub(crate) n_input_cells: u32, - pub _bound: Option, pub(crate) included: bool, - pub(crate) n_parts: u32, - pub(crate) instances_per_component: u32, } -impl RangeCheckBuiltinRunner { - pub fn new(ratio: Option, n_parts: u32, included: bool) -> RangeCheckBuiltinRunner { - let bound = Felt252::TWO.pow(16 * n_parts as u128); - let _bound = if n_parts != 0 && bound.is_zero() { - None - } else { - Some(bound) - }; - +impl RangeCheckBuiltinRunner { + pub fn new(ratio: Option, included: bool) -> RangeCheckBuiltinRunner { RangeCheckBuiltinRunner { ratio, base: 0, stop_ptr: None, - cells_per_instance: CELLS_PER_RANGE_CHECK, - n_input_cells: CELLS_PER_RANGE_CHECK, - _bound, included, - n_parts, - instances_per_component: 1, } } @@ -88,8 +72,26 @@ impl RangeCheckBuiltinRunner { self.ratio } + pub fn name(&self) -> &'static str { + match N_PARTS { + RC_N_PARTS_96 => RANGE_CHECK_96_BUILTIN_NAME, + _ => RANGE_CHECK_BUILTIN_NAME, + } + } + + pub fn n_parts(&self) -> u64 { + N_PARTS + } + + pub fn bound(&self) -> &'static Felt252 { + match N_PARTS { + RC_N_PARTS_96 => &BOUND_96, + _ => &BOUND_STANDARD, + } + } + pub fn add_validation_rule(&self, memory: &mut Memory) { - let rule: ValidationRule = ValidationRule(Box::new( + let rule = ValidationRule(Box::new( |memory: &Memory, address: Relocatable| -> Result, MemoryError> { let num = memory .get_integer(address) @@ -107,18 +109,6 @@ impl RangeCheckBuiltinRunner { memory.add_validation_rule(self.base, rule); } - pub fn deduce_memory_cell( - &self, - _address: Relocatable, - _memory: &Memory, - ) -> Result, RunnerError> { - Ok(None) - } - - pub fn get_memory_segment_addresses(&self) -> (usize, Option) { - (self.base, self.stop_ptr) - } - pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result { segments .get_segment_used_size(self.base) @@ -144,7 +134,7 @@ impl RangeCheckBuiltinRunner { .rev() .map(move |i| ((digit >> (i * INNER_RC_BOUND_SHIFT)) & INNER_RC_BOUND_MASK)) }) - .take(self.n_parts as usize) + .take(N_PARTS as usize) .fold(rc_bounds, |mm, x| { (min(mm.0, x as usize), max(mm.1, x as usize)) }); @@ -159,43 +149,6 @@ impl RangeCheckBuiltinRunner { self.get_used_cells(segments) } - pub fn final_stack( - &mut self, - segments: &MemorySegmentManager, - pointer: Relocatable, - ) -> Result { - if self.included { - let stop_pointer_addr = (pointer - 1) - .map_err(|_| RunnerError::NoStopPointer(Box::new(RANGE_CHECK_BUILTIN_NAME)))?; - let stop_pointer = segments - .memory - .get_relocatable(stop_pointer_addr) - .map_err(|_| RunnerError::NoStopPointer(Box::new(RANGE_CHECK_BUILTIN_NAME)))?; - if self.base as isize != stop_pointer.segment_index { - return Err(RunnerError::InvalidStopPointerIndex(Box::new(( - RANGE_CHECK_BUILTIN_NAME, - stop_pointer, - self.base, - )))); - } - let stop_ptr = stop_pointer.offset; - let num_instances = self.get_used_instances(segments)?; - let used = num_instances * self.cells_per_instance as usize; - if stop_ptr != used { - return Err(RunnerError::InvalidStopPointer(Box::new(( - RANGE_CHECK_BUILTIN_NAME, - Relocatable::from((self.base as isize, used)), - Relocatable::from((self.base as isize, stop_ptr)), - )))); - } - self.stop_ptr = Some(stop_ptr); - Ok(stop_pointer_addr) - } else { - self.stop_ptr = Some(0); - Ok(pointer) - } - } - pub fn air_private_input(&self, memory: &Memory) -> Vec { let mut private_inputs = vec![]; if let Some(segment) = memory.data.get(self.base) { @@ -214,7 +167,8 @@ mod tests { use super::*; use crate::relocatable; use crate::serde::deserialize_program::BuiltinName; - use crate::stdlib::collections::HashMap; + use crate::vm::errors::runner_errors::RunnerError; + use crate::vm::runners::builtin_runner::RANGE_CHECK_BUILTIN_NAME; use crate::vm::vm_memory::memory::Memory; use crate::{ hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor, @@ -232,7 +186,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_instances() { - let builtin = RangeCheckBuiltinRunner::new(Some(10), 12, true); + let builtin = RangeCheckBuiltinRunner::::new(Some(10), true); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![1]); @@ -243,7 +197,8 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack() { - let mut builtin = RangeCheckBuiltinRunner::new(Some(10), 12, true); + let mut builtin: BuiltinRunner = + RangeCheckBuiltinRunner::::new(Some(10), true).into(); let mut vm = vm!(); @@ -267,7 +222,8 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_stop_pointer() { - let mut builtin = RangeCheckBuiltinRunner::new(Some(10), 12, true); + let mut builtin: BuiltinRunner = + RangeCheckBuiltinRunner::::new(Some(10), true).into(); let mut vm = vm!(); @@ -295,7 +251,8 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_when_notincluded() { - let mut builtin = RangeCheckBuiltinRunner::new(Some(10), 12, false); + let mut builtin: BuiltinRunner = + RangeCheckBuiltinRunner::::new(Some(10), false).into(); let mut vm = vm!(); @@ -319,7 +276,8 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_non_relocatable() { - let mut builtin = RangeCheckBuiltinRunner::new(Some(10), 12, true); + let mut builtin: BuiltinRunner = + RangeCheckBuiltinRunner::::new(Some(10), true).into(); let mut vm = vm!(); @@ -345,7 +303,8 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_and_allocated_size_test() { - let builtin: BuiltinRunner = RangeCheckBuiltinRunner::new(Some(10), 12, true).into(); + let builtin: BuiltinRunner = + RangeCheckBuiltinRunner::::new(Some(10), true).into(); let mut vm = vm!(); @@ -391,7 +350,8 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_units() { - let builtin: BuiltinRunner = RangeCheckBuiltinRunner::new(Some(10), 12, true).into(); + let builtin: BuiltinRunner = + RangeCheckBuiltinRunner::::new(Some(10), true).into(); let mut vm = vm!(); @@ -435,7 +395,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn initialize_segments_for_range_check() { - let mut builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let mut builtin = RangeCheckBuiltinRunner::::new(Some(8), true); let mut segments = MemorySegmentManager::new(); builtin.initialize_segments(&mut segments); assert_eq!(builtin.base, 0); @@ -444,7 +404,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_initial_stack_for_range_check_with_base() { - let mut builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let mut builtin = RangeCheckBuiltinRunner::::new(Some(8), true); builtin.base = 1; let initial_stack = builtin.initial_stack(); assert_eq!( @@ -454,72 +414,26 @@ mod tests { assert_eq!(initial_stack.len(), 1); } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_segment_addresses() { - let builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); - - assert_eq!(builtin.get_memory_segment_addresses(), (0, None),); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_missing_segment_used_sizes() { - let builtin = BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(256), 8, true)); - let vm = vm!(); - - assert_eq!( - builtin.get_memory_accesses(&vm), - Err(MemoryError::MissingSegmentUsedSizes), - ); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_empty() { - let builtin = BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(256), 8, true)); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![0]); - assert_eq!(builtin.get_memory_accesses(&vm), Ok(vec![])); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses() { - let builtin = BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(256), 8, true)); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![4]); - assert_eq!( - builtin.get_memory_accesses(&vm), - Ok(vec![ - (builtin.base() as isize, 0).into(), - (builtin.base() as isize, 1).into(), - (builtin.base() as isize, 2).into(), - (builtin.base() as isize, 3).into(), - ]), - ); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_base() { - let builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let builtin = RangeCheckBuiltinRunner::::new(Some(8), true); assert_eq!(builtin.base(), 0); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_ratio() { - let builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let builtin = RangeCheckBuiltinRunner::::new(Some(8), true); assert_eq!(builtin.ratio(), Some(8)); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_missing_segment_used_sizes() { - let builtin = BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(256), 8, true)); + let builtin = BuiltinRunner::RangeCheck( + RangeCheckBuiltinRunner::::new(Some(256), true), + ); let vm = vm!(); assert_eq!( @@ -531,7 +445,9 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_empty() { - let builtin = BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(256), 8, true)); + let builtin = BuiltinRunner::RangeCheck( + RangeCheckBuiltinRunner::::new(Some(256), true), + ); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![0]); @@ -541,7 +457,9 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells() { - let builtin = BuiltinRunner::RangeCheck(RangeCheckBuiltinRunner::new(Some(256), 8, true)); + let builtin = BuiltinRunner::RangeCheck( + RangeCheckBuiltinRunner::::new(Some(256), true), + ); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![4]); @@ -551,7 +469,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_range_check_usage_succesful_a() { - let builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let builtin = RangeCheckBuiltinRunner::::new(Some(8), true); let memory = memory![((0, 0), 1), ((0, 1), 2), ((0, 2), 3), ((0, 3), 4)]; assert_eq!(builtin.get_range_check_usage(&memory), Some((0, 4))); } @@ -559,7 +477,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_range_check_usage_succesful_b() { - let builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let builtin = RangeCheckBuiltinRunner::::new(Some(8), true); let memory = memory![ ((0, 0), 1465218365), ((0, 1), 2134570341), @@ -572,7 +490,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_range_check_usage_succesful_c() { - let builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let builtin = RangeCheckBuiltinRunner::::new(Some(8), true); let memory = memory![ ((0, 0), 634834751465218365_i64), ((0, 1), 42876922134570341_i64), @@ -587,7 +505,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_range_check_empty_memory() { - let builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let builtin = RangeCheckBuiltinRunner::::new(Some(8), true); let memory = Memory::new(); assert_eq!(builtin.get_range_check_usage(&memory), None); } @@ -596,7 +514,8 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_perm_range_check_units() { - let builtin_runner: BuiltinRunner = RangeCheckBuiltinRunner::new(Some(8), 8, true).into(); + let builtin_runner: BuiltinRunner = + RangeCheckBuiltinRunner::::new(Some(8), true).into(); let mut vm = vm!(); vm.current_step = 8; @@ -607,11 +526,12 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_air_private_input() { - let builtin: BuiltinRunner = RangeCheckBuiltinRunner::new(None, 4, true).into(); + let builtin: BuiltinRunner = + RangeCheckBuiltinRunner::::new(None, true).into(); - let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 2)]; + let segments = segments![((0, 0), 0), ((0, 1), 1), ((0, 2), 2)]; assert_eq!( - builtin.air_private_input(&memory), + builtin.air_private_input(&segments), (vec![ PrivateInput::Value(PrivateInputValue { index: 0, diff --git a/vm/src/vm/runners/builtin_runner/segment_arena.rs b/vm/src/vm/runners/builtin_runner/segment_arena.rs index 82c9e72aea..5cc61fb706 100644 --- a/vm/src/vm/runners/builtin_runner/segment_arena.rs +++ b/vm/src/vm/runners/builtin_runner/segment_arena.rs @@ -1,7 +1,4 @@ -use crate::stdlib::boxed::Box; use crate::vm::errors::memory_errors::MemoryError; -use crate::vm::errors::runner_errors::RunnerError; -use crate::vm::vm_memory::memory::Memory; use crate::{ types::relocatable::{MaybeRelocatable, Relocatable}, vm::vm_memory::memory_segments::MemorySegmentManager, @@ -9,19 +6,16 @@ use crate::{ #[cfg(not(feature = "std"))] use alloc::vec::Vec; +use num_integer::div_ceil; -use super::SEGMENT_ARENA_BUILTIN_NAME; - -const ARENA_BUILTIN_SIZE: u32 = 3; +pub(crate) const ARENA_BUILTIN_SIZE: u32 = 3; // The size of the builtin segment at the time of its creation. const INITIAL_SEGMENT_SIZE: usize = ARENA_BUILTIN_SIZE as usize; #[derive(Debug, Clone)] pub struct SegmentArenaBuiltinRunner { base: Relocatable, - included: bool, - pub(crate) cells_per_instance: u32, - pub(crate) n_input_cells_per_instance: u32, + pub(crate) included: bool, pub(crate) stop_ptr: Option, } @@ -30,8 +24,6 @@ impl SegmentArenaBuiltinRunner { SegmentArenaBuiltinRunner { base: Relocatable::from((0, 0)), included, - cells_per_instance: ARENA_BUILTIN_SIZE, - n_input_cells_per_instance: ARENA_BUILTIN_SIZE, stop_ptr: None, } } @@ -65,60 +57,14 @@ impl SegmentArenaBuiltinRunner { } } - pub fn final_stack( - &mut self, - segments: &MemorySegmentManager, - pointer: Relocatable, - ) -> Result { - if self.included { - let stop_pointer_addr = (pointer - 1) - .map_err(|_| RunnerError::NoStopPointer(Box::new(SEGMENT_ARENA_BUILTIN_NAME)))?; - let stop_pointer = segments - .memory - .get_relocatable(stop_pointer_addr) - .map_err(|_| RunnerError::NoStopPointer(Box::new(SEGMENT_ARENA_BUILTIN_NAME)))?; - if self.base.segment_index != stop_pointer.segment_index { - return Err(RunnerError::InvalidStopPointerIndex(Box::new(( - SEGMENT_ARENA_BUILTIN_NAME, - stop_pointer, - self.base.segment_index as usize, - )))); - } - let used = self.get_used_cells(segments).map_err(RunnerError::Memory)?; - if stop_pointer != (self.base + used)? { - return Err(RunnerError::InvalidStopPointer(Box::new(( - SEGMENT_ARENA_BUILTIN_NAME, - (self.base + used)?, - stop_pointer, - )))); - } - self.stop_ptr = Some(stop_pointer.offset); - Ok(stop_pointer_addr) - } else { - self.stop_ptr = Some(self.base.offset); - Ok(pointer) - } - } - pub fn get_used_instances( &self, segments: &MemorySegmentManager, ) -> Result { - self.get_used_cells(segments) - } - - pub fn get_memory_segment_addresses(&self) -> (usize, Option) { - (self.base.segment_index as usize, self.stop_ptr) - } - - pub fn add_validation_rule(&self, _memory: &mut Memory) {} - - pub fn deduce_memory_cell( - &self, - _address: Relocatable, - _memory: &Memory, - ) -> Result, RunnerError> { - Ok(None) + Ok(div_ceil( + self.get_used_cells(segments)?, + ARENA_BUILTIN_SIZE as usize, + )) } pub fn base(&self) -> usize { @@ -142,9 +88,12 @@ fn gen_arg(segments: &mut MemorySegmentManager, data: &[MaybeRelocatable; 3]) -> #[cfg(test)] mod tests { use super::*; - use crate::stdlib::collections::HashMap; + use crate::vm::errors::runner_errors::RunnerError; + use crate::vm::runners::builtin_runner::SEGMENT_ARENA_BUILTIN_NAME; use crate::vm::vm_core::VirtualMachine; use crate::{relocatable, utils::test_utils::*, vm::runners::builtin_runner::BuiltinRunner}; + #[cfg(not(feature = "std"))] + use alloc::boxed::Box; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -187,7 +136,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_stop_pointer() { - let mut builtin = SegmentArenaBuiltinRunner::new(true); + let mut builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into(); let mut vm = vm!(); @@ -214,7 +163,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_valid() { - let mut builtin = SegmentArenaBuiltinRunner::new(false); + let mut builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(false).into(); let mut vm = vm!(); @@ -262,7 +211,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_non_relocatable() { - let mut builtin = SegmentArenaBuiltinRunner::new(true); + let mut builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into(); let mut vm = vm!(); @@ -322,43 +271,6 @@ mod tests { assert_eq!(initial_stack.len(), 1); } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_segment_addresses() { - let builtin = SegmentArenaBuiltinRunner::new(true); - - assert_eq!(builtin.get_memory_segment_addresses(), (0, None),); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_missing_segment_used_sizes() { - let builtin = BuiltinRunner::SegmentArena(SegmentArenaBuiltinRunner::new(true)); - let vm = vm!(); - - assert_eq!(builtin.get_memory_accesses(&vm), Ok(vec![]),); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_empty() { - let builtin = BuiltinRunner::SegmentArena(SegmentArenaBuiltinRunner::new(true)); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![0]); - assert_eq!(builtin.get_memory_accesses(&vm), Ok(vec![])); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses() { - let builtin = BuiltinRunner::SegmentArena(SegmentArenaBuiltinRunner::new(true)); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![4]); - assert_eq!(builtin.get_memory_accesses(&vm), Ok(vec![]),); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_missing_segment_used_sizes() { @@ -412,8 +324,8 @@ mod tests { let builtin = BuiltinRunner::SegmentArena(SegmentArenaBuiltinRunner::new(true)); let mut memory_segment_manager = MemorySegmentManager::new(); memory_segment_manager.segment_used_sizes = Some(vec![6]); - - assert_eq!(builtin.get_used_instances(&memory_segment_manager), Ok(3)); + // (SIZE(6) - INITIAL_SIZE(3)) / CELLS_PER_INSTANCE(3) + assert_eq!(builtin.get_used_instances(&memory_segment_manager), Ok(1)); } #[test] @@ -437,14 +349,6 @@ mod tests { ); } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn test_add_validation_rule() { - let builtin = SegmentArenaBuiltinRunner::new(true); - let mut vm = vm!(); - builtin.add_validation_rule(&mut vm.segments.memory); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn initial_stackincluded_test() { @@ -502,7 +406,7 @@ mod tests { fn get_air_private_input() { let builtin: BuiltinRunner = SegmentArenaBuiltinRunner::new(true).into(); - let memory = memory![((0, 0), 0), ((0, 1), 1), ((0, 2), 2), ((0, 3), 3)]; - assert!(builtin.air_private_input(&memory).is_empty()); + let segments = segments![((0, 0), 0), ((0, 1), 1), ((0, 2), 2), ((0, 3), 3)]; + assert!(builtin.air_private_input(&segments).is_empty()); } } diff --git a/vm/src/vm/runners/builtin_runner/signature.rs b/vm/src/vm/runners/builtin_runner/signature.rs index d854afa05a..9520ebcc63 100644 --- a/vm/src/vm/runners/builtin_runner/signature.rs +++ b/vm/src/vm/runners/builtin_runner/signature.rs @@ -7,12 +7,9 @@ use crate::types::instance_definitions::ecdsa_instance_def::CELLS_PER_SIGNATURE; use crate::vm::runners::cairo_pie::BuiltinAdditionalData; use crate::Felt252; use crate::{ - types::{ - instance_definitions::ecdsa_instance_def::EcdsaInstanceDef, - relocatable::{MaybeRelocatable, Relocatable}, - }, + types::relocatable::{MaybeRelocatable, Relocatable}, vm::{ - errors::{memory_errors::MemoryError, runner_errors::RunnerError}, + errors::memory_errors::MemoryError, vm_memory::{ memory::{Memory, ValidationRule}, memory_segments::MemorySegmentManager, @@ -33,32 +30,22 @@ lazy_static! { .unwrap(); } -use super::SIGNATURE_BUILTIN_NAME; - #[derive(Debug, Clone)] pub struct SignatureBuiltinRunner { pub(crate) included: bool, ratio: Option, base: usize, - pub(crate) cells_per_instance: u32, - pub(crate) n_input_cells: u32, - _total_n_bits: u32, pub(crate) stop_ptr: Option, - pub(crate) instances_per_component: u32, signatures: Rc>>, } impl SignatureBuiltinRunner { - pub(crate) fn new(instance_def: &EcdsaInstanceDef, included: bool) -> Self { + pub(crate) fn new(ratio: Option, included: bool) -> Self { SignatureBuiltinRunner { base: 0, included, - ratio: instance_def.ratio, - cells_per_instance: 2, - n_input_cells: 2, - _total_n_bits: 251, + ratio, stop_ptr: None, - instances_per_component: 1, signatures: Rc::new(RefCell::new(HashMap::new())), } } @@ -106,7 +93,7 @@ impl SignatureBuiltinRunner { self.base } pub fn add_validation_rule(&self, memory: &mut Memory) { - let cells_per_instance = self.cells_per_instance; + let cells_per_instance = CELLS_PER_SIGNATURE; let signatures = Rc::clone(&self.signatures); let rule: ValidationRule = ValidationRule(Box::new( move |memory: &Memory, addr: Relocatable| -> Result, MemoryError> { @@ -156,22 +143,10 @@ impl SignatureBuiltinRunner { memory.add_validation_rule(self.base, rule); } - pub fn deduce_memory_cell( - &self, - _address: Relocatable, - _memory: &Memory, - ) -> Result, RunnerError> { - Ok(None) - } - pub fn ratio(&self) -> Option { self.ratio } - pub fn get_memory_segment_addresses(&self) -> (usize, Option) { - (self.base, self.stop_ptr) - } - pub fn get_used_cells(&self, segments: &MemorySegmentManager) -> Result { segments .get_segment_used_size(self.base) @@ -183,44 +158,7 @@ impl SignatureBuiltinRunner { segments: &MemorySegmentManager, ) -> Result { let used_cells = self.get_used_cells(segments)?; - Ok(div_ceil(used_cells, self.cells_per_instance as usize)) - } - - pub fn final_stack( - &mut self, - segments: &MemorySegmentManager, - pointer: Relocatable, - ) -> Result { - if self.included { - let stop_pointer_addr = (pointer - 1) - .map_err(|_| RunnerError::NoStopPointer(Box::new(SIGNATURE_BUILTIN_NAME)))?; - let stop_pointer = segments - .memory - .get_relocatable(stop_pointer_addr) - .map_err(|_| RunnerError::NoStopPointer(Box::new(SIGNATURE_BUILTIN_NAME)))?; - if self.base as isize != stop_pointer.segment_index { - return Err(RunnerError::InvalidStopPointerIndex(Box::new(( - SIGNATURE_BUILTIN_NAME, - stop_pointer, - self.base, - )))); - } - let stop_ptr = stop_pointer.offset; - let num_instances = self.get_used_instances(segments)?; - let used = num_instances * self.cells_per_instance as usize; - if stop_ptr != used { - return Err(RunnerError::InvalidStopPointer(Box::new(( - SIGNATURE_BUILTIN_NAME, - Relocatable::from((self.base as isize, used)), - Relocatable::from((self.base as isize, stop_ptr)), - )))); - } - self.stop_ptr = Some(stop_ptr); - Ok(stop_pointer_addr) - } else { - self.stop_ptr = Some(0); - Ok(pointer) - } + Ok(div_ceil(used_cells, CELLS_PER_SIGNATURE as usize)) } pub fn get_additional_data(&self) -> BuiltinAdditionalData { @@ -278,11 +216,13 @@ mod tests { use super::*; use crate::{ relocatable, - types::instance_definitions::ecdsa_instance_def::EcdsaInstanceDef, utils::test_utils::*, vm::{ - errors::memory_errors::{InsufficientAllocatedCellsError, MemoryError}, - runners::builtin_runner::BuiltinRunner, + errors::{ + memory_errors::{InsufficientAllocatedCellsError, MemoryError}, + runner_errors::RunnerError, + }, + runners::builtin_runner::{BuiltinRunner, SIGNATURE_BUILTIN_NAME}, vm_core::VirtualMachine, vm_memory::{memory::Memory, memory_segments::MemorySegmentManager}, }, @@ -294,8 +234,7 @@ mod tests { #[test] fn get_used_cells_and_allocated_size_valid() { - let builtin: BuiltinRunner = - SignatureBuiltinRunner::new(&EcdsaInstanceDef::new(Some(10)), true).into(); + let builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(10), true).into(); let mut vm = vm!(); vm.current_step = 110; vm.segments.segment_used_sizes = Some(vec![1]); @@ -305,7 +244,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn initialize_segments_for_ecdsa() { - let mut builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let mut builtin = SignatureBuiltinRunner::new(Some(512), true); let mut segments = MemorySegmentManager::new(); builtin.initialize_segments(&mut segments); assert_eq!(builtin.base, 0); @@ -314,8 +253,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_instances() { - let builtin: BuiltinRunner = - SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true).into(); + let builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into(); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![1]); @@ -326,7 +264,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack() { - let mut builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let mut builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into(); let mut vm = vm!(); @@ -350,7 +288,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_stop_pointer() { - let mut builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let mut builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into(); let mut vm = vm!(); @@ -378,7 +316,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_error_non_relocatable() { - let mut builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let mut builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into(); let mut vm = vm!(); @@ -399,70 +337,10 @@ mod tests { ); } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_segment_addresses() { - let builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); - - assert_eq!(builtin.get_memory_segment_addresses(), (0, None)); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_missing_segment_used_sizes() { - let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new( - &EcdsaInstanceDef::default(), - true, - )); - let vm = vm!(); - - assert_eq!( - builtin.get_memory_accesses(&vm), - Err(MemoryError::MissingSegmentUsedSizes), - ); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses_empty() { - let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new( - &EcdsaInstanceDef::default(), - true, - )); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![0]); - assert_eq!(builtin.get_memory_accesses(&vm), Ok(vec![])); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn get_memory_accesses() { - let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new( - &EcdsaInstanceDef::default(), - true, - )); - let mut vm = vm!(); - - vm.segments.segment_used_sizes = Some(vec![4]); - assert_eq!( - builtin.get_memory_accesses(&vm), - Ok(vec![ - (builtin.base() as isize, 0).into(), - (builtin.base() as isize, 1).into(), - (builtin.base() as isize, 2).into(), - (builtin.base() as isize, 3).into(), - ]), - ); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_missing_segment_used_sizes() { - let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new( - &EcdsaInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new(Some(512), true)); let vm = vm!(); assert_eq!( @@ -474,10 +352,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_empty() { - let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new( - &EcdsaInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new(Some(512), true)); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![0]); @@ -487,10 +362,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells() { - let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new( - &EcdsaInstanceDef::default(), - true, - )); + let builtin = BuiltinRunner::Signature(SignatureBuiltinRunner::new(Some(512), true)); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![4]); @@ -500,7 +372,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_initial_stack_for_range_check_with_base() { - let mut builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let mut builtin = SignatureBuiltinRunner::new(Some(512), true); builtin.base = 1; let initial_stack = builtin.initial_stack(); assert_eq!( @@ -513,7 +385,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn initial_stack_not_included_test() { - let ecdsa_builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), false); + let ecdsa_builtin = SignatureBuiltinRunner::new(Some(512), false); assert_eq!(ecdsa_builtin.initial_stack(), Vec::new()) } @@ -521,7 +393,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn deduce_memory_cell_test() { let memory = Memory::new(); - let builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into(); let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory); assert_eq!(result, Ok(None)); } @@ -529,39 +401,21 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_ratio() { - let builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let builtin = SignatureBuiltinRunner::new(Some(512), true); assert_eq!(builtin.ratio(), Some(512)); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_base() { - let builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let builtin = SignatureBuiltinRunner::new(Some(512), true); assert_eq!(builtin.base(), 0); } - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn test_get_memory_segment_addresses() { - let builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); - - assert_eq!(builtin.get_memory_segment_addresses(), (0, None)); - } - - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn deduce_memory_cell() { - let memory = Memory::new(); - let builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); - let result = builtin.deduce_memory_cell(Relocatable::from((0, 5)), &memory); - assert_eq!(result, Ok(None)); - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_allocated_memory_min_step_not_reached() { - let builtin: BuiltinRunner = - SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true).into(); + let builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into(); let mut vm = vm!(); vm.current_step = 500; assert_eq!( @@ -578,8 +432,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_used_cells_and_allocated_size_insufficient_allocated() { - let builtin: BuiltinRunner = - SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true).into(); + let builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into(); let mut vm = vm!(); vm.segments.segment_used_sizes = Some(vec![50]); vm.current_step = 512; @@ -598,7 +451,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_invalid_stop_pointer() { - let mut builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let mut builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into(); let mut vm = vm!(); vm.segments = segments![((0, 0), (1, 0))]; assert_eq!( @@ -614,7 +467,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn final_stack_no_used_instances() { - let mut builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let mut builtin: BuiltinRunner = SignatureBuiltinRunner::new(Some(512), true).into(); let mut vm = vm!(); vm.segments = segments![((0, 0), (0, 0))]; assert_eq!( @@ -625,7 +478,7 @@ mod tests { #[test] fn get_additional_info() { - let mut builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let mut builtin = SignatureBuiltinRunner::new(Some(512), true); let signatures = HashMap::from([( Relocatable::from((4, 0)), Signature { diff --git a/vm/src/vm/runners/cairo_pie.rs b/vm/src/vm/runners/cairo_pie.rs index 5e3aba9571..16bcb80d41 100644 --- a/vm/src/vm/runners/cairo_pie.rs +++ b/vm/src/vm/runners/cairo_pie.rs @@ -84,6 +84,7 @@ pub struct CairoPieMetadata { pub execution_segment: SegmentInfo, pub ret_fp_segment: SegmentInfo, pub ret_pc_segment: SegmentInfo, + #[serde(serialize_with = "serde_impl::serialize_builtin_segments")] pub builtin_segments: HashMap, pub extra_segments: Vec, } @@ -113,7 +114,7 @@ impl CairoPie { let file = File::create(file_path)?; let mut zip_writer = ZipWriter::new(file); let options = - zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Stored); + zip::write::FileOptions::default().compression_method(zip::CompressionMethod::Deflated); zip_writer.start_file("version.json", options)?; zip_writer.write_all(serde_json::to_string(&self.version)?.as_bytes())?; zip_writer.start_file("metadata.json", options)?; @@ -132,8 +133,9 @@ impl CairoPie { mod serde_impl { use crate::stdlib::collections::HashMap; use num_traits::Num; + use serde::ser::SerializeMap; - use super::{CairoPieMemory, CAIRO_PIE_VERSION}; + use super::{CairoPieMemory, SegmentInfo, CAIRO_PIE_VERSION}; use crate::stdlib::prelude::{String, Vec}; use crate::{ types::relocatable::{MaybeRelocatable, Relocatable}, @@ -186,7 +188,6 @@ mod serde_impl { seq_serializer.end() } - #[allow(clippy::format_collect)] pub fn serialize_memory( values: &[((usize, usize), MaybeRelocatable)], serializer: S, @@ -224,12 +225,11 @@ mod serde_impl { }; } - serializer.serialize_str( - res.iter() - .map(|b| format!("{:02x}", b)) - .collect::() - .as_str(), - ) + let string = res + .iter() + .fold(String::new(), |string, b| string + &format!("{:02x}", b)); + + serializer.serialize_str(&string) } impl CairoPieMemory { @@ -321,6 +321,33 @@ mod serde_impl { seq_serializer.end() } + + pub fn serialize_builtin_segments( + values: &HashMap, + serializer: S, + ) -> Result + where + S: Serializer, + { + let mut map_serializer = serializer.serialize_map(Some(values.len()))?; + const BUILTIN_ORDERED_LIST: &[&str] = &[ + "output", + "pedersen", + "range_check", + "ecdsa", + "bitwise", + "ec_op", + "keccak", + "poseidon", + ]; + + for name in BUILTIN_ORDERED_LIST { + if let Some(info) = values.get(*name) { + map_serializer.serialize_entry(name, info)? + } + } + map_serializer.end() + } } #[cfg(test)] diff --git a/vm/src/vm/runners/cairo_runner.rs b/vm/src/vm/runners/cairo_runner.rs index cadc1d02ba..c61c39b071 100644 --- a/vm/src/vm/runners/cairo_runner.rs +++ b/vm/src/vm/runners/cairo_runner.rs @@ -7,7 +7,7 @@ use crate::{ ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}, prelude::*, }, - types::instance_definitions::keccak_instance_def::KeccakInstanceDef, + types::layout::MEMORY_UNITS_PER_STEP, vm::{ runners::builtin_runner::SegmentArenaBuiltinRunner, trace::trace_entry::{relocate_trace_register, RelocatedTraceEntry}, @@ -22,10 +22,6 @@ use crate::{ types::{ errors::{math_errors::MathError, program_errors::ProgramError}, exec_scope::ExecutionScopes, - instance_definitions::{ - bitwise_instance_def::BitwiseInstanceDef, ec_op_instance_def::EcOpInstanceDef, - ecdsa_instance_def::EcdsaInstanceDef, - }, layout::CairoLayout, program::Program, relocatable::{relocate_address, relocate_value, MaybeRelocatable, Relocatable}, @@ -54,10 +50,14 @@ use num_integer::div_rem; use num_traits::{ToPrimitive, Zero}; use serde::{Deserialize, Serialize}; +use super::builtin_runner::ModBuiltinRunner; use super::{ - builtin_runner::{KeccakBuiltinRunner, PoseidonBuiltinRunner, OUTPUT_BUILTIN_NAME}, + builtin_runner::{ + KeccakBuiltinRunner, PoseidonBuiltinRunner, RC_N_PARTS_96, RC_N_PARTS_STANDARD, + }, cairo_pie::{self, CairoPie, CairoPieMetadata, CairoPieVersion}, }; +use crate::types::instance_definitions::mod_instance_def::ModInstanceDef; #[derive(Clone, Debug, Eq, PartialEq)] pub enum CairoArg { @@ -179,6 +179,7 @@ impl CairoRunner { "plain" => CairoLayout::plain_instance(), "small" => CairoLayout::small_instance(), "dex" => CairoLayout::dex_instance(), + "recursive" => CairoLayout::recursive_instance(), "starknet" => CairoLayout::starknet_instance(), "starknet_with_keccak" => CairoLayout::starknet_with_keccak_instance(), "recursive_large_output" => CairoLayout::recursive_large_output_instance(), @@ -258,6 +259,9 @@ impl CairoRunner { BuiltinName::ec_op, BuiltinName::keccak, BuiltinName::poseidon, + BuiltinName::range_check96, + BuiltinName::add_mod, + BuiltinName::mul_mod, ]; if !is_subsequence(&self.program.builtins, &builtin_ordered_list) { return Err(RunnerError::DisorderedBuiltins); @@ -283,9 +287,8 @@ impl CairoRunner { let included = program_builtins.remove(&BuiltinName::range_check); if included || self.is_proof_mode() { builtin_runners.push( - RangeCheckBuiltinRunner::new( + RangeCheckBuiltinRunner::::new( instance_def.ratio, - instance_def.n_parts, included, ) .into(), @@ -296,28 +299,30 @@ impl CairoRunner { if let Some(instance_def) = self.layout.builtins.ecdsa.as_ref() { let included = program_builtins.remove(&BuiltinName::ecdsa); if included || self.is_proof_mode() { - builtin_runners.push(SignatureBuiltinRunner::new(instance_def, included).into()); + builtin_runners + .push(SignatureBuiltinRunner::new(instance_def.ratio, included).into()); } } if let Some(instance_def) = self.layout.builtins.bitwise.as_ref() { let included = program_builtins.remove(&BuiltinName::bitwise); if included || self.is_proof_mode() { - builtin_runners.push(BitwiseBuiltinRunner::new(instance_def, included).into()); + builtin_runners + .push(BitwiseBuiltinRunner::new(instance_def.ratio, included).into()); } } if let Some(instance_def) = self.layout.builtins.ec_op.as_ref() { let included = program_builtins.remove(&BuiltinName::ec_op); if included || self.is_proof_mode() { - builtin_runners.push(EcOpBuiltinRunner::new(instance_def, included).into()); + builtin_runners.push(EcOpBuiltinRunner::new(instance_def.ratio, included).into()); } } if let Some(instance_def) = self.layout.builtins.keccak.as_ref() { let included = program_builtins.remove(&BuiltinName::keccak); if included || self.is_proof_mode() { - builtin_runners.push(KeccakBuiltinRunner::new(instance_def, included).into()); + builtin_runners.push(KeccakBuiltinRunner::new(instance_def.ratio, included).into()); } } @@ -328,10 +333,32 @@ impl CairoRunner { .push(PoseidonBuiltinRunner::new(instance_def.ratio, included).into()); } } + + if let Some(instance_def) = self.layout.builtins.range_check96.as_ref() { + let included = program_builtins.remove(&BuiltinName::range_check96); + if included || self.is_proof_mode() { + builtin_runners.push( + RangeCheckBuiltinRunner::::new(instance_def.ratio, included) + .into(), + ); + } + } + if let Some(instance_def) = self.layout.builtins.add_mod.as_ref() { + let included = program_builtins.remove(&BuiltinName::add_mod); + if included || self.is_proof_mode() { + builtin_runners.push(ModBuiltinRunner::new_add_mod(instance_def, included).into()); + } + } + if let Some(instance_def) = self.layout.builtins.mul_mod.as_ref() { + let included = program_builtins.remove(&BuiltinName::mul_mod); + if included || self.is_proof_mode() { + builtin_runners.push(ModBuiltinRunner::new_mul_mod(instance_def, included).into()); + } + } if !program_builtins.is_empty() && !allow_missing_builtins { return Err(RunnerError::NoBuiltinForInstance(Box::new(( program_builtins.iter().map(|n| n.name()).collect(), - self.layout._name.clone(), + self.layout.name.clone(), )))); } @@ -371,25 +398,24 @@ impl CairoRunner { BuiltinName::pedersen => vm .builtin_runners .push(HashBuiltinRunner::new(Some(32), true).into()), - BuiltinName::range_check => vm - .builtin_runners - .push(RangeCheckBuiltinRunner::new(Some(1), 8, true).into()), + BuiltinName::range_check => vm.builtin_runners.push( + RangeCheckBuiltinRunner::::new(Some(1), true).into(), + ), BuiltinName::output => vm .builtin_runners .push(OutputBuiltinRunner::new(true).into()), - BuiltinName::ecdsa => vm.builtin_runners.push( - SignatureBuiltinRunner::new(&EcdsaInstanceDef::new(Some(1)), true).into(), - ), - BuiltinName::bitwise => vm.builtin_runners.push( - BitwiseBuiltinRunner::new(&BitwiseInstanceDef::new(Some(1)), true).into(), - ), + BuiltinName::ecdsa => vm + .builtin_runners + .push(SignatureBuiltinRunner::new(Some(1), true).into()), + BuiltinName::bitwise => vm + .builtin_runners + .push(BitwiseBuiltinRunner::new(Some(1), true).into()), BuiltinName::ec_op => vm .builtin_runners - .push(EcOpBuiltinRunner::new(&EcOpInstanceDef::new(Some(1)), true).into()), - BuiltinName::keccak => vm.builtin_runners.push( - KeccakBuiltinRunner::new(&KeccakInstanceDef::new(Some(1), vec![200; 8]), true) - .into(), - ), + .push(EcOpBuiltinRunner::new(Some(1), true).into()), + BuiltinName::keccak => vm + .builtin_runners + .push(KeccakBuiltinRunner::new(Some(1), true).into()), BuiltinName::poseidon => vm .builtin_runners .push(PoseidonBuiltinRunner::new(Some(1), true).into()), @@ -399,6 +425,17 @@ impl CairoRunner { .push(SegmentArenaBuiltinRunner::new(true).into()) } } + BuiltinName::range_check96 => vm + .builtin_runners + .push(RangeCheckBuiltinRunner::::new(Some(1), true).into()), + BuiltinName::add_mod => vm.builtin_runners.push( + ModBuiltinRunner::new_add_mod(&ModInstanceDef::new(Some(1), 1, 96), true) + .into(), + ), + BuiltinName::mul_mod => vm.builtin_runners.push( + ModBuiltinRunner::new_mul_mod(&ModInstanceDef::new(Some(1), 1, 96), true) + .into(), + ), } } @@ -435,30 +472,19 @@ impl CairoRunner { entrypoint: usize, stack: Vec, ) -> Result<(), RunnerError> { - if let Some(prog_base) = self.program_base { - let initial_pc = Relocatable { - segment_index: prog_base.segment_index, - offset: prog_base.offset + entrypoint, - }; - self.initial_pc = Some(initial_pc); - vm.load_data(prog_base, &self.program.shared_program_data.data) - .map_err(RunnerError::MemoryInitializationError)?; - - // Mark all addresses from the program segment as accessed - let base = self - .program_base - .unwrap_or_else(|| Relocatable::from((0, 0))); - for i in 0..self.program.shared_program_data.data.len() { - vm.segments.memory.mark_as_accessed((base + i)?); - } - } - if let Some(exec_base) = self.execution_base { - vm.segments - .load_data(exec_base, &stack) - .map_err(RunnerError::MemoryInitializationError)?; - } else { - return Err(RunnerError::NoProgBase); + let prog_base = self.program_base.ok_or(RunnerError::NoProgBase)?; + let exec_base = self.execution_base.ok_or(RunnerError::NoExecBase)?; + self.initial_pc = Some((prog_base + entrypoint)?); + vm.load_data(prog_base, &self.program.shared_program_data.data) + .map_err(RunnerError::MemoryInitializationError)?; + + // Mark all addresses from the program segment as accessed + for i in 0..self.program.shared_program_data.data.len() { + vm.segments.memory.mark_as_accessed((prog_base + i)?); } + vm.segments + .load_data(exec_base, &stack) + .map_err(RunnerError::MemoryInitializationError)?; Ok(()) } @@ -496,9 +522,19 @@ impl CairoRunner { vm: &mut VirtualMachine, ) -> Result { let mut stack = Vec::new(); - - for builtin_runner in vm.builtin_runners.iter() { - stack.append(&mut builtin_runner.initial_stack()); + { + let builtin_runners = vm + .builtin_runners + .iter() + .map(|b| (b.identifier(), b)) + .collect::>(); + for builtin_id in &self.program.builtins { + if let Some(builtin_runner) = builtin_runners.get(builtin_id) { + stack.append(&mut builtin_runner.initial_stack()); + } else { + stack.push(Felt252::ZERO.into()) + } + } } if self.is_proof_mode() { @@ -1090,8 +1126,8 @@ impl CairoRunner { let (_, size) = builtin_runner .get_used_cells_and_allocated_size(vm) .map_err(RunnerError::FinalizeSegements)?; - if builtin_runner.name() == OUTPUT_BUILTIN_NAME { - let public_memory = (0..size).map(|i| (i, 0)).collect(); + if let BuiltinRunner::Output(output_builtin) = builtin_runner { + let public_memory = output_builtin.get_public_memory(&vm.segments)?; vm.segments .finalize(Some(size), builtin_runner.base(), Some(&public_memory)) } else { @@ -1099,6 +1135,7 @@ impl CairoRunner { .finalize(Some(size), builtin_runner.base(), None) } } + vm.segments.finalize_zero_segment(); self.segments_finalized = true; Ok(()) } @@ -1167,13 +1204,13 @@ impl CairoRunner { // Out of the memory units available per step, a fraction is used for public memory, and // four are used for the instruction. - let total_memory_units = instance._memory_units_per_step * vm_current_step_u32; + let total_memory_units = MEMORY_UNITS_PER_STEP * vm_current_step_u32; let (public_memory_units, rem) = - div_rem(total_memory_units, instance._public_memory_fraction); + div_rem(total_memory_units, instance.public_memory_fraction); if rem != 0 { return Err(MathError::SafeDivFailU32( total_memory_units, - instance._public_memory_fraction, + instance.public_memory_fraction, ) .into()); } @@ -1238,14 +1275,33 @@ impl CairoRunner { Ok(()) } - pub fn read_return_values(&mut self, vm: &mut VirtualMachine) -> Result<(), RunnerError> { + pub fn read_return_values( + &mut self, + vm: &mut VirtualMachine, + allow_missing_builtins: bool, + ) -> Result<(), RunnerError> { if !self.run_ended { return Err(RunnerError::ReadReturnValuesNoEndRun); } let mut pointer = vm.get_ap(); - for builtin_runner in vm.builtin_runners.iter_mut().rev() { - let new_pointer = builtin_runner.final_stack(&vm.segments, pointer)?; - pointer = new_pointer; + for builtin_id in self.program.builtins.iter().rev() { + if let Some(builtin_runner) = vm + .builtin_runners + .iter_mut() + .find(|b| b.identifier() == *builtin_id) + { + let new_pointer = builtin_runner.final_stack(&vm.segments, pointer)?; + pointer = new_pointer; + } else { + if !allow_missing_builtins { + return Err(RunnerError::MissingBuiltin(builtin_id.name())); + } + pointer.offset = pointer.offset.saturating_sub(1); + + if !vm.get_integer(pointer)?.is_zero() { + return Err(RunnerError::MissingBuiltinStopPtrNotZero(builtin_id.name())); + } + } } if self.segments_finalized { return Err(RunnerError::FailedAddingReturnValues); @@ -1266,22 +1322,6 @@ impl CairoRunner { Ok(()) } - //NOTE: No longer needed in 0.11 - /// Add (or replace if already present) a custom hash builtin. Returns a Relocatable - /// with the new builtin base as the segment index. - pub fn add_additional_hash_builtin(&self, vm: &mut VirtualMachine) -> Relocatable { - // Create, initialize and insert the new custom hash runner. - let mut builtin: BuiltinRunner = HashBuiltinRunner::new(Some(32), true).into(); - builtin.initialize_segments(&mut vm.segments); - let segment_index = builtin.base() as isize; - vm.builtin_runners.push(builtin); - - Relocatable { - segment_index, - offset: 0, - } - } - // Iterates over the program builtins in reverse, calling BuiltinRunner::final_stack on each of them and returns the final pointer // This method is used by cairo-vm-py to replace starknet functionality pub fn get_builtins_final_stack( @@ -1416,7 +1456,7 @@ impl CairoRunner { &self, vm: &VirtualMachine, ) -> Result { - let layout_name = self.get_layout()._name.as_str(); + let layout_name = self.get_layout().name.as_str(); let dyn_layout = match layout_name { "dynamic" => Some(self.get_layout()), _ => None, @@ -1427,7 +1467,7 @@ impl CairoRunner { layout_name, dyn_layout, &vm.get_public_memory_addresses()?, - vm.get_memory_segment_addresses()?, + self.get_memory_segment_addresses(vm)?, self.relocated_trace .as_ref() .ok_or(PublicInputError::EmptyTrace)?, @@ -1439,13 +1479,45 @@ impl CairoRunner { pub fn get_air_private_input(&self, vm: &VirtualMachine) -> AirPrivateInput { let mut private_inputs = HashMap::new(); for builtin in vm.builtin_runners.iter() { - private_inputs.insert( - builtin.name(), - builtin.air_private_input(&vm.segments.memory), - ); + private_inputs.insert(builtin.name(), builtin.air_private_input(&vm.segments)); } AirPrivateInput(private_inputs) } + + pub fn get_memory_segment_addresses( + &self, + vm: &VirtualMachine, + ) -> Result, VirtualMachineError> { + let relocation_table = vm + .relocation_table + .as_ref() + .ok_or(MemoryError::UnrelocatedMemory)?; + + let relocate = |segment: (usize, usize)| -> Result<(usize, usize), VirtualMachineError> { + let (index, stop_ptr_offset) = segment; + let base = relocation_table + .get(index) + .ok_or(VirtualMachineError::RelocationNotFound(index))?; + Ok((*base, base + stop_ptr_offset)) + }; + + vm.builtin_runners + .iter() + .map(|builtin| -> Result<_, VirtualMachineError> { + let (base, stop_ptr) = builtin.get_memory_segment_addresses(); + let stop_ptr = if self.program.builtins.contains(&builtin.identifier()) { + stop_ptr.ok_or_else(|| RunnerError::NoStopPointer(Box::new(builtin.name())))? + } else { + stop_ptr.unwrap_or_default() + }; + + Ok(( + builtin.name().strip_suffix("_builtin").unwrap_or_default(), + relocate((base, stop_ptr))?, + )) + }) + .collect() + } } #[derive(Clone, Debug, Eq, PartialEq)] @@ -1563,7 +1635,6 @@ mod tests { hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor, relocatable, serde::deserialize_program::{Identifier, ReferenceManager}, - types::instance_definitions::bitwise_instance_def::BitwiseInstanceDef, utils::test_utils::*, vm::trace::trace_entry::TraceEntry, }; @@ -3257,6 +3328,7 @@ mod tests { // Swap the first and second builtins (first should be `output`). vm.builtin_runners.swap(0, 1); + cairo_runner.program.builtins.swap(0, 1); cairo_runner.initialize_segments(&mut vm, None); @@ -3713,8 +3785,7 @@ mod tests { let mut vm = vm!(); vm.current_step = 8192; - vm.builtin_runners = - vec![BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true).into()]; + vm.builtin_runners = vec![BitwiseBuiltinRunner::new(Some(256), true).into()]; assert_matches!(cairo_runner.check_diluted_check_usage(&vm), Ok(())); } @@ -4101,7 +4172,8 @@ mod tests { vm.segments.memory.data = vec![vec![Some(MemoryCell::new(mayberelocatable!( 0x80FF_8000_0530u64 )))]]; - vm.builtin_runners = vec![RangeCheckBuiltinRunner::new(Some(12), 5, true).into()]; + vm.builtin_runners = + vec![RangeCheckBuiltinRunner::::new(Some(12), true).into()]; assert_matches!( cairo_runner.get_perm_range_check_limits(&vm), @@ -4155,7 +4227,8 @@ mod tests { let cairo_runner = cairo_runner!(program); let mut vm = vm!(); - vm.builtin_runners = vec![RangeCheckBuiltinRunner::new(Some(8), 8, true).into()]; + vm.builtin_runners = + vec![RangeCheckBuiltinRunner::::new(Some(8), true).into()]; vm.segments.memory.data = vec![vec![Some(MemoryCell::new(mayberelocatable!( 0x80FF_8000_0530u64 )))]]; @@ -4222,7 +4295,8 @@ mod tests { let cairo_runner = cairo_runner!(program); let mut vm = vm!(); - vm.builtin_runners = vec![RangeCheckBuiltinRunner::new(Some(8), 8, true).into()]; + vm.builtin_runners = + vec![RangeCheckBuiltinRunner::::new(Some(8), true).into()]; vm.segments.memory.data = vec![vec![Some(MemoryCell::new(mayberelocatable!( 0x80FF_8000_0530u64 )))]]; @@ -4657,7 +4731,7 @@ mod tests { let mut vm = vm!(); //Check values written by first call to segments.finalize() - assert_eq!(cairo_runner.read_return_values(&mut vm), Ok(())); + assert_eq!(cairo_runner.read_return_values(&mut vm, false), Ok(())); assert_eq!( cairo_runner .execution_public_memory @@ -4679,7 +4753,7 @@ mod tests { cairo_runner.run_ended = false; let mut vm = vm!(); assert_eq!( - cairo_runner.read_return_values(&mut vm), + cairo_runner.read_return_values(&mut vm, false), Err(RunnerError::ReadReturnValuesNoEndRun) ); } @@ -4698,7 +4772,7 @@ mod tests { cairo_runner.segments_finalized = true; let mut vm = vm!(); assert_eq!( - cairo_runner.read_return_values(&mut vm), + cairo_runner.read_return_values(&mut vm, false), Err(RunnerError::FailedAddingReturnValues) ); } @@ -4726,7 +4800,7 @@ mod tests { vm.set_ap(1); vm.segments.segment_used_sizes = Some(vec![0, 1, 0]); //Check values written by first call to segments.finalize() - assert_eq!(cairo_runner.read_return_values(&mut vm), Ok(())); + assert_eq!(cairo_runner.read_return_values(&mut vm, false), Ok(())); let output_builtin = match &vm.builtin_runners[0] { BuiltinRunner::Output(runner) => runner, _ => unreachable!(), @@ -4757,7 +4831,7 @@ mod tests { vm.set_ap(1); vm.segments.segment_used_sizes = Some(vec![1, 1, 0]); //Check values written by first call to segments.finalize() - assert_eq!(cairo_runner.read_return_values(&mut vm), Ok(())); + assert_eq!(cairo_runner.read_return_values(&mut vm, false), Ok(())); let output_builtin = match &vm.builtin_runners[0] { BuiltinRunner::Output(runner) => runner, _ => unreachable!(), @@ -4779,7 +4853,7 @@ mod tests { cairo_runner.segments_finalized = false; let mut vm = vm!(); let output_builtin = OutputBuiltinRunner::new(true); - let bitwise_builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); + let bitwise_builtin = BitwiseBuiltinRunner::new(Some(256), true); vm.builtin_runners.push(output_builtin.into()); vm.builtin_runners.push(bitwise_builtin.into()); cairo_runner.initialize_segments(&mut vm, None); @@ -4795,13 +4869,13 @@ mod tests { // We use 5 as bitwise builtin's segment size as a bitwise instance is 5 cells vm.segments.segment_used_sizes = Some(vec![0, 2, 0, 5]); //Check values written by first call to segments.finalize() - assert_eq!(cairo_runner.read_return_values(&mut vm), Ok(())); + assert_eq!(cairo_runner.read_return_values(&mut vm, false), Ok(())); let output_builtin = match &vm.builtin_runners[0] { BuiltinRunner::Output(runner) => runner, _ => unreachable!(), }; assert_eq!(output_builtin.stop_ptr, Some(0)); - assert_eq!(cairo_runner.read_return_values(&mut vm), Ok(())); + assert_eq!(cairo_runner.read_return_values(&mut vm, false), Ok(())); let bitwise_builtin = match &vm.builtin_runners[1] { BuiltinRunner::Bitwise(runner) => runner, _ => unreachable!(), @@ -4809,32 +4883,6 @@ mod tests { assert_eq!(bitwise_builtin.stop_ptr, Some(5)); } - /// Test that add_additional_hash_builtin() creates an additional builtin. - #[test] - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn add_additional_hash_builtin() { - let program = program!(); - let cairo_runner = cairo_runner!(program); - let mut vm = vm!(); - - let num_builtins = vm.builtin_runners.len(); - cairo_runner.add_additional_hash_builtin(&mut vm); - assert_eq!(vm.builtin_runners.len(), num_builtins + 1); - - let builtin = vm - .builtin_runners - .last() - .expect("missing last builtin runner"); - match builtin { - BuiltinRunner::Hash(builtin) => { - assert_eq!(builtin.base(), 0); - assert_eq!(builtin.ratio(), Some(32)); - assert!(builtin.included); - } - _ => unreachable!(), - } - } - #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_from_entrypoint_custom_program_test() { diff --git a/vm/src/vm/security.rs b/vm/src/vm/security.rs index 2749fcf154..45440cfd76 100644 --- a/vm/src/vm/security.rs +++ b/vm/src/vm/security.rs @@ -87,7 +87,6 @@ mod test { use super::*; use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; use crate::serde::deserialize_program::BuiltinName; - use crate::stdlib::collections::HashMap; use crate::types::relocatable::Relocatable; diff --git a/vm/src/vm/trace/mod.rs b/vm/src/vm/trace/mod.rs index 838bfe9d39..5a9f683570 100644 --- a/vm/src/vm/trace/mod.rs +++ b/vm/src/vm/trace/mod.rs @@ -10,7 +10,7 @@ pub mod trace_entry { ///A trace entry for every instruction that was executed. ///Holds the register values before the instruction was executed. ///Register values for ap & fp are represented as their offsets, as their indexes will always be 1 - #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)] + #[derive(Debug, PartialEq, Eq, Deserialize, Serialize, Clone)] pub struct TraceEntry { pub pc: Relocatable, pub ap: usize, diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index 4dc6be0dcd..42a02b80fb 100644 --- a/vm/src/vm/vm_core.rs +++ b/vm/src/vm/vm_core.rs @@ -19,7 +19,9 @@ use crate::{ exec_scope_errors::ExecScopeError, memory_errors::MemoryError, vm_errors::VirtualMachineError, }, - runners::builtin_runner::{BuiltinRunner, RangeCheckBuiltinRunner, SignatureBuiltinRunner}, + runners::builtin_runner::{ + BuiltinRunner, OutputBuiltinRunner, RangeCheckBuiltinRunner, SignatureBuiltinRunner, + }, trace::trace_entry::TraceEntry, vm_memory::memory_segments::MemorySegmentManager, }, @@ -32,7 +34,10 @@ use core::num::NonZeroUsize; use num_traits::{ToPrimitive, Zero}; use super::errors::runner_errors::RunnerError; -use super::runners::builtin_runner::OUTPUT_BUILTIN_NAME; +use super::runners::builtin_runner::{ + ModBuiltinRunner, ADD_MOD_BUILTIN_NAME, MUL_MOD_BUILTIN_NAME, OUTPUT_BUILTIN_NAME, + RC_N_PARTS_STANDARD, +}; const MAX_TRACEBACK_ENTRIES: u32 = 20; @@ -922,7 +927,9 @@ impl VirtualMachine { self.segments.memory.get_integer_range(addr, size) } - pub fn get_range_check_builtin(&self) -> Result<&RangeCheckBuiltinRunner, VirtualMachineError> { + pub fn get_range_check_builtin( + &self, + ) -> Result<&RangeCheckBuiltinRunner, VirtualMachineError> { for builtin in &self.builtin_runners { if let BuiltinRunner::RangeCheck(range_check_builtin) = builtin { return Ok(range_check_builtin); @@ -942,8 +949,22 @@ impl VirtualMachine { Err(VirtualMachineError::NoSignatureBuiltin) } - pub fn disable_trace(&mut self) { - self.trace = None + + pub fn get_output_builtin_mut( + &mut self, + ) -> Result<&mut OutputBuiltinRunner, VirtualMachineError> { + for builtin in self.get_builtin_runners_as_mut() { + if let BuiltinRunner::Output(output_builtin) = builtin { + return Ok(output_builtin); + }; + } + + Err(VirtualMachineError::NoOutputBuiltin) + } + + #[cfg(feature = "tracer")] + pub fn relocate_segments(&self) -> Result, MemoryError> { + self.segments.relocate_segments() } #[doc(hidden)] @@ -1013,7 +1034,6 @@ impl VirtualMachine { let segment_used_sizes = self.segments.compute_effective_sizes(); let segment_index = builtin.base(); - #[allow(deprecated)] for i in 0..segment_used_sizes[segment_index] { let formatted_value = match self .segments @@ -1044,40 +1064,6 @@ impl VirtualMachine { } } - pub fn get_memory_segment_addresses( - &self, - ) -> Result, VirtualMachineError> { - let relocation_table = self - .relocation_table - .as_ref() - .ok_or(MemoryError::UnrelocatedMemory)?; - - let relocate = |segment: (usize, usize)| -> Result<(usize, usize), VirtualMachineError> { - let (index, stop_ptr_offset) = segment; - let base = relocation_table - .get(index) - .ok_or(VirtualMachineError::RelocationNotFound(index))?; - Ok((*base, base + stop_ptr_offset)) - }; - - self.builtin_runners - .iter() - .map(|builtin| -> Result<_, VirtualMachineError> { - let addresses = - if let (base, Some(stop_ptr)) = builtin.get_memory_segment_addresses() { - (base, stop_ptr) - } else { - return Err(RunnerError::NoStopPointer(Box::new(builtin.name())).into()); - }; - - Ok(( - builtin.name().strip_suffix("_builtin").unwrap_or_default(), - relocate(addresses)?, - )) - }) - .collect() - } - #[doc(hidden)] pub fn builtins_final_stack_from_stack_pointer_dict( &mut self, @@ -1102,9 +1088,59 @@ impl VirtualMachine { #[doc(hidden)] pub fn set_output_stop_ptr_offset(&mut self, offset: usize) { if let Some(BuiltinRunner::Output(builtin)) = self.builtin_runners.first_mut() { - builtin.set_stop_ptr_offset(offset) + builtin.set_stop_ptr_offset(offset); + if let Some(segment_used_sizes) = &mut self.segments.segment_used_sizes { + segment_used_sizes[builtin.base()] = offset; + } } } + + /// Fetches add_mod & mul_mod builtins according to the optional arguments and executes `fill_memory` + /// Returns an error if either of this optional parameters is true but the corresponding builtin is not present + /// Verifies that both builtin's (if present) batch sizes match the batch_size arg if set + // This method is needed as running `fill_memory` direclty from outside the vm struct would require cloning the builtin runners to avoid double borrowing + pub fn mod_builtin_fill_memory( + &mut self, + add_mod_ptr_n: Option<(Relocatable, usize)>, + mul_mod_ptr_n: Option<(Relocatable, usize)>, + batch_size: Option, + ) -> Result<(), VirtualMachineError> { + let fetch_builtin_params = |mod_params: Option<(Relocatable, usize)>, + mod_name: &'static str| + -> Result< + Option<(Relocatable, &ModBuiltinRunner, usize)>, + VirtualMachineError, + > { + if let Some((ptr, n)) = mod_params { + let mod_builtin = self + .builtin_runners + .iter() + .find_map(|b| match b { + BuiltinRunner::Mod(b) if b.name() == mod_name => Some(b), + _ => None, + }) + .ok_or_else(|| VirtualMachineError::NoModBuiltin(mod_name))?; + if let Some(batch_size) = batch_size { + if mod_builtin.batch_size() != batch_size { + return Err(VirtualMachineError::ModBuiltinBatchSize(Box::new(( + mod_builtin.name(), + batch_size, + )))); + } + } + Ok(Some((ptr, mod_builtin, n))) + } else { + Ok(None) + } + }; + + ModBuiltinRunner::fill_memory( + &mut self.segments.memory, + fetch_builtin_params(add_mod_ptr_n, ADD_MOD_BUILTIN_NAME)?, + fetch_builtin_params(mul_mod_ptr_n, MUL_MOD_BUILTIN_NAME)?, + ) + .map_err(VirtualMachineError::RunnerError) + } } pub struct VirtualMachineBuilder { @@ -1213,7 +1249,6 @@ mod tests { use crate::vm::runners::builtin_runner::{ BITWISE_BUILTIN_NAME, EC_OP_BUILTIN_NAME, HASH_BUILTIN_NAME, }; - use crate::vm::vm_memory::memory::Memory; use crate::{ any_box, hint_processor::builtin_hint_processor::builtin_hint_processor_definition::{ @@ -1221,9 +1256,6 @@ mod tests { }, relocatable, types::{ - instance_definitions::{ - bitwise_instance_def::BitwiseInstanceDef, ec_op_instance_def::EcOpInstanceDef, - }, instruction::{Op1Addr, Register}, relocatable::Relocatable, }, @@ -3345,7 +3377,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn deduce_memory_cell_bitwise_builtin_valid_and() { let mut vm = vm!(); - let builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); + let builtin = BitwiseBuiltinRunner::new(Some(256), true); vm.builtin_runners.push(builtin.into()); vm.segments = segments![((0, 5), 10), ((0, 6), 12), ((0, 7), 0)]; assert_matches!( @@ -3383,7 +3415,7 @@ mod tests { opcode: Opcode::AssertEq, }; - let mut builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); + let mut builtin = BitwiseBuiltinRunner::new(Some(256), true); builtin.base = 2; let mut vm = vm!(); @@ -3424,7 +3456,7 @@ mod tests { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn deduce_memory_cell_ec_op_builtin_valid() { let mut vm = vm!(); - let builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let builtin = EcOpBuiltinRunner::new(Some(256), true); vm.builtin_runners.push(builtin.into()); vm.segments = segments![ @@ -3493,7 +3525,7 @@ mod tests { end */ fn verify_auto_deductions_for_ec_op_builtin_valid() { - let mut builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let mut builtin = EcOpBuiltinRunner::new(Some(256), true); builtin.base = 3; let mut vm = vm!(); vm.builtin_runners.push(builtin.into()); @@ -3541,7 +3573,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn verify_auto_deductions_for_ec_op_builtin_valid_points_invalid_result() { - let mut builtin = EcOpBuiltinRunner::new(&EcOpInstanceDef::default(), true); + let mut builtin = EcOpBuiltinRunner::new(Some(256), true); builtin.base = 3; let mut vm = vm!(); vm.builtin_runners.push(builtin.into()); @@ -3612,7 +3644,7 @@ mod tests { end */ fn verify_auto_deductions_bitwise() { - let mut builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); + let mut builtin = BitwiseBuiltinRunner::new(Some(256), true); builtin.base = 2; let mut vm = vm!(); vm.builtin_runners.push(builtin.into()); @@ -3635,7 +3667,7 @@ mod tests { end */ fn verify_auto_deductions_for_addr_bitwise() { - let mut builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); + let mut builtin = BitwiseBuiltinRunner::new(Some(256), true); builtin.base = 2; let builtin: BuiltinRunner = builtin.into(); let mut vm = vm!(); @@ -3825,7 +3857,7 @@ mod tests { fn test_get_builtin_runners() { let mut vm = vm!(); let hash_builtin = HashBuiltinRunner::new(Some(8), true); - let bitwise_builtin = BitwiseBuiltinRunner::new(&BitwiseInstanceDef::default(), true); + let bitwise_builtin = BitwiseBuiltinRunner::new(Some(256), true); vm.builtin_runners.push(hash_builtin.into()); vm.builtin_runners.push(bitwise_builtin.into()); @@ -3837,11 +3869,26 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] - fn disable_trace() { - let mut vm = VirtualMachine::new(true); - assert!(vm.trace.is_some()); - vm.disable_trace(); - assert!(vm.trace.is_none()); + fn test_get_output_builtin_mut() { + let mut vm = vm!(); + + assert_matches!( + vm.get_output_builtin_mut(), + Err(VirtualMachineError::NoOutputBuiltin) + ); + + let output_builtin = OutputBuiltinRunner::new(true); + vm.builtin_runners.push(output_builtin.clone().into()); + + let vm_output_builtin = vm + .get_output_builtin_mut() + .expect("Output builtin should be returned"); + + assert_eq!(vm_output_builtin.base(), output_builtin.base()); + assert_eq!(vm_output_builtin.pages, output_builtin.pages); + assert_eq!(vm_output_builtin.attributes, output_builtin.attributes); + assert_eq!(vm_output_builtin.stop_ptr, output_builtin.stop_ptr); + assert_eq!(vm_output_builtin.included, output_builtin.included); } #[test] @@ -4313,11 +4360,10 @@ mod tests { ap: 18, fp: 0, }) - .segments(MemorySegmentManager { - segment_sizes: HashMap::new(), - segment_used_sizes: Some(vec![1]), - public_memory_offsets: HashMap::new(), - memory: Memory::new(), + .segments({ + let mut segments = MemorySegmentManager::new(); + segments.segment_used_sizes = Some(vec![1]); + segments }) .skip_instruction_execution(true) .trace(Some(vec![TraceEntry { diff --git a/vm/src/vm/vm_memory/memory.rs b/vm/src/vm/vm_memory/memory.rs index f50d5c11cd..2ea342573b 100644 --- a/vm/src/vm/vm_memory/memory.rs +++ b/vm/src/vm/vm_memory/memory.rs @@ -1,5 +1,6 @@ use crate::stdlib::{borrow::Cow, collections::HashMap, fmt, prelude::*}; +use crate::types::errors::math_errors::MathError; use crate::vm::runners::cairo_pie::CairoPieMemory; use crate::Felt252; use crate::{ @@ -284,6 +285,14 @@ impl Memory { } } + /// Gets the value from memory address as a usize. + /// Returns an Error if the value at the memory address is missing not a Felt252, or can't be converted to usize. + pub fn get_usize(&self, key: Relocatable) -> Result { + let felt = self.get_integer(key)?.into_owned(); + felt.to_usize() + .ok_or_else(|| MemoryError::Math(MathError::Felt252ToUsizeConversion(Box::new(felt)))) + } + /// Gets the value from memory address as a Relocatable value. /// Returns an Error if the value at the memory address is missing or not a Relocatable. pub fn get_relocatable(&self, key: Relocatable) -> Result { @@ -516,6 +525,21 @@ impl Memory { .count(), ) } + + // Inserts a value into memory & inmediately marks it as accessed if insertion was succesful + // Used by ModBuiltinRunner, as it accesses memory outside of it's segment when operating + pub(crate) fn insert_as_accessed( + &mut self, + key: Relocatable, + val: V, + ) -> Result<(), MemoryError> + where + MaybeRelocatable: From, + { + self.insert(key, val)?; + self.mark_as_accessed(key); + Ok(()) + } } impl From<&Memory> for CairoPieMemory { @@ -605,10 +629,11 @@ mod memory_tests { use super::*; use crate::{ felt_hex, relocatable, - types::instance_definitions::ecdsa_instance_def::EcdsaInstanceDef, utils::test_utils::*, vm::{ - runners::builtin_runner::{RangeCheckBuiltinRunner, SignatureBuiltinRunner}, + runners::builtin_runner::{ + RangeCheckBuiltinRunner, SignatureBuiltinRunner, RC_N_PARTS_STANDARD, + }, vm_memory::memory_segments::MemorySegmentManager, }, }; @@ -779,7 +804,7 @@ mod memory_tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn validate_existing_memory_for_range_check_within_bounds() { - let mut builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let mut builtin = RangeCheckBuiltinRunner::::new(Some(8), true); let mut segments = MemorySegmentManager::new(); builtin.initialize_segments(&mut segments); builtin.add_validation_rule(&mut segments.memory); @@ -804,7 +829,7 @@ mod memory_tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn validate_existing_memory_for_range_check_outside_bounds() { - let mut builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let mut builtin = RangeCheckBuiltinRunner::::new(Some(8), true); let mut segments = MemorySegmentManager::new(); segments.add(); builtin.initialize_segments(&mut segments); @@ -829,7 +854,7 @@ mod memory_tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn validate_existing_memory_for_invalid_signature() { - let mut builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let mut builtin = SignatureBuiltinRunner::new(Some(512), true); let mut segments = MemorySegmentManager::new(); builtin.initialize_segments(&mut segments); segments.memory = memory![ @@ -859,7 +884,7 @@ mod memory_tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn validate_existing_memory_for_valid_signature() { - let mut builtin = SignatureBuiltinRunner::new(&EcdsaInstanceDef::default(), true); + let mut builtin = SignatureBuiltinRunner::new(Some(512), true); let signature_r = felt_hex!("0x411494b501a98abd8262b0da1351e17899a0c4ef23dd2f96fec5ba847310b20"); @@ -895,7 +920,7 @@ mod memory_tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn validate_existing_memory_for_range_check_relocatable_value() { - let mut builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let mut builtin = RangeCheckBuiltinRunner::::new(Some(8), true); let mut segments = MemorySegmentManager::new(); builtin.initialize_segments(&mut segments); segments.memory = memory![((0, 0), (0, 4))]; @@ -912,7 +937,7 @@ mod memory_tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn validate_existing_memory_for_range_check_out_of_bounds_diff_segment() { - let mut builtin = RangeCheckBuiltinRunner::new(Some(8), 8, true); + let mut builtin = RangeCheckBuiltinRunner::::new(Some(8), true); let mut segments = MemorySegmentManager::new(); segments.memory = Memory::new(); segments.add(); diff --git a/vm/src/vm/vm_memory/memory_segments.rs b/vm/src/vm/vm_memory/memory_segments.rs index cabba6266b..f9886da1a5 100644 --- a/vm/src/vm/vm_memory/memory_segments.rs +++ b/vm/src/vm/vm_memory/memory_segments.rs @@ -1,5 +1,9 @@ +use core::cmp::max; use core::fmt; +use crate::Felt252; +use num_traits::Zero; + use crate::stdlib::prelude::*; use crate::stdlib::{any::Any, collections::HashMap}; use crate::vm::runners::cairo_runner::CairoArg; @@ -12,6 +16,8 @@ use crate::{ }, }; +use super::memory::MemoryCell; + pub struct MemorySegmentManager { pub segment_sizes: HashMap, pub segment_used_sizes: Option>, @@ -19,6 +25,11 @@ pub struct MemorySegmentManager { // A map from segment index to a list of pairs (offset, page_id) that constitute the // public memory. Note that the offset is absolute (not based on the page_id). pub public_memory_offsets: HashMap>, + // Segment index of the zero segment index, a memory segment filled with zeroes, used exclusively by builtin runners + // This segment will never have index 0 so we use 0 to represent uninitialized value + zero_segment_index: usize, + // Segment size of the zero segment index + zero_segment_size: usize, } impl MemorySegmentManager { @@ -71,6 +82,8 @@ impl MemorySegmentManager { segment_used_sizes: None, public_memory_offsets: HashMap::new(), memory: Memory::new(), + zero_segment_index: 0, + zero_segment_size: 0, } } @@ -206,11 +219,16 @@ impl MemorySegmentManager { if i > builtin_segments_start && i <= builtin_segments_end { continue; } - let accessed_amount = match self.memory.get_amount_of_accessed_addresses_for_segment(i) - { - Some(accessed_amount) if accessed_amount > 0 => accessed_amount, - _ => continue, - }; + let accessed_amount = + // Instead of marking the values in the zero segment until zero_segment_size as accessed we use zero_segment_size as accessed_amount + if !self.zero_segment_index.is_zero() && i == self.zero_segment_index { + self.zero_segment_size + } else { + match self.memory.get_amount_of_accessed_addresses_for_segment(i) { + Some(accessed_amount) if accessed_amount > 0 => accessed_amount, + _ => continue, + } + }; let segment_size = self .get_segment_size(i) .ok_or(MemoryError::MissingSegmentUsedSizes)?; @@ -263,6 +281,33 @@ impl MemorySegmentManager { self.public_memory_offsets .insert(segment_index, public_memory.cloned().unwrap_or_default()); } + + // Creates the zero segment if it wasn't previously created + // Fills the segment with the value 0 until size is reached + // Returns the index of the zero segment + pub(crate) fn add_zero_segment(&mut self, size: usize) -> usize { + if self.zero_segment_index.is_zero() { + self.zero_segment_index = self.add().segment_index as usize; + } + // Fil zero segment with zero values until size is reached + for _ in 0..self.zero_segment_size.saturating_sub(size) { + // As zero_segment_index is only accessible to the segment manager + // we can asume that it is always valid and index direcly into it + self.memory.data[self.zero_segment_index] + .push(Some(MemoryCell::new(Felt252::ZERO.into()))) + } + self.zero_segment_size = max(self.zero_segment_size, size); + self.zero_segment_index + } + + // Finalizes the zero segment and clears it's tracking data from the manager + pub(crate) fn finalize_zero_segment(&mut self) { + if !self.zero_segment_index.is_zero() { + self.finalize(Some(self.zero_segment_size), self.zero_segment_index, None); + self.zero_segment_index = 0; + self.zero_segment_size = 0; + } + } } impl Default for MemorySegmentManager {