diff --git a/.github/workflows/bench.yml b/.github/workflows/bench.yml index 280f3b79ba..f428b7b096 100644 --- a/.github/workflows/bench.yml +++ b/.github/workflows/bench.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Install Rust - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 with: components: rustfmt, clippy - uses: actions/checkout@v3 diff --git a/.github/workflows/cairo_1_programs.yml b/.github/workflows/cairo_1_programs.yml index aa7957cc58..420d325ee2 100644 --- a/.github/workflows/cairo_1_programs.yml +++ b/.github/workflows/cairo_1_programs.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 - name: Set up Cargo cache uses: Swatinem/rust-cache@v2 - name: Checkout diff --git a/.github/workflows/fresh_run.yml b/.github/workflows/fresh_run.yml index a09ffb4747..2cf3ec999d 100644 --- a/.github/workflows/fresh_run.yml +++ b/.github/workflows/fresh_run.yml @@ -33,12 +33,12 @@ jobs: sudo rm -rf /opt/ghc sudo rm -rf "/usr/local/share/boost" sudo rm -rf "$AGENT_TOOLSDIRECTORY" - + - name: Checkout uses: actions/checkout@v3 - + - name: Install Rust - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 - name: Install Pyenv uses: "gabrielfalcao/pyenv-action@v13" diff --git a/.github/workflows/hint_accountant.yml b/.github/workflows/hint_accountant.yml index 9d015e41d5..096256a169 100644 --- a/.github/workflows/hint_accountant.yml +++ b/.github/workflows/hint_accountant.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 - name: Set up Cargo cache uses: Swatinem/rust-cache@v2 - name: Checkout diff --git a/.github/workflows/hyper_threading_benchmarks.yml b/.github/workflows/hyper_threading_benchmarks.yml index d772a0a6e5..f24c0f26f4 100644 --- a/.github/workflows/hyper_threading_benchmarks.yml +++ b/.github/workflows/hyper_threading_benchmarks.yml @@ -26,7 +26,7 @@ jobs: sudo apt-get install -y hyperfine - name: Install Rust - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 with: components: rustfmt, clippy diff --git a/.github/workflows/hyperfine.yml b/.github/workflows/hyperfine.yml index f2fd0f8405..370d76d80a 100644 --- a/.github/workflows/hyperfine.yml +++ b/.github/workflows/hyperfine.yml @@ -67,14 +67,14 @@ jobs: steps: - name: Populate cache uses: actions/cache@v3 - id: cache + id: cache with: path: bin/cairo-vm-cli-${{ matrix.branch }} key: binary-${{ github.event.pull_request[matrix.branch].sha }} - name: Install Rust if: ${{ steps.cache.outputs.cache-hit != 'true' }} - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 - name: Checkout if: ${{ steps.cache.outputs.cache-hit != 'true' }} diff --git a/.github/workflows/iai_main.yml b/.github/workflows/iai_main.yml index 6177590fff..9545d7c1f6 100644 --- a/.github/workflows/iai_main.yml +++ b/.github/workflows/iai_main.yml @@ -11,7 +11,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Install Rust - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 - name: Set up cargo cache uses: Swatinem/rust-cache@v2 - name: Python3 Build diff --git a/.github/workflows/iai_pr.yml b/.github/workflows/iai_pr.yml index 875643ea72..bfbd513f84 100644 --- a/.github/workflows/iai_pr.yml +++ b/.github/workflows/iai_pr.yml @@ -23,7 +23,7 @@ jobs: - name: Install Rust if: ${{ steps.cache-iai-results.outputs.cache-hit != 'true' }} - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 - name: Set up cargo cache if: ${{ steps.cache-iai-results.outputs.cache-hit != 'true' }} uses: Swatinem/rust-cache@v2 @@ -51,7 +51,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Install Rust - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 - name: Set up cargo cache uses: Swatinem/rust-cache@v2 - name: Python3 Build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 792a7535f2..0d85ed146c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,7 +13,7 @@ jobs: - name: Checkout sources uses: actions/checkout@v2 - name: Install stable toolchain - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 - name: Publish crate cairo-vm env: CRATES_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0efa960359..6a76308239 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -161,7 +161,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Rust - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 with: components: rustfmt, clippy - name: Set up cargo cache @@ -201,7 +201,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Rust - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 with: targets: wasm32-unknown-unknown @@ -247,7 +247,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Rust - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 with: targets: wasm32-unknown-unknown @@ -287,7 +287,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Rust - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 with: targets: wasm32-unknown-unknown @@ -323,13 +323,13 @@ jobs: strategy: fail-fast: false matrix: - special_features: ["", "extensive_hints", "mod_builtin"] + special_features: ["", "extensive_hints", "mod_builtin", "cairo-0-secp-hints", "cairo-0-data-availability-hints"] 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 steps: - name: Install Rust - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 with: components: llvm-tools-preview - name: Set up cargo cache @@ -370,7 +370,7 @@ jobs: 'test') cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}-${{ matrix.special_features }}.info \ --partition count:${PARTITION}/4 \ - --workspace --features "cairo-1-hints, test_utils, ${{ matrix.special_features }}" + --workspace --features "cairo-1-hints, test_utils, ${{ matrix.special_features }}" ;; 'test-no_std') cargo llvm-cov nextest --lcov --output-path lcov-${{ matrix.target }}-${{ matrix.special_features }}.info \ @@ -395,7 +395,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Install Rust - uses: dtolnay/rust-toolchain@1.80.0 + uses: dtolnay/rust-toolchain@1.81.0 - name: Set up cargo cache uses: Swatinem/rust-cache@v2 - name: Checkout @@ -600,6 +600,36 @@ jobs: key: codecov-cache-test-no_std-extensive_hints-${{ github.sha }} fail-on-cache-miss: true + - name: Fetch results for tests with stdlib (w/mod_builtin; part. 1) + uses: actions/cache/restore@v3 + with: + path: lcov-test#1-mod_builtin.info + key: codecov-cache-test#1-mod_builtin-${{ github.sha }} + fail-on-cache-miss: true + - name: Fetch results for tests with stdlib (w/mod_builtin; part. 2) + uses: actions/cache/restore@v3 + with: + path: lcov-test#2-mod_builtin.info + key: codecov-cache-test#2-mod_builtin-${{ github.sha }} + fail-on-cache-miss: true + - name: Fetch results for tests with stdlib (w/mod_builtin; part. 3) + uses: actions/cache/restore@v3 + with: + path: lcov-test#3-mod_builtin.info + key: codecov-cache-test#3-mod_builtin-${{ github.sha }} + fail-on-cache-miss: true + - name: Fetch results for tests with stdlib (w/mod_builtin; part. 4) + uses: actions/cache/restore@v3 + with: + path: lcov-test#4-mod_builtin.info + key: codecov-cache-test#4-mod_builtin-${{ github.sha }} + fail-on-cache-miss: true + - name: Fetch results for tests without stdlib (w/mod_builtin) + uses: actions/cache/restore@v3 + with: + path: lcov-no_std-mod_builtin.info + key: codecov-cache-test-no_std-mod_builtin-${{ github.sha }} + fail-on-cache-miss: true - name: Upload coverage to codecov.io uses: codecov/codecov-action@v3 @@ -725,6 +755,57 @@ jobs: - name: Run script run: ./vm/src/tests/compare_factorial_outputs_all_layouts.sh + compare-outputs-dynamic-layouts: + name: Compare outputs with dynamic layouts + needs: [ build-programs, build-release, run-cairo-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: Fetch pie + uses: actions/cache/restore@v3 + with: + path: | + cairo_programs/**/*.memory + cairo_programs/**/*.trace + cairo_programs/**/*.air_public_input + cairo_programs/**/*.air_private_input + cairo_programs/**/*.pie.zip + key: cairo_test_programs-release-trace-cache-${{ github.sha }} + fail-on-cache-miss: true + + - name: Run script + run: ./vm/src/tests/compare_outputs_dynamic_layouts.sh + compare-run-from-cairo-pie-all-outputs: name: Compare all outputs from running Cairo PIEs needs: [ build-programs, build-release, run-cairo-release ] @@ -763,4 +844,3 @@ jobs: - name: Run comparison run: ./vm/src/tests/compare_all_pie_outputs.sh - diff --git a/.github/workflows/test_install.yml b/.github/workflows/test_install.yml index 53b32a21ef..7f56294398 100644 --- a/.github/workflows/test_install.yml +++ b/.github/workflows/test_install.yml @@ -12,7 +12,7 @@ jobs: name: "Install on ${{ matrix.os }}" strategy: matrix: - os: [ubuntu-22.04, macos-12] + os: [ubuntu-22.04, macos-13] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index 69a905dfdf..69fa82e403 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ cairo-vm-cli/air_input.pub ensure-no_std/Cargo.lock cairo_programs/proof_programs/*.cairo +!cairo_layout_params_file.json !vm/src/tests/cairo_pie_test_output.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a88025788..a452c3732b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,72 @@ * fix: Always use a normal segment in first SegmentArena segment [#1845](https://github.com/lambdaclass/cairo-vm/pull/1845) +* feat: implement `kzg` data availability hints [#1887](https://github.com/lambdaclass/cairo-vm/pull/1887) + +#### [2.0.0-rc3] - 2024-12-26 + +* chore: update cairo-lang dependencies to 2.10.0-rc.0 #[1901](https://github.com/lambdaclass/cairo-vm/pull/1901) + +#### [2.0.0-rc2] - 2024-12-12 + +* feat: Add support for subtractions containing references as right hand side operands [#1898](https://github.com/lambdaclass/cairo-vm/pull/1898) + +* fix: Change wildcard getrandom dependency. + +* Update starknet-crypto to 0.7.3, removing the old FieldElement completly in favour of the new Felt (that is Copy). + +* chore: update the cairo-vm version used in the readme + +* chore: update cairo-lang dependencies to 2.9.2 + +* fix: replace `div_rem` with `div_mod_floor` in `verify_zero` hints [#1881](https://github.com/lambdaclass/cairo-vm/pull/1881) + +* feat: Implement `SECP related` hints [#1829](https://github.com/lambdaclass/cairo-vm/pull/1829) + +* chore: bump pip `cairo-lang` 0.13.3 [#1884](https://github.com/lambdaclass/cairo-vm/pull/1884) + +* fix: [#1862](https://github.com/lambdaclass/cairo-vm/pull/1862): + * Use MaybeRelocatable for relocation table + +* chore: bump pip `cairo-lang` 0.13.3 [#1884](https://github.com/lambdaclass/cairo-vm/pull/1884) + +* chore: [#1880](https://github.com/lambdaclass/cairo-vm/pull/1880): + * Refactor vm crate to make it possible to use hint extension feature for nested programs with hints. + +#### [2.0.0-rc1] - 2024-11-20 + +* feat: add `EvalCircuit` and `TestLessThanOrEqualAddress` hints [#1843](https://github.com/lambdaclass/cairo-vm/pull/1843) + +* fix: [#1873](https://github.com/lambdaclass/cairo-vm/pull/1873) + * Fix broken num-prime `is_prime` call +* fix: [#1868](https://github.com/lambdaclass/cairo-vm/pull/1855): + * Adds logic to include the 3 new builtins in `builtin_segments` when serializing the output cairo pie's metadata. + +* fix: [#1855](https://github.com/lambdaclass/cairo-vm/pull/1855): + * Adds logic to skip pedersen additional data comparison when checking pie compatibility. + +* serde: add `size` field to `Identifier` [#1861]https://github.com/lambdaclass/cairo-vm/pull/1861 + +#### [2.0.0-rc0] - 2024-10-22 + +* fix: [#1864](https://github.com/lambdaclass/cairo-vm/pull/1864): + * Runner: include data from constants segment to the bytecode when assembling program + +* chore: bump `cairo-lang-` dependencies to 2.9.0-dev.0 [#1858](https://github.com/lambdaclass/cairo-vm/pull/1858/files) + +* chore: update Rust required version to 1.81.0 [#1857](https://github.com/lambdaclass/cairo-vm/pull/1857) + +* fix: [#1851](https://github.com/lambdaclass/cairo-vm/pull/1851): + * Fix unsorted signature and mod builtin outputs in air_private_input. + +* feat(BREAKING): [#1824](https://github.com/lambdaclass/cairo-vm/pull/1824)[#1838](https://github.com/lambdaclass/cairo-vm/pull/1838): + * Add support for dynamic layout + * CLI change(BREAKING): The flag `cairo_layout_params_file` must be specified when using dynamic layout. + * Signature change(BREAKING): Both `CairoRunner::new` and `CairoRunner::new_v2` now receive an `Option`, used only with dynamic layout. + +* fix: [#1841](https://github.com/lambdaclass/cairo-vm/pull/1841): + * Fix modulo builtin to comply with prover constraints + * chore: bump pip `cairo-lang` 0.13.2 [#1827](https://github.com/lambdaclass/cairo-vm/pull/1827) * chore: bump `cairo-lang-` dependencies to 2.8.0 [#1833](https://github.com/lambdaclass/cairo-vm/pull/1833/files) diff --git a/CODEOWNERS b/CODEOWNERS index 4e1812b071..4f9f94059e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @igaray @Oppen @fmoletta @juanbono @pefontana +* @igaray @Oppen @fmoletta @juanbono @pefontana @gabrielbosio diff --git a/Cargo.lock b/Cargo.lock index 03c0cb9c47..a97304a19d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,19 +4,13 @@ version = 3 [[package]] name = "addr2line" -version = "0.22.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.0" @@ -61,9 +55,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.18" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anes" @@ -73,9 +67,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", @@ -88,49 +82,49 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.86" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" dependencies = [ "derive_arbitrary", ] @@ -149,7 +143,7 @@ dependencies = [ "digest", "itertools 0.10.5", "num-bigint", - "num-traits 0.2.19", + "num-traits", "paste", "rustc_version", "zeroize", @@ -172,7 +166,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" dependencies = [ "num-bigint", - "num-traits 0.2.19", + "num-traits", "proc-macro2", "quote", "syn 1.0.109", @@ -195,7 +189,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ - "num-traits 0.2.19", + "num-traits", "rand", ] @@ -222,9 +216,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.4.12" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" +checksum = "df895a515f70646414f4b45c0b79082783b80552b373a68283012928df56f522" dependencies = [ "brotli", "flate2", @@ -238,20 +232,20 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] name = "autocfg" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" @@ -304,17 +298,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.73" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", - "miniz_oxide 0.7.4", + "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] @@ -338,7 +332,16 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" dependencies = [ - "bit-vec", + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", ] [[package]] @@ -347,6 +350,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "1.3.2" @@ -382,9 +391,9 @@ dependencies = [ [[package]] name = "brotli" -version = "6.0.0" +version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" +checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -403,9 +412,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "786a307d683a5bf92e6fd5fd69a7eb613751668d1d8d67d802846dfe367c62c8" dependencies = [ "memchr", "serde", @@ -431,29 +440,29 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.7.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] name = "cairo-lang-casm" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9e8fe95ee2add1537d00467b98bb8928334633eb01dcba7f33fb64769af259" +checksum = "31a9a437bd4015a0f888d0de9876fd4786eb24b4e17b25c86c53980865980f9d" dependencies = [ "cairo-lang-utils", "indoc", "num-bigint", - "num-traits 0.2.19", + "num-traits", "parity-scale-codec", "serde", ] [[package]] name = "cairo-lang-compiler" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0db1ae47b4918a894b60160fac42e6fbcb5a8c0023dd6c290ba03a1bcdf5a554" +checksum = "4608693f366e8e86061c824adaca33b56bf0bd7e1cd5edc8592be7a380950322" dependencies = [ "anyhow", "cairo-lang-defs", @@ -477,18 +486,18 @@ dependencies = [ [[package]] name = "cairo-lang-debug" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c87b905b74516c33fc7e6d61b5243363ce65133054c30bd9531f47e30ca201" +checksum = "4217fd373449a74efde259f8a4df501a7664f6f9c73b547c3aff632ad14feabf" dependencies = [ "cairo-lang-utils", ] [[package]] name = "cairo-lang-defs" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611996d85ec608bfec75d546a5c2ec44f664f4bd2514840a5b369d30a1a8bfdb" +checksum = "dadf2d1002c9851ea17d37b83a26bbe6f0f53d5f94ba2e477a7a6e9d498f805b" dependencies = [ "cairo-lang-debug", "cairo-lang-diagnostics", @@ -503,9 +512,9 @@ dependencies = [ [[package]] name = "cairo-lang-diagnostics" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d015a0790b1f5de8b22b4b4b60d392c35bed07b7aa9dd22361af2793835cee51" +checksum = "4c4ac1831e7c14e5308a66254bcc57a8d4790f18567ef8d4d6768b39ffb955a0" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", @@ -515,9 +524,9 @@ dependencies = [ [[package]] name = "cairo-lang-eq-solver" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c580e56e5857d51b6bf2ec5ed5fdd33fd3b74dad7e3cb6d7398396174a6c85" +checksum = "33c5f879bca42caef7e06f1de022d6961d36c5567db600faed8a947e2b705eaa" dependencies = [ "cairo-lang-utils", "good_lp", @@ -525,9 +534,9 @@ dependencies = [ [[package]] name = "cairo-lang-filesystem" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5368e66a742b8532d656171525bfea599490280ceee10bdac93ad60775fc4e59" +checksum = "c632b6d3ee5f1684f757f86e04ba9cf1daa8e4e74c6a5d6c0b7773cc4465a6c6" dependencies = [ "cairo-lang-debug", "cairo-lang-utils", @@ -536,13 +545,14 @@ dependencies = [ "semver", "serde", "smol_str", + "toml", ] [[package]] name = "cairo-lang-formatter" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1200324728e7f4c4acedceee427d9b3ffce221af57e469a454f007cbc248255" +checksum = "8295a3ddc62d8d92d942292f103de0434d622f47e936a3aea25eccc3eed88c58" dependencies = [ "anyhow", "cairo-lang-diagnostics", @@ -555,15 +565,14 @@ dependencies = [ "itertools 0.12.1", "rust-analyzer-salsa", "serde", - "smol_str", "thiserror", ] [[package]] name = "cairo-lang-lowering" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a7a3069c75e1aca7cf15f20d03baf71f5c86e5be26988f6c25656549aa8b54a" +checksum = "b2a93c2644c64cfdbbe64b1bd6e13d9c6ed511950cfae2e738d228bc89dc5605" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -578,16 +587,17 @@ dependencies = [ "itertools 0.12.1", "log", "num-bigint", - "num-traits 0.2.19", + "num-integer", + "num-traits", "rust-analyzer-salsa", "smol_str", ] [[package]] name = "cairo-lang-parser" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c13b245ddc740ebfed8b05e1bdb7805a06d267cf89d46486c9609306f92d45ce" +checksum = "d884d9418895fef5b8ffd7458211523ab5abf4357845938b354adfbae1089fe2" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -597,7 +607,7 @@ dependencies = [ "colored", "itertools 0.12.1", "num-bigint", - "num-traits 0.2.19", + "num-traits", "rust-analyzer-salsa", "smol_str", "unescaper", @@ -605,9 +615,9 @@ dependencies = [ [[package]] name = "cairo-lang-plugins" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b656552d0ab4a69be223e42c4e1c4028e512f506a237d04bbe4ccab9a1e13c5" +checksum = "b224afdc76d9890edf9009fa8ffff9a91ac15614a41049d48c77c192e8c966e3" dependencies = [ "cairo-lang-defs", "cairo-lang-diagnostics", @@ -622,36 +632,41 @@ dependencies = [ "smol_str", ] +[[package]] +name = "cairo-lang-primitive-token" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "123ac0ecadf31bacae77436d72b88fa9caef2b8e92c89ce63a125ae911a12fae" + [[package]] name = "cairo-lang-proc-macros" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05cc6adb49faa42ea825e041dff0496c2e72e4ddaf50734062a62383c0c8adbf" +checksum = "0ae35a282e5f2d15f47ba6fffae5a32e8aa2f254366865339cf326e2e015b8f8" dependencies = [ "cairo-lang-debug", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] name = "cairo-lang-project" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad123ba0e0dd5e1ea80977c0244ff4b0b6d8bf050d42ecb5ff0cf7f885e871f9" +checksum = "4da40ba380208db0b861d8b1e6e558adaa98dd0b382177b25bdb1abf8fd8d766" dependencies = [ "cairo-lang-filesystem", "cairo-lang-utils", "serde", - "smol_str", "thiserror", "toml", ] [[package]] name = "cairo-lang-semantic" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d528c79e4ff3e1364569c07e22660ddf60c0d1989705b8f0feed9949962b28a" +checksum = "babdf14729236dfb455519d35e7e399ba73f0eaa4f1e929474d4c37dc9ef7a29" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -667,17 +682,18 @@ dependencies = [ "indoc", "itertools 0.12.1", "num-bigint", - "num-traits 0.2.19", + "num-traits", "rust-analyzer-salsa", + "sha3", "smol_str", "toml", ] [[package]] name = "cairo-lang-sierra" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bdb0c2cc419f45ab7e413322502ca02c2a2c56aeabdd0885e3740f378d8b269" +checksum = "74c0e4951ecd88023856e0faa9fd444647d9f1ec69ca09dfa8e3aebf9d2afdef" dependencies = [ "anyhow", "cairo-lang-utils", @@ -689,7 +705,7 @@ dependencies = [ "lalrpop-util", "num-bigint", "num-integer", - "num-traits 0.2.19", + "num-traits", "regex", "rust-analyzer-salsa", "serde", @@ -702,9 +718,9 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-ap-change" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7224cd827ccf69e742c90a60278876865a96b545a101248d9472d2e02f9190b3" +checksum = "9ea9c51356e603fa38fcbd4524d19e391ac25e89e64889c3a4ef849de3d1e911" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -712,15 +728,15 @@ dependencies = [ "cairo-lang-utils", "itertools 0.12.1", "num-bigint", - "num-traits 0.2.19", + "num-traits", "thiserror", ] [[package]] name = "cairo-lang-sierra-gas" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e379e3010827fe983e66aa38a0d25fe24cfc11eaf8cadf4dc7bcb31fff031de" +checksum = "1af17244a222fd2398caaf09e909f0b584abe14c77e1b5dc8f479ef35d1e8d50" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -728,15 +744,15 @@ dependencies = [ "cairo-lang-utils", "itertools 0.12.1", "num-bigint", - "num-traits 0.2.19", + "num-traits", "thiserror", ] [[package]] name = "cairo-lang-sierra-generator" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6b353930676c06bb885a16ec3b120109aa15539c49f41b3370a5a6314dc29dc" +checksum = "2368d57175b18976222f844e4dda52d0025d70ce8b2dda35e0cc96efaa9bb4a5" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -749,7 +765,7 @@ dependencies = [ "cairo-lang-syntax", "cairo-lang-utils", "itertools 0.12.1", - "num-traits 0.2.19", + "num-traits", "rust-analyzer-salsa", "serde", "serde_json", @@ -758,9 +774,9 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-to-casm" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83873751d489aae4674f3d755a4897429a664bdc4b0847283e13889f0b0c2a44" +checksum = "866e6cbba9e81bae1c2f6b8f8e718f702fee69980c3f22bdea1e4657f09a540c" dependencies = [ "assert_matches", "cairo-lang-casm", @@ -772,16 +788,16 @@ dependencies = [ "indoc", "itertools 0.12.1", "num-bigint", - "num-traits 0.2.19", + "num-traits", "starknet-types-core", "thiserror", ] [[package]] name = "cairo-lang-sierra-type-size" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd84b445715326e44832836732b6bda76a119116b296ac9b6b87e2a4177634a" +checksum = "f8e797aa2f4023e984d13c5adf7068688da665328da6b055842f50fb673fb48b" dependencies = [ "cairo-lang-sierra", "cairo-lang-utils", @@ -789,9 +805,9 @@ dependencies = [ [[package]] name = "cairo-lang-starknet" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8df3086f909d27a49d6706be835725df4e21fb50efe699cd763d1f782a31dea" +checksum = "85c16967be9b0befaa0e21f65c9c803f8354d0db09de7adf28cdf0dc54b2c90d" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -819,9 +835,9 @@ dependencies = [ [[package]] name = "cairo-lang-starknet-classes" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41bcab650779b3431389dc52f1e643a7c9690a1aa2b072c8f01955503d094007" +checksum = "b0f7b0c28430c9ad477c38dba089ac5a148443bbeaa77cc3f14980de255a220e" dependencies = [ "cairo-lang-casm", "cairo-lang-sierra", @@ -831,7 +847,7 @@ dependencies = [ "itertools 0.12.1", "num-bigint", "num-integer", - "num-traits 0.2.19", + "num-traits", "serde", "serde_json", "sha3", @@ -842,15 +858,16 @@ dependencies = [ [[package]] name = "cairo-lang-syntax" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e2dc876ec02a197b8d13dbfc0b2cf7a7e31dcfc6446761cbb85f5b42d589cdc" +checksum = "f5adf933d378225e031200bd41c82e09cb0fde1042f5fe3f3c82d1ccd559a6f2" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", + "cairo-lang-primitive-token", "cairo-lang-utils", "num-bigint", - "num-traits 0.2.19", + "num-traits", "rust-analyzer-salsa", "smol_str", "unescaper", @@ -858,9 +875,9 @@ dependencies = [ [[package]] name = "cairo-lang-syntax-codegen" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8727fe3f24ec0834ec6656c70a59f85233439f0a09ca53cf5e27fbdb1b40193" +checksum = "4b34c81e723cf05fc0655aa8527f5a2730e30b31368b1b887c3eeb50a00c81c4" dependencies = [ "genco", "xshell", @@ -868,9 +885,9 @@ dependencies = [ [[package]] name = "cairo-lang-test-utils" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a7681562268173d74b1c8d2438a1d9ec3218c89a8e39a8be3f10e044fa46ebe" +checksum = "aa7c6930beb6f221c1bc274460fca091e93c377570994b612682da30795ed74e" dependencies = [ "cairo-lang-formatter", "cairo-lang-utils", @@ -881,22 +898,22 @@ dependencies = [ [[package]] name = "cairo-lang-utils" -version = "2.8.0" +version = "2.10.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e6004780c42bf28ce5afd048cc628b3de34aaf24fd2c228ae73217c58999f9" +checksum = "91b6546d9f285c7d4a2700c084f745c35e1884a1dc2e4fa54a71034cea5606aa" dependencies = [ "hashbrown 0.14.5", - "indexmap 2.5.0", + "indexmap 2.7.0", "itertools 0.12.1", "num-bigint", - "num-traits 0.2.19", + "num-traits", "schemars", "serde", ] [[package]] name = "cairo-vm" -version = "1.0.1" +version = "2.0.0-rc3" dependencies = [ "anyhow", "arbitrary", @@ -911,7 +928,8 @@ dependencies = [ "clap", "criterion", "generic-array", - "hashbrown 0.14.5", + "getrandom", + "hashbrown 0.15.2", "hex", "iai-callgrind", "keccak", @@ -921,7 +939,7 @@ dependencies = [ "num-bigint", "num-integer", "num-prime", - "num-traits 0.2.19", + "num-traits", "proptest", "rand", "rstest", @@ -940,7 +958,7 @@ dependencies = [ [[package]] name = "cairo-vm-cli" -version = "1.0.1" +version = "2.0.0-rc3" dependencies = [ "assert_matches", "bincode", @@ -955,14 +973,14 @@ dependencies = [ [[package]] name = "cairo-vm-tracer" -version = "1.0.1" +version = "2.0.0-rc3" dependencies = [ "axum", "cairo-vm", "include_dir", "mime_guess", "num-bigint", - "num-traits 0.2.19", + "num-traits", "serde", "thiserror-no-std", "tokio", @@ -974,7 +992,7 @@ dependencies = [ [[package]] name = "cairo1-run" -version = "1.0.1" +version = "2.0.0-rc3" dependencies = [ "assert_matches", "bincode", @@ -992,7 +1010,7 @@ dependencies = [ "itertools 0.11.0", "mimalloc", "num-bigint", - "num-traits 0.2.19", + "num-traits", "rstest", "serde_json", "thiserror", @@ -1006,9 +1024,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.15" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57b6a275aa2903740dc87da01c62040406b8812552e97129a63ea8850a17c6e6" +checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e" dependencies = [ "jobserver", "libc", @@ -1050,9 +1068,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.16" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed6719fffa43d0d87e5fd8caeab59be1554fb028cd30edc88fc4369b17971019" +checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" dependencies = [ "clap_builder", "clap_derive", @@ -1060,9 +1078,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.15" +version = "4.5.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "216aec2b177652e3846684cbfe25c9964d18ec45234f0f5da5157b207ed1aab6" +checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" dependencies = [ "anstream", "anstyle", @@ -1072,36 +1090,36 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.13" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "colored" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -1122,18 +1140,18 @@ checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" [[package]] name = "const_format" -version = "0.2.32" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.32" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ "proc-macro2", "quote", @@ -1151,9 +1169,9 @@ dependencies = [ [[package]] name = "cpufeatures" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] @@ -1180,7 +1198,7 @@ dependencies = [ "criterion-plot", "is-terminal", "itertools 0.10.5", - "num-traits 0.2.19", + "num-traits", "once_cell", "oorandom", "plotters", @@ -1205,9 +1223,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1224,9 +1242,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" @@ -1268,13 +1286,13 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.3.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] @@ -1353,19 +1371,19 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "fastrand" -version = "2.1.1" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fixedbitset" @@ -1375,12 +1393,12 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide", ] [[package]] @@ -1389,6 +1407,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1406,9 +1430,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1421,9 +1445,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1431,15 +1455,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1448,32 +1472,32 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-timer" @@ -1483,9 +1507,9 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1501,9 +1525,9 @@ dependencies = [ [[package]] name = "genco" -version = "0.17.9" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afac3cbb14db69ac9fef9cdb60d8a87e39a7a527f85a81a923436efa40ad42c6" +checksum = "a35958104272e516c2a5f66a9d82fba4784d2b585fc1e2358b8f96e15d342995" dependencies = [ "genco-macros", "relative-path", @@ -1512,13 +1536,13 @@ dependencies = [ [[package]] name = "genco-macros" -version = "0.17.9" +version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "553630feadf7b76442b0849fd25fdf89b860d933623aec9693fed19af0400c78" +checksum = "43eaff6bbc0b3a878361aced5ec6a2818ee7c541c5b33b5880dfa9a86c23e9e7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] @@ -1546,15 +1570,15 @@ dependencies = [ [[package]] name = "gimli" -version = "0.29.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "globset" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" dependencies = [ "aho-corasick", "bstr", @@ -1565,12 +1589,12 @@ dependencies = [ [[package]] name = "good_lp" -version = "1.8.1" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3198bd13dea84c76a64621d6ee8ee26a4960a9a0d538eca95ca8f1320a469ac9" +checksum = "97630e1e456d7081c524488a87d8f8f7ed0fd3100ba10c55e3cfa7add5ce05c6" dependencies = [ "fnv", - "minilp", + "microlp", ] [[package]] @@ -1600,6 +1624,18 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", + "serde", +] + [[package]] name = "heck" version = "0.4.1" @@ -1612,12 +1648,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.4.0" @@ -1632,7 +1662,7 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hint_accountant" -version = "1.0.1" +version = "2.0.0-rc3" dependencies = [ "cairo-vm", "serde", @@ -1678,9 +1708,9 @@ checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" [[package]] name = "httparse" -version = "1.9.4" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -1690,9 +1720,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.30" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ "bytes", "futures-channel", @@ -1713,7 +1743,7 @@ dependencies = [ [[package]] name = "hyper_threading" -version = "1.0.1" +version = "2.0.0-rc3" dependencies = [ "cairo-vm", "rayon", @@ -1734,9 +1764,9 @@ checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005" [[package]] name = "ignore" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b" dependencies = [ "crossbeam-deque", "globset", @@ -1750,13 +1780,13 @@ dependencies = [ [[package]] name = "impl-trait-for-tuples" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.91", ] [[package]] @@ -1797,12 +1827,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "serde", ] @@ -1814,9 +1844,9 @@ checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" [[package]] name = "iri-string" -version = "0.7.2" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f5f6c2df22c009ac44f6f1499308e7a3ac7ba42cd2378475cc691510e1eef1b" +checksum = "dc0f0a572e8ffe56e2ff4f769f32ffe919282c3916799f8b68688b6030063bea" dependencies = [ "memchr", "serde", @@ -1828,7 +1858,7 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" dependencies = [ - "hermit-abi 0.4.0", + "hermit-abi", "libc", "windows-sys 0.52.0", ] @@ -1868,9 +1898,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.11" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" [[package]] name = "jobserver" @@ -1906,7 +1936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55cb077ad656299f160924eb2912aa147d7339ea7d69e1b5517326fdcec3c1ca" dependencies = [ "ascii-canvas", - "bit-set", + "bit-set 0.5.3", "ena", "itertools 0.11.0", "lalrpop-util", @@ -1932,9 +1962,9 @@ dependencies = [ [[package]] name = "lambdaworks-crypto" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb5d4f22241504f7c7b8d2c3a7d7835d7c07117f10bff2a7d96a9ef6ef217c3" +checksum = "bbc2a4da0d9e52ccfe6306801a112e81a8fc0c76aa3e4449fefeda7fef72bb34" dependencies = [ "lambdaworks-math", "serde", @@ -1944,9 +1974,9 @@ dependencies = [ [[package]] name = "lambdaworks-math" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "358e172628e713b80a530a59654154bfc45783a6ed70ea284839800cebdf8f97" +checksum = "d1bd2632acbd9957afc5aeec07ad39f078ae38656654043bf16e046fa2730e23" dependencies = [ "serde", "serde_json", @@ -1963,15 +1993,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.158" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" - -[[package]] -name = "libm" -version = "0.2.8" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libmimalloc-sys" @@ -2017,11 +2041,11 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -2032,10 +2056,11 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "matrixmultiply" -version = "0.2.4" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916806ba0031cd542105d916a97c8572e1fa6dd79c9c51e7eb43a09ec2dd84c1" +checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" dependencies = [ + "autocfg", "rawpointer", ] @@ -2045,6 +2070,16 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "microlp" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4d5845c04529928925fd1abd6776813710ad57ef41cb31958b1d35622a7d8e9" +dependencies = [ + "log", + "sprs", +] + [[package]] name = "mimalloc" version = "0.1.43" @@ -2070,16 +2105,6 @@ dependencies = [ "unicase", ] -[[package]] -name = "minilp" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a7750a9e5076c660b7bec5e6457b4dbff402b9863c8d112891434e18fd5385" -dependencies = [ - "log", - "sprs", -] - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -2088,29 +2113,19 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi 0.3.9", "libc", "wasi", "windows-sys 0.52.0", @@ -2118,14 +2133,16 @@ dependencies = [ [[package]] name = "ndarray" -version = "0.13.1" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac06db03ec2f46ee0ecdca1a1c34a99c0d188a0d83439b84bf0cb4b386e4ab09" +checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841" dependencies = [ "matrixmultiply", "num-complex", "num-integer", - "num-traits 0.2.19", + "num-traits", + "portable-atomic", + "portable-atomic-util", "rawpointer", ] @@ -2162,19 +2179,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", - "num-traits 0.2.19", + "num-traits", "rand", "serde", ] [[package]] name = "num-complex" -version = "0.2.4" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ - "autocfg", - "num-traits 0.2.19", + "num-traits", ] [[package]] @@ -2183,7 +2199,7 @@ version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "num-traits 0.2.19", + "num-traits", ] [[package]] @@ -2194,7 +2210,7 @@ checksum = "64a5fe11d4135c3bcdf3a95b18b194afa9608a5f6ff034f5d857bc9a27fb0119" dependencies = [ "num-bigint", "num-integer", - "num-traits 0.2.19", + "num-traits", ] [[package]] @@ -2209,19 +2225,10 @@ dependencies = [ "num-bigint", "num-integer", "num-modular", - "num-traits 0.2.19", + "num-traits", "rand", ] -[[package]] -name = "num-traits" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" -dependencies = [ - "num-traits 0.2.19", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -2229,23 +2236,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", - "libm", ] [[package]] name = "object" -version = "0.36.4" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "oorandom" @@ -2305,7 +2311,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2333,7 +2339,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.5.0", + "indexmap 2.7.0", ] [[package]] @@ -2353,29 +2359,29 @@ checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -2385,17 +2391,17 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "plotters" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" dependencies = [ - "num-traits 0.2.19", + "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", @@ -2404,19 +2410,34 @@ dependencies = [ [[package]] name = "plotters-backend" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" [[package]] name = "plotters-svg" -version = "0.3.6" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b30686a7d9c3e010b84284bdd26a29f2138574f52f5eb6f794fc0ad924e705" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" dependencies = [ "plotters-backend", ] +[[package]] +name = "portable-atomic" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "ppv-lite86" version = "0.2.20" @@ -2434,9 +2455,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", @@ -2453,24 +2474,24 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "proptest" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +checksum = "14cae93065090804185d3b75f0bf93b8eeda30c7a9b4a33d3bdb3988d6229e50" dependencies = [ - "bit-set", - "bit-vec", + "bit-set 0.8.0", + "bit-vec 0.8.0", "bitflags 2.6.0", "lazy_static", - "num-traits 0.2.19", + "num-traits", "rand", "rand_chacha", "rand_xorshift", @@ -2488,9 +2509,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] @@ -2568,9 +2589,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.3" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -2588,9 +2609,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.6" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -2600,9 +2621,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -2611,9 +2632,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "relative-path" @@ -2663,7 +2684,7 @@ version = "0.17.0-pre.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719825638c59fd26a55412a24561c7c5bcf54364c88b9a7a04ba08a6eafaba8d" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.7.0", "lock_api", "oorandom", "parking_lot", @@ -2683,7 +2704,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] @@ -2693,7 +2714,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" dependencies = [ "arrayvec", - "num-traits 0.2.19", + "num-traits", ] [[package]] @@ -2719,22 +2740,22 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.35" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a85d50532239da68e9addb745ba38ff4612a242c1c7ceea689c4bc7c2f43c36f" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "rusty-fork" @@ -2785,7 +2806,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] @@ -2802,31 +2823,31 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +checksum = "3cb6eb87a131f756572d7fb904f6e7b68633f09cca868c5df1c4b8d1a694bbba" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.209" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99fce0ffe7310761ca6bf9faf5115afbc19688edd00171d81b1bb1b116c63e09" +checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.209" +version = "1.0.216" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5831b979fd7b5439637af1752d535ff49f4860c0f341d1baeb6faf0f4242170" +checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] @@ -2837,14 +2858,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] name = "serde_json" -version = "1.0.127" +version = "1.0.134" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" +checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" dependencies = [ "itoa", "memchr", @@ -2864,9 +2885,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -2951,9 +2972,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", "windows-sys 0.52.0", @@ -2967,13 +2988,14 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "sprs" -version = "0.7.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec63571489873d4506683915840eeb1bb16b3198ee4894cc6f2fe3013d505e56" +checksum = "704ef26d974e8a452313ed629828cd9d4e4fa34667ca1ad9d6b1fffa43c6e166" dependencies = [ "ndarray", "num-complex", - "num-traits 0.1.43", + "num-traits", + "smallvec", ] [[package]] @@ -2984,61 +3006,37 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "starknet-crypto" -version = "0.6.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e2c30c01e8eb0fc913c4ee3cf676389fffc1d1182bfe5bb9670e4e72e968064" +checksum = "ded22ccf4cb9e572ce3f77de6066af53560cd2520d508876c83bb1e6b29d5cbc" dependencies = [ "crypto-bigint", "hex", "hmac", "num-bigint", "num-integer", - "num-traits 0.2.19", + "num-traits", "rfc6979", "sha2", - "starknet-crypto-codegen", "starknet-curve", - "starknet-ff", + "starknet-types-core", "zeroize", ] -[[package]] -name = "starknet-crypto-codegen" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc159a1934c7be9761c237333a57febe060ace2bc9e3b337a59a37af206d19f" -dependencies = [ - "starknet-curve", - "starknet-ff", - "syn 2.0.77", -] - [[package]] name = "starknet-curve" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1c383518bb312751e4be80f53e8644034aa99a0afb29d7ac41b89a997db875b" -dependencies = [ - "starknet-ff", -] - -[[package]] -name = "starknet-ff" -version = "0.3.7" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abf1b44ec5b18d87c1ae5f54590ca9d0699ef4dd5b2ffa66fc97f24613ec585" +checksum = "bcde6bd74269b8161948190ace6cf069ef20ac6e79cd2ba09b320efa7500b6de" dependencies = [ - "ark-ff", - "crypto-bigint", - "getrandom", - "hex", + "starknet-types-core", ] [[package]] name = "starknet-types-core" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6bacf0ba19bc721e518bc4bf389ff13daa8a7c5db5fd320600473b8aa9fcbd" +checksum = "fa1b9e01ccb217ab6d475c5cda05dbb22c30029f7bb52b192a010a00d77a3d74" dependencies = [ "arbitrary", "lambdaworks-crypto", @@ -3046,7 +3044,7 @@ dependencies = [ "lazy_static", "num-bigint", "num-integer", - "num-traits 0.2.19", + "num-traits", "serde", ] @@ -3088,9 +3086,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" dependencies = [ "proc-macro2", "quote", @@ -3111,9 +3109,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tempfile" -version = "3.12.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c" dependencies = [ "cfg-if", "fastrand", @@ -3135,22 +3133,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.63" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] @@ -3204,9 +3202,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.40.0" +version = "1.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" dependencies = [ "backtrace", "bytes", @@ -3226,14 +3224,14 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -3265,11 +3263,11 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.20" +version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ - "indexmap 2.5.0", + "indexmap 2.7.0", "serde", "serde_spanned", "toml_datetime", @@ -3336,9 +3334,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -3348,20 +3346,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -3380,9 +3378,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -3394,9 +3392,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6631e42e10b40c0690bf92f404ebcfe6e1fdb480391d15f17cc8e96eeed5369" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" dependencies = [ "serde", "stable_deref_trait", @@ -3431,30 +3429,27 @@ dependencies = [ [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-xid" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "utf8parse" @@ -3464,9 +3459,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" +checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ "getrandom", ] @@ -3538,7 +3533,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", "wasm-bindgen-shared", ] @@ -3572,7 +3567,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3605,12 +3600,12 @@ checksum = "b7f89739351a2e03cb94beb799d47fb2cac01759b40ec441f7de39b00cbf7ef0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] name = "wasm-demo" -version = "1.0.1" +version = "2.0.0-rc3" dependencies = [ "cairo-vm", "console_error_panic_hook", @@ -3660,22 +3655,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3684,22 +3670,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-targets", ] [[package]] @@ -3708,46 +3679,28 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3760,48 +3713,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -3810,9 +3739,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.18" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] @@ -3828,24 +3757,24 @@ dependencies = [ [[package]] name = "xshell" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437" +checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d" dependencies = [ "xshell-macros", ] [[package]] name = "xshell-macros" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852" +checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547" [[package]] name = "yansi" -version = "0.5.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" @@ -3865,7 +3794,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] @@ -3885,7 +3814,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.91", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 64a4af3e09..2a9121eea1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = ["ensure-no_std"] resolver = "2" [workspace.package] -version = "1.0.1" +version = "2.0.0-rc3" edition = "2021" license = "Apache-2.0" repository = "https://github.com/lambdaclass/cairo-vm/" @@ -24,8 +24,8 @@ readme = "README.md" keywords = ["starknet", "cairo", "vm", "wasm", "no_std"] [workspace.dependencies] -cairo-vm = { path = "./vm", version = "1.0.1", default-features = false } -cairo-vm-tracer = { path = "./cairo-vm-tracer", version = "1.0.1", default-features = false } +cairo-vm = { path = "./vm", version = "2.0.0-rc3", default-features = false } +cairo-vm-tracer = { path = "./cairo-vm-tracer", version = "2.0.0-rc3", default-features = false } mimalloc = { version = "0.1.37", default-features = false } num-bigint = { version = "0.4", default-features = false, features = [ "serde", @@ -43,7 +43,7 @@ hex = { version = "0.4.3", default-features = false } bincode = { version = "2.0.0-rc.3", default-features = false, features = [ "serde", ] } -starknet-crypto = { version = "0.6.1", default-features = false, features = [ +starknet-crypto = { version = "0.7.3", default-features = false, features = [ "signature-display", "alloc", ] } @@ -55,22 +55,22 @@ nom = { version = "7", default-features = false } sha2 = { version = "0.10.7", features = ["compress"], default-features = false } generic-array = { version = "0.14.7", default-features = false } keccak = { version = "0.1.2", default-features = false } -hashbrown = { version = "0.14.0", features = ["serde"] } -anyhow = { version = "1.0.69", default-features = false } +hashbrown = { version = "0.15.2", features = ["serde"] } +anyhow = { version = "1.0.94", default-features = false } thiserror-no-std = { version = "2.0.2", default-features = false } bitvec = { version = "1", default-features = false, features = ["alloc"] } # Dependencies for cairo-1-hints feature -cairo-lang-starknet = { version = "2.8.0", default-features = false } -cairo-lang-casm = { version = "2.8.0", default-features = false } +cairo-lang-starknet = { version = "2.10.0-rc.0", default-features = false } +cairo-lang-casm = { version = "2.10.0-rc.0", default-features = false } -cairo-lang-starknet-classes = { version = "2.8.0", default-features = false } -cairo-lang-compiler = { version = "=2.8.0", default-features = false } -cairo-lang-sierra-to-casm = { version = "2.8.0", default-features = false } -cairo-lang-sierra = { version = "2.8.0", default-features = false } -cairo-lang-runner = { version = "2.8.0", default-features = false } -cairo-lang-utils = { version = "=2.8.0", default-features = false } +cairo-lang-starknet-classes = { version = "2.10.0-rc.0", default-features = false } +cairo-lang-compiler = { version = "=2.10.0-rc.0", default-features = false } +cairo-lang-sierra-to-casm = { version = "2.10.0-rc.0", default-features = false } +cairo-lang-sierra = { version = "2.10.0-rc.0", default-features = false } +cairo-lang-runner = { version = "2.10.0-rc.0", default-features = false } +cairo-lang-utils = { version = "=2.10.0-rc.0", default-features = false } # TODO: check these dependencies for wasm compatibility ark-ff = { version = "0.4.2", default-features = false } diff --git a/Makefile b/Makefile index 06cba4b0f1..9b60105ab3 100644 --- a/Makefile +++ b/Makefile @@ -90,6 +90,14 @@ BAD_TEST_DIR=cairo_programs/bad_programs BAD_TEST_FILES:=$(wildcard $(BAD_TEST_DIR)/*.cairo) COMPILED_BAD_TESTS:=$(patsubst $(BAD_TEST_DIR)/%.cairo, $(BAD_TEST_DIR)/%.json, $(BAD_TEST_FILES)) +SECP_CAIRO0_HINTS_DIR=cairo_programs/cairo-0-secp-hints-feature +SECP_CAIRO0_HINTS_FILES:=$(wildcard $(SECP_CAIRO0_HINTS_DIR)/*.cairo) +COMPILED_SECP_CAIRO0_HINTS:=$(patsubst $(SECP_CAIRO0_HINTS_DIR)/%.cairo, $(SECP_CAIRO0_HINTS_DIR)/%.json, $(SECP_CAIRO0_HINTS_FILES)) + +KZG_DA_CAIRO0_HINTS_DIR=cairo_programs/cairo-0-kzg-da-hints +KZG_DA_CAIRO0_HINTS_FILES:=$(wildcard $(KZG_DA_CAIRO0_HINTS_DIR)/*.cairo) +COMPILED_KZG_DA_CAIRO0_HINTS:=$(patsubst $(KZG_DA_CAIRO0_HINTS_DIR)/%.cairo, $(KZG_DA_CAIRO0_HINTS_DIR)/%.json, $(KZG_DA_CAIRO0_HINTS_FILES)) + 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)) @@ -180,7 +188,7 @@ $(CAIRO_2_CONTRACTS_TEST_DIR)/%.casm: $(CAIRO_2_CONTRACTS_TEST_DIR)/%.sierra # ====================== CAIRO_2_REPO_DIR = cairo2 -CAIRO_2_VERSION = 2.8.0 +CAIRO_2_VERSION = 2.10.0-rc.0 build-cairo-2-compiler-macos: @if [ ! -d "$(CAIRO_2_REPO_DIR)" ]; then \ @@ -239,7 +247,7 @@ run: check: cargo check -cairo_test_programs: $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COMPILED_NORETROCOMPAT_TESTS) $(COMPILED_PRINT_TESTS) $(COMPILED_MOD_BUILTIN_TESTS) +cairo_test_programs: $(COMPILED_TESTS) $(COMPILED_BAD_TESTS) $(COMPILED_NORETROCOMPAT_TESTS) $(COMPILED_PRINT_TESTS) $(COMPILED_MOD_BUILTIN_TESTS) $(COMPILED_SECP_CAIRO0_HINTS) $(COMPILED_KZG_DA_CAIRO0_HINTS) 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) @@ -264,7 +272,7 @@ test-wasm: cairo_proof_programs cairo_test_programs # NOTE: release mode is needed to avoid "too many locals" error wasm-pack test --release --node vm --no-default-features test-extensive_hints: cairo_proof_programs cairo_test_programs - $(TEST_COMMAND) --workspace --features "test_utils, cairo-1-hints, extensive_hints" + $(TEST_COMMAND) --workspace --features "test_utils, cairo-1-hints, cairo-0-secp-hints, cairo-0-data-availability-hints, extensive_hints" check-fmt: cargo fmt --all -- --check @@ -344,6 +352,8 @@ clean: rm -f $(TEST_DIR)/*.pie rm -f $(BENCH_DIR)/*.json rm -f $(BAD_TEST_DIR)/*.json + rm -f $(SECP_CAIRO0_HINTS_DIR)/*.json + rm -f $(KZG_DA_CAIRO0_HINTS_DIR)/*.json rm -f $(PRINT_TEST_DIR)/*.json rm -f $(CAIRO_1_CONTRACTS_TEST_DIR)/*.sierra rm -f $(CAIRO_1_CONTRACTS_TEST_DIR)/*.casm diff --git a/README.md b/README.md index 0618a0fcea..1be8961632 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,7 @@ It's Turing-complete and it was created by [Starkware](https://starkware.co/) as These are needed in order to compile and use the project. -- [Rust 1.80.0 or newer](https://www.rust-lang.org/tools/install) +- [Rust 1.81.0 or newer](https://www.rust-lang.org/tools/install) - Cargo #### Optional @@ -110,7 +110,7 @@ You can then activate this environment by running You can add the following to your rust project's `Cargo.toml`: ```toml -cairo-vm = { version = '0.7.0'} +cairo-vm = { version = '1.0.1'} ``` ### Running cairo-vm from CLI @@ -182,6 +182,8 @@ The cairo-vm-cli supports the following optional arguments: - `run_from_cairo_pie`: Runs a Cairo PIE instead of a compiled json file. The name of the file will be the first argument received by the CLI (as if it were to run a normal compiled program). Can only be used if proof_mode is not enabled. +- `cairo_layout_params_file`: Only used with dynamic layout. Receives the name of a json file with the dynamic layout parameters. + For example, to obtain the air public inputs from a fibonacci program run, we can run : ```bash @@ -241,7 +243,7 @@ When using cairo-vm with the Starknet devnet there are additional parameters tha &mut hint_processor, ); ``` -### Running cairo 1 programs +### Running cairo 1 programs To run a cairo 1 program enter in the folder `cd cairo1-run` and follow the [`cairo1-run documentation`](cairo1-run/README.md) @@ -264,6 +266,15 @@ Now that you have the dependencies necessary to run the test suite you can run: make test ``` +### Using a Dynamic Layout + +A dynamic layout must be specified with a dynamic params file. You can find an example in: `vm/src/tests/cairo_layout_params_file.json`. + +To run cairo 0 or 1 programs with a dynamic layout, you must use `--layout dynamic` and the `--cairo_layout_params_file` flag pointing a dynamic params file. For example, run: +```bash +cargo run --bin cairo-vm-cli cairo_programs/fibonacci.json --layout dynamic --cairo_layout_params_file vm/src/tests/cairo_layout_params_file.json +``` + ### 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) diff --git a/bench/criterion_benchmark.rs b/bench/criterion_benchmark.rs index 7078ad21a8..d473fe61f6 100644 --- a/bench/criterion_benchmark.rs +++ b/bench/criterion_benchmark.rs @@ -30,6 +30,7 @@ fn build_many_runners(c: &mut Criterion) { CairoRunner::new( black_box(&program), black_box(LayoutName::starknet_with_keccak), + black_box(None), black_box(false), black_box(false), ) @@ -45,7 +46,16 @@ fn load_program_data(c: &mut Criterion) { let program = Program::from_bytes(program.as_slice(), Some("main")).unwrap(); c.bench_function("initialize", |b| { b.iter_batched( - || CairoRunner::new(&program, LayoutName::starknet_with_keccak, false, false).unwrap(), + || { + CairoRunner::new( + &program, + LayoutName::starknet_with_keccak, + None, + false, + false, + ) + .unwrap() + }, |mut runner| _ = black_box(runner.initialize(false).unwrap()), BatchSize::SmallInput, ) diff --git a/bench/iai_benchmark.rs b/bench/iai_benchmark.rs index b5bff69dce..441f79b5bc 100644 --- a/bench/iai_benchmark.rs +++ b/bench/iai_benchmark.rs @@ -34,6 +34,7 @@ fn build_runner() { let runner = CairoRunner::new( black_box(&program), LayoutName::starknet_with_keccak, + None, false, false, ) @@ -47,7 +48,14 @@ fn build_runner_helper() -> CairoRunner { //Picked the biggest one at the time of writing let program = include_bytes!("../cairo_programs/benchmarks/keccak_integration_benchmark.json"); let program = Program::from_bytes(program.as_slice(), Some("main")).unwrap(); - CairoRunner::new(&program, LayoutName::starknet_with_keccak, false, false).unwrap() + CairoRunner::new( + &program, + LayoutName::starknet_with_keccak, + None, + false, + false, + ) + .unwrap() } #[inline(never)] diff --git a/cairo-vm-cli/src/main.rs b/cairo-vm-cli/src/main.rs index 08095128ca..51cb3a649a 100644 --- a/cairo-vm-cli/src/main.rs +++ b/cairo-vm-cli/src/main.rs @@ -6,6 +6,7 @@ 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::types::layout::CairoLayoutParams; use cairo_vm::types::layout_name::LayoutName; use cairo_vm::vm::errors::cairo_run_errors::CairoRunError; use cairo_vm::vm::errors::trace_errors::TraceError; @@ -43,8 +44,13 @@ struct Args { entrypoint: String, #[structopt(long = "memory_file")] memory_file: Option, + /// When using dynamic layout, it's parameters must be specified through a layout params file. #[clap(long = "layout", default_value = "plain", value_enum)] layout: LayoutName, + /// Required when using with dynamic layout. + /// Ignored otherwise. + #[clap(long = "cairo_layout_params_file", required_if_eq("layout", "dynamic"))] + cairo_layout_params_file: Option, #[structopt(long = "proof_mode")] proof_mode: bool, #[structopt(long = "secure_run")] @@ -162,6 +168,11 @@ fn run(args: impl Iterator) -> Result<(), Error> { let trace_enabled = args.trace_file.is_some() || args.air_public_input.is_some(); + let cairo_layout_params = match args.cairo_layout_params_file { + Some(file) => Some(CairoLayoutParams::from_file(&file)?), + None => None, + }; + let cairo_run_config = cairo_run::CairoRunConfig { entrypoint: &args.entrypoint, trace_enabled, @@ -170,6 +181,7 @@ fn run(args: impl Iterator) -> Result<(), Error> { proof_mode: args.proof_mode, secure_run: args.secure_run, allow_missing_builtins: args.allow_missing_builtins, + dynamic_layout_params: cairo_layout_params, ..Default::default() }; @@ -405,6 +417,19 @@ mod tests { assert_matches!(run(args), Err(Error::Runner(_))); } + #[test] + fn test_run_dynamic_params() { + let mut args = vec!["cairo-vm-cli".to_string()]; + args.extend_from_slice(&["--layout".to_string(), "dynamic".to_string()]); + args.extend_from_slice(&[ + "--cairo_layout_params_file".to_string(), + "../vm/src/tests/cairo_layout_params_file.json".to_string(), + ]); + args.push("../cairo_programs/proof_programs/fibonacci.json".to_string()); + + assert_matches!(run(args.into_iter()), Ok(_)); + } + //Since the functionality here is trivial, I just call the function //to fool Codecov. #[test] diff --git a/cairo1-run/Cargo.toml b/cairo1-run/Cargo.toml index 500d410d28..e78ef041f9 100644 --- a/cairo1-run/Cargo.toml +++ b/cairo1-run/Cargo.toml @@ -12,9 +12,9 @@ keywords.workspace = true cairo-vm = { workspace = true, features = ["std", "cairo-1-hints", "clap"] } serde_json = { workspace = true } -cairo-lang-sierra-type-size = { version = "2.8.0", default-features = false } -cairo-lang-sierra-ap-change = { version = "2.8.0", default-features = false } -cairo-lang-sierra-gas = { version = "2.8.0", default-features = false } +cairo-lang-sierra-type-size = { version = "2.10.0-rc.0", default-features = false } +cairo-lang-sierra-ap-change = { version = "2.10.0-rc.0", default-features = false } +cairo-lang-sierra-gas = { version = "2.10.0-rc.0", default-features = false } cairo-lang-starknet-classes.workspace = true cairo-lang-sierra-to-casm.workspace = true cairo-lang-compiler.workspace = true @@ -34,3 +34,4 @@ num-bigint.workspace = true [features] default = ["with_mimalloc"] with_mimalloc = ["dep:mimalloc"] +mod_builtin = ["cairo-vm/mod_builtin"] diff --git a/cairo1-run/Makefile b/cairo1-run/Makefile index a00f9dfe3f..87777270b6 100644 --- a/cairo1-run/Makefile +++ b/cairo1-run/Makefile @@ -3,17 +3,17 @@ CAIRO_1_FOLDER=../cairo_programs/cairo-1-programs $(CAIRO_1_FOLDER)/%.trace: $(CAIRO_1_FOLDER)/%.cairo - cargo run --release $< --trace_file $@ --layout all_cairo + cargo run --release -F mod_builtin $< --trace_file $@ --layout all_cairo $(CAIRO_1_FOLDER)/%.memory: $(CAIRO_1_FOLDER)/%.cairo - cargo run --release $< --memory_file $@ --layout all_cairo + cargo run --release -F mod_builtin $< --memory_file $@ --layout all_cairo CAIRO_1_PROGRAMS=$(wildcard ../cairo_programs/cairo-1-programs/*.cairo) TRACES:=$(patsubst $(CAIRO_1_FOLDER)/%.cairo, $(CAIRO_1_FOLDER)/%.trace, $(CAIRO_1_PROGRAMS)) MEMORY:=$(patsubst $(CAIRO_1_FOLDER)/%.cairo, $(CAIRO_1_FOLDER)/%.memory, $(CAIRO_1_PROGRAMS)) deps: - git clone --depth=1 -b v2.8.0 https://github.com/starkware-libs/cairo.git \ + git clone --depth=1 -b v2.10.0-rc.0 https://github.com/starkware-libs/cairo.git \ && mv cairo/corelib/ . \ && rm -rf cairo/ diff --git a/cairo1-run/README.md b/cairo1-run/README.md index 9271d860bb..0aa67c0b22 100644 --- a/cairo1-run/README.md +++ b/cairo1-run/README.md @@ -7,7 +7,7 @@ Once you are inside the `./cairo1-run` folder, use the CLI with the following co To install the required dependencies(cairo corelib) run ```bash -make deps +make deps ``` Now that you have the dependencies necessary to run the tests, you can run: @@ -19,7 +19,7 @@ make test To execute a Cairo 1 program (either as Cairo 1 source file or Sierra) ```bash -cargo run ../cairo_programs/cairo-1-programs/fibonacci.cairo +cargo run ../cairo_programs/cairo-1-programs/fibonacci.cairo ``` Arguments to generate the trace and memory files @@ -44,7 +44,7 @@ cargo run ../cairo_programs/cairo-1-programs/with_input/array_input_sum.cairo -- To execute all the cairo 1 programs inside `../cairo_programs/cairo-1-programs/` and generate the corresponding trace and the memory files ```bash -make run +make run ``` ## CLI argument list @@ -71,6 +71,10 @@ The cairo1-run cli supports the following optional arguments: * `--append_return_values`: Adds extra instructions to the program in order to append the return and input values to the output builtin's segment. This is the default behaviour for proof_mode. Only allows `Array` as return and input value. +## Running circuits + +Circuits in cairo 1 require to enable the `mod_builtin` feature in order for the `AddMod`, `MulMod` and `RangeCheck96` builtins to be taken into account. + # Running scarb projects As cairo1-run skips gas checks when running, you will need to add the following to your Scarb.toml to ensure that compilation is done without adding gas checks: @@ -86,7 +90,7 @@ Then run the compiled project's sierra file located at `project_name/target/proj Example: ```bash - cargo run path-to-project/target/project_name.sierra.json + cargo run path-to-project/target/project_name.sierra.json ``` # Known bugs & issues diff --git a/cairo1-run/src/cairo_run.rs b/cairo1-run/src/cairo_run.rs index 3f8095e160..b196bda230 100644 --- a/cairo1-run/src/cairo_run.rs +++ b/cairo1-run/src/cairo_run.rs @@ -11,12 +11,13 @@ use cairo_lang_casm::{ use cairo_lang_sierra::{ extensions::{ bitwise::BitwiseType, + circuit::{AddModType, MulModType}, core::{CoreLibfunc, CoreType}, ec::EcOpType, gas::GasBuiltinType, pedersen::PedersenType, poseidon::PoseidonType, - range_check::RangeCheckType, + range_check::{RangeCheck96Type, RangeCheckType}, segment_arena::SegmentArenaType, starknet::syscalls::SystemType, ConcreteType, NamedType, @@ -38,8 +39,8 @@ use cairo_vm::{ math_utils::signed_felt, serde::deserialize_program::{ApTracking, FlowTrackingData, HintParams, ReferenceManager}, types::{ - builtin_name::BuiltinName, layout_name::LayoutName, program::Program, - relocatable::MaybeRelocatable, + builtin_name::BuiltinName, layout::CairoLayoutParams, layout_name::LayoutName, + program::Program, relocatable::MaybeRelocatable, }, vm::{ errors::{runner_errors::RunnerError, vm_errors::VirtualMachineError}, @@ -86,6 +87,7 @@ pub struct Cairo1RunConfig<'a> { pub relocate_mem: bool, /// Cairo layout chosen for the run pub layout: LayoutName, + pub dynamic_layout_params: Option, /// Run in proof_mode pub proof_mode: bool, /// Should be true if either air_public_input or cairo_pie_output are needed @@ -106,6 +108,7 @@ impl Default for Cairo1RunConfig<'_> { proof_mode: false, finalize_builtins: false, append_return_values: false, + dynamic_layout_params: None, } } } @@ -202,10 +205,14 @@ pub fn cairo_run_program( cairo_run_config.copy_to_output(), ); - let data: Vec = instructions - .flat_map(|inst| inst.assemble().encode()) - .map(|x| Felt252::from(&x)) - .map(MaybeRelocatable::from) + // The bytecode includes all program instructions plus entry/footer, + // plus data from the constants segments. + let data: Vec = casm_program + .assemble_ex(&entry_code.instructions, &libfunc_footer) + .bytecode + .into_iter() + .map(Into::::into) + .map(Into::::into) .collect(); let program = if cairo_run_config.proof_mode { @@ -248,6 +255,7 @@ pub fn cairo_run_program( let mut runner = CairoRunner::new_v2( &program, cairo_run_config.layout, + cairo_run_config.dynamic_layout_params.clone(), runner_mode, cairo_run_config.trace_enabled, )?; @@ -363,6 +371,8 @@ fn build_hints_vec<'b>( (hints, program_hints) } +// Function derived from the cairo-lang-runner crate. +// https://github.com/starkware-libs/cairo/blob/40a7b60687682238f7f71ef7c59c986cc5733915/crates/cairo-lang-runner/src/lib.rs#L551-L552 /// Finds first function ending with `name_suffix`. fn find_function<'a>( sierra_program: &'a SierraProgram, @@ -381,6 +391,8 @@ fn find_function<'a>( .ok_or_else(|| RunnerError::MissingMain) } +// Function derived from the cairo-lang-runner crate. +// https://github.com/starkware-libs/cairo/blob/40a7b60687682238f7f71ef7c59c986cc5733915/crates/cairo-lang-runner/src/lib.rs#L750 /// Creates a list of instructions that will be appended to the program's bytecode. fn create_code_footer() -> Vec { casm! { @@ -524,6 +536,8 @@ fn load_arguments( Ok(()) } +// Function derived from the cairo-lang-runner crate. +// https://github.com/starkware-libs/cairo/blob/40a7b60687682238f7f71ef7c59c986cc5733915/crates/cairo-lang-runner/src/lib.rs#L703 /// 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( @@ -657,6 +671,9 @@ fn create_entry_code( BuiltinName::ec_op => builtin_vars[&EcOpType::ID], BuiltinName::poseidon => builtin_vars[&PoseidonType::ID], BuiltinName::segment_arena => builtin_vars[&SegmentArenaType::ID], + BuiltinName::add_mod => builtin_vars[&AddModType::ID], + BuiltinName::mul_mod => builtin_vars[&MulModType::ID], + BuiltinName::range_check96 => builtin_vars[&RangeCheck96Type::ID], _ => unreachable!(), }; if copy_to_output_builtin { @@ -866,6 +883,8 @@ fn create_entry_code( )) } +// Function derived from the cairo-lang-runner crate. +// https://github.com/starkware-libs/cairo/blob/40a7b60687682238f7f71ef7c59c986cc5733915/crates/cairo-lang-runner/src/lib.rs#L577 fn get_info<'a>( sierra_program_registry: &'a ProgramRegistry, ty: &'a cairo_lang_sierra::ids::ConcreteTypeId, @@ -887,6 +906,13 @@ fn get_function_builtins( let mut builtin_offset: HashMap = HashMap::new(); let mut current_offset = 3; for (debug_name, builtin_name, sierra_id) in [ + ("MulMod", BuiltinName::mul_mod, MulModType::ID), + ("AddMod", BuiltinName::add_mod, AddModType::ID), + ( + "RangeCheck96", + BuiltinName::range_check96, + RangeCheck96Type::ID, + ), ("Poseidon", BuiltinName::poseidon, PoseidonType::ID), ("EcOp", BuiltinName::ec_op, EcOpType::ID), ("Bitwise", BuiltinName::bitwise, BitwiseType::ID), @@ -947,8 +973,11 @@ fn is_implicit_generic_id(generic_ty: &GenericTypeId) -> bool { PedersenType::ID, PoseidonType::ID, RangeCheckType::ID, + RangeCheck96Type::ID, SegmentArenaType::ID, SystemType::ID, + MulModType::ID, + AddModType::ID, ] .contains(generic_ty) } @@ -1143,6 +1172,9 @@ fn finalize_builtins( "Pedersen" => BuiltinName::pedersen, "Output" => BuiltinName::output, "Ecdsa" => BuiltinName::ecdsa, + "AddMod" => BuiltinName::add_mod, + "MulMod" => BuiltinName::mul_mod, + "RangeCheck96" => BuiltinName::range_check96, _ => { stack_pointer.offset += size as usize; continue; @@ -1231,7 +1263,7 @@ fn serialize_output_inner<'a>( .expect("Missing return value") .get_relocatable() .expect("Box Pointer is not Relocatable"); - let type_size = type_sizes[&info.ty].try_into().expect("could not parse to usize"); + let type_size = type_sizes[&info.ty].try_into().expect("could not parse to usize"); let data = vm .get_continuous_range(ptr, type_size) .expect("Failed to extract value from nullable ptr"); diff --git a/cairo1-run/src/main.rs b/cairo1-run/src/main.rs index 47c9abfbca..6ec7d6d32f 100644 --- a/cairo1-run/src/main.rs +++ b/cairo1-run/src/main.rs @@ -4,6 +4,7 @@ use cairo1_run::{cairo_run_program, Cairo1RunConfig, FuncArg}; use cairo_lang_compiler::{ compile_prepared_db, db::RootDatabase, project::setup_project, CompilerConfig, }; +use cairo_vm::types::layout::CairoLayoutParams; use cairo_vm::{ air_public_input::PublicInputError, types::layout_name::LayoutName, vm::errors::trace_errors::TraceError, Felt252, @@ -24,15 +25,20 @@ struct Args { trace_file: Option, #[structopt(long = "memory_file")] memory_file: Option, + /// When using dynamic layout, it's parameters must be specified through a layout params file. #[clap(long = "layout", default_value = "plain", value_enum)] layout: LayoutName, + /// Required when using with dynamic layout. + /// Ignored otherwise. + #[clap(long = "cairo_layout_params_file", required_if_eq("layout", "dynamic"))] + cairo_layout_params_file: Option, #[clap(long = "proof_mode", value_parser)] proof_mode: bool, #[clap(long = "air_public_input", requires = "proof_mode")] 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( @@ -153,6 +159,11 @@ fn run(args: impl Iterator) -> Result, Error> { args.args = process_args(&std::fs::read_to_string(filename)?).unwrap(); } + let cairo_layout_params = match args.cairo_layout_params_file { + Some(file) => Some(CairoLayoutParams::from_file(&file)?), + None => None, + }; + let cairo_run_config = Cairo1RunConfig { proof_mode: args.proof_mode, serialize_output: args.print_output, @@ -162,6 +173,7 @@ fn run(args: impl Iterator) -> Result, Error> { args: &args.args.0, finalize_builtins: args.air_public_input.is_some() || args.cairo_pie_output.is_some(), append_return_values: args.append_return_values, + dynamic_layout_params: cairo_layout_params, }; // Try to parse the file as a sierra program @@ -424,8 +436,14 @@ mod tests { Some("[17 18]"), Some("[17 18]") )] - - fn test_run_progarm( + #[cfg_attr(feature = "mod_builtin", case( + "circuit.cairo", + "36699840570117848377038274035 72042528776886984408017100026 54251667697617050795983757117 7", + "[36699840570117848377038274035 72042528776886984408017100026 54251667697617050795983757117 7]", + None, + None + ))] + fn test_run_program( #[case] program: &str, #[case] expected_output: &str, #[case] expected_serialized_output: &str, @@ -478,6 +496,19 @@ mod tests { assert_matches!(run(args), Ok(Some(res)) if res == expected_output, "Program {} failed with flags {}", program, extra_flags.concat()); } + #[test] + fn test_run_dynamic_params() { + let mut args = vec!["cairo1-run".to_string()]; + args.extend_from_slice(&["--layout".to_string(), "dynamic".to_string()]); + args.extend_from_slice(&[ + "--cairo_layout_params_file".to_string(), + "../vm/src/tests/cairo_layout_params_file.json".to_string(), + ]); + args.push("../cairo_programs/cairo-1-programs/fibonacci.cairo".to_string()); + + assert_matches!(run(args.into_iter()), Ok(_)); + } + // these tests are separated so as to run them without --append_return_values and --proof_mode options // since they require to use the squashed version of felt252 #[rstest] diff --git a/cairo1-run/trace b/cairo1-run/trace deleted file mode 100644 index 338056a5a3..0000000000 Binary files a/cairo1-run/trace and /dev/null differ diff --git a/cairo_programs/cairo-0-kzg-da-hints/reduced_mul.cairo b/cairo_programs/cairo-0-kzg-da-hints/reduced_mul.cairo new file mode 100644 index 0000000000..8f54d7d0cb --- /dev/null +++ b/cairo_programs/cairo-0-kzg-da-hints/reduced_mul.cairo @@ -0,0 +1,29 @@ +%builtins range_check + +from starkware.starknet.core.os.data_availability.bls_field import reduced_mul, BigInt3 + +func main{range_check_ptr: felt}() { + let x = BigInt3(0, 0, 0); + let y = BigInt3(1, 1, 1); + + let res = reduced_mul(x, y); + + assert res = BigInt3(0, 0, 0); + + let x = BigInt3(100, 99, 98); + let y = BigInt3(10, 9, 8); + + let res = reduced_mul(x, y); + + assert res = BigInt3( + 49091481911800146991175221, 43711329369885800715738617, 405132241597509509195407 + ); + + let x = BigInt3(47503316700827173496989353, 17218105161352860131668522, 527908748911931938599018); + let y = BigInt3(50964737623371959432443726, 60451660835701602854498663, 5043009036652075489876599); + + let res = reduced_mul(x, y); + assert res = BigInt3(43476011663489831917914902, 15057238271740518603165849, 1923992965848504555868221); + + return (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_compute_q_mod_prime.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_compute_q_mod_prime.cairo new file mode 100644 index 0000000000..5cee8bbf04 --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_compute_q_mod_prime.cairo @@ -0,0 +1,52 @@ +%builtins range_check + +from starkware.cairo.common.cairo_secp.bigint import BigInt3, nondet_bigint3, UnreducedBigInt3 + +const BASE = 2 ** 86; +const SECP_REM = 19; + +func test_q_mod_prime{range_check_ptr: felt}(val: UnreducedBigInt3) { + let q = [ap]; + %{ + from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P + from starkware.cairo.common.cairo_secp.secp_utils import pack + + q, r = divmod(pack(ids.val, PRIME), SECP256R1_P) + assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." + ids.q = q % PRIME + %} + let q_biased = [ap + 1]; + q_biased = q + 2 ** 127, ap++; + [range_check_ptr] = q_biased, ap++; + // This implies that q is in the range [-2**127, 2**127). + + tempvar r1 = (val.d0 + q * SECP_REM) / BASE; + assert [range_check_ptr + 1] = r1 + 2 ** 127; + // This implies that r1 is in the range [-2**127, 2**127). + // Therefore, r1 * BASE is in the range [-2**213, 2**213). + // By the soundness assumption, val.d0 is in the range (-2**250, 2**250). + // This implies that r1 * BASE = val.d0 + q * SECP_REM (as integers). + + tempvar r2 = (val.d1 + r1) / BASE; + assert [range_check_ptr + 2] = r2 + 2 ** 127; + // Similarly, this implies that r2 * BASE = val.d1 + r1 (as integers). + // Therefore, r2 * BASE**2 = val.d1 * BASE + r1 * BASE. + + assert val.d2 = q * (BASE / 8) - r2; + // Similarly, this implies that q * BASE / 4 = val.d2 + r2 (as integers). + // Therefore, + // q * BASE**3 / 4 = val.d2 * BASE**2 + r2 * BASE ** 2 = + // val.d2 * BASE**2 + val.d1 * BASE + r1 * BASE = + // val.d2 * BASE**2 + val.d1 * BASE + val.d0 + q * SECP_REM = + // val + q * SECP_REM. + // Hence, val = q * (BASE**3 / 4 - SECP_REM) = q * (2**256 - SECP_REM) = q * secp256k1_prime. + + let range_check_ptr = range_check_ptr + 3; + return (); +} + +func main{range_check_ptr: felt}() { + let val = UnreducedBigInt3(0, 0, 0); + test_q_mod_prime(val); + return (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec.cairo new file mode 100644 index 0000000000..98bc7f1cfe --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec.cairo @@ -0,0 +1,105 @@ +%builtins range_check + +// Tests: +// - cairo0_hints::COMPUTE_Q_MOD_PRIME +// - cairo0_hints::COMPUTE_IDS_HIGH_LOW +// - cairo0_hints::SECP_DOUBLE_ASSIGN_NEW_X +// - cairo0_hints::FAST_SECP_ADD_ASSIGN_NEW_Y + +from starkware.cairo.common.secp256r1.ec import ( + EcPoint, + compute_doubling_slope, + compute_slope, + ec_double, + fast_ec_add, + ec_mul_inner, +) +from starkware.cairo.common.cairo_secp.bigint3 import BigInt3 + +func main{range_check_ptr: felt}() { + let x = BigInt3(1, 5, 10); + let y = BigInt3(2, 4, 20); + + + let point_a = EcPoint(x, y); + + let point_b = EcPoint( + BigInt3(1, 5, 10), + BigInt3(77371252455336262886226989, 77371252455336267181195259, 19342813113834066795298795), + ); + + // let (point_c) = ec_negate(EcPoint(BigInt3(156, 6545, 100010), BigInt3(1123, -1325, 910))); + let point_c = EcPoint( + BigInt3(156, 6545, 100010), + BigInt3(77371252455336262886225868, 1324, 19342813113834066795297906), + ); + + // compute_doubling_slope + let (slope_a) = compute_doubling_slope(point_b); + assert slope_a = BigInt3( + 64839545681970757313529612, 5953360968438044038987377, 13253714962539897079325475 + ); + + let (slope_b) = compute_doubling_slope( + EcPoint(BigInt3(1231, 51235643, 100000), BigInt3(77371252455, 7737125245, 19342813113)) + ); + assert slope_b = BigInt3( + 61129622008745017597879703, 29315582959606925875642332, 13600923539144215962821694 + ); + + // compute_slope + let (slope_c) = compute_slope(point_a, point_c); + assert slope_c = BigInt3( + 69736698275759322439409874, 45955733659898858347886847, 18034242868575077772302310 + ); + + let (slope_d) = compute_slope(point_c, point_b); + assert slope_d = BigInt3( + 66872739393348882319301304, 44057296979296181456999622, 6628179500048909995474229 + ); + + // ec_double + let (point_d) = ec_double(point_a); + assert point_d = EcPoint( + BigInt3(62951442591564288805558802, 32562108923955565608466346, 18605500881547971871596634), + BigInt3(32147810383256899543807670, 5175857156528420748725791, 6618806236944685895112117), + ); + + let (point_e) = ec_double( + EcPoint(BigInt3(156, 6545, 100010), BigInt3(5336262886225868, 1324, 113834066795297906)) + ); + assert point_e = EcPoint( + BigInt3(47503316700827173496989353, 17218105161352860131668522, 527908748911931938599018), + BigInt3(50964737623371959432443726, 60451660835701602854498663, 5043009036652075489876599), + ); + + // fast_ec_add + let (point_f) = fast_ec_add(point_a, point_e); + assert point_f = EcPoint( + BigInt3(29666922781464823323928071, 37719311829566792810003084, 9541551049028573381125035), + BigInt3(12938160206947174373897851, 22954464827120147659997987, 2690642098017756659925259), + ); + + let (point_g) = fast_ec_add( + EcPoint(BigInt3(89712, 56, 7348489324), BigInt3(980126, 10, 8793)), + EcPoint(BigInt3(16451, 5967, 2171381), BigInt3(12364564, 123654, 193)), + ); + assert point_g = EcPoint( + BigInt3(14771767859485410664249539, 62406103981610765545970487, 8912032684309792565082157), + BigInt3(25591125893919304137822981, 54543895003572926651874352, 18968003584818937876851951), + ); + + // ec_mul_inner + let (pow2, res) = ec_mul_inner( + EcPoint( + BigInt3(65162296, 359657, 04862662171381), BigInt3(5166641367474701, 63029418, 793) + ), + 123, + 298, + ); + assert pow2 = EcPoint( + BigInt3(73356165220405599685396595, 44054642183803477920871071, 5138516367480965019117743), + BigInt3(40256732918865941543909206, 68107624737772931608959283, 1842118797516663063623771), + ); + return (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_double_assign_new_x.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_double_assign_new_x.cairo new file mode 100644 index 0000000000..15deaca336 --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_double_assign_new_x.cairo @@ -0,0 +1,26 @@ +%builtins range_check + +from starkware.cairo.common.secp256r1.ec import ( + EcPoint, + ec_double +) +from starkware.cairo.common.cairo_secp.bigint import BigInt3 + +func main{range_check_ptr}() { + let x = BigInt3(235, 522, 111); + let y = BigInt3(1323, 15124, 796759); + + let point = EcPoint(x, y); + + let (r) = ec_double(point); + + assert r.x.d0 = 64413149096815403908768532; + assert r.x.d1 = 28841630551789071202278393; + assert r.x.d2 = 11527965423300397026710769; + + assert r.y.d0 = 6162628527473476058419904; + assert r.y.d1 = 69076668518034904023852368; + assert r.y.d2 = 10886445027049641070037760; + + return (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_mul_by_uint256.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_mul_by_uint256.cairo new file mode 100644 index 0000000000..4e6a939e0b --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_mul_by_uint256.cairo @@ -0,0 +1,26 @@ +%builtins range_check + +from starkware.cairo.common.secp256r1.ec import ( + EcPoint, + ec_mul_by_uint256 +) +from starkware.cairo.common.uint256 import Uint256 +from starkware.cairo.common.cairo_secp.bigint import BigInt3 + +func main{range_check_ptr: felt}() { + let x = BigInt3(235, 522, 111); + let y = BigInt3(1323, 15124, 796759); + + let point = EcPoint(x, y); + + let scalar = Uint256( + 143186476941636880901214103594843510573, 124026708105846590725274683684370988502 + ); + let (res) = ec_mul_by_uint256(point, scalar); + + assert res = EcPoint( + BigInt3(31454759005629465428788733, 35370111304581841775514461, 13535495107675380502530193), + BigInt3(18078210390106977421552565, 53503834862379828768870254, 3887397808398301655656699), + ); + return (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_get_point_from_x.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_get_point_from_x.cairo new file mode 100644 index 0000000000..8ac143a75b --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_get_point_from_x.cairo @@ -0,0 +1,31 @@ +%builtins range_check + +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.cairo_secp.bigint3 import BigInt3 +from starkware.cairo.common.cairo_secp.ec_point import EcPoint +from starkware.cairo.common.secp256r1.ec import ( + try_get_point_from_x +) +from starkware.cairo.common.uint256 import Uint256 + + +func main{range_check_ptr: felt}() { + let zero = BigInt3( + 0, 0, 0 + ); + let result: EcPoint* = alloc(); + let (is_on_curve) = try_get_point_from_x(zero, 0, result); + assert is_on_curve = 1; + + let x = BigInt3(512,2412,133); + let result: EcPoint* = alloc(); + let (is_on_curve) = try_get_point_from_x(x, 1, result); + assert is_on_curve = 1; + + let x = BigInt3(64,0,6546); + + let result: EcPoint* = alloc(); + let (is_on_curve) = try_get_point_from_x(x, 1, result); + assert is_on_curve = 0; + return (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_value.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_value.cairo new file mode 100644 index 0000000000..15f8771f64 --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_value.cairo @@ -0,0 +1,39 @@ +%builtins range_check + +from starkware.cairo.common.cairo_secp.bigint import BigInt3, nondet_bigint3, UnreducedBigInt3 + +func reduce_value{range_check_ptr}(x: UnreducedBigInt3) -> (res: BigInt3) { + %{ + from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P + from starkware.cairo.common.cairo_secp.secp_utils import pack + value = pack(ids.x, PRIME) % SECP256R1_P + %} + let (res) = nondet_bigint3(); + return (res=res); +} + +func test_reduce_value{range_check_ptr: felt}() { + let x = UnreducedBigInt3(0, 0, 0); + let (reduce_a) = reduce_value(x); + assert reduce_a = BigInt3( + 0, 0, 0 + ); + + let y = UnreducedBigInt3(12354, 745634534, 81298789312879123); + let (reduce_b) = reduce_value(y); + assert reduce_b = BigInt3( + 12354, 745634534, 81298789312879123 + ); + + let z = UnreducedBigInt3(12354812987893128791212331231233, 7453123123123123312634534, 8129224990312325879); + let (reduce_c) = reduce_value(z); + assert reduce_c = BigInt3( + 16653320122975184709085185, 7453123123123123312794216, 8129224990312325879 + ); + return (); +} + +func main{range_check_ptr: felt}() { + test_reduce_value(); + return (); +} diff --git a/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_x.cairo b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_x.cairo new file mode 100644 index 0000000000..fa9865c520 --- /dev/null +++ b/cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_x.cairo @@ -0,0 +1,39 @@ +%builtins range_check + +from starkware.cairo.common.cairo_secp.bigint import BigInt3, nondet_bigint3, UnreducedBigInt3 + +func reduce_x{range_check_ptr}(x: UnreducedBigInt3) -> (res: BigInt3) { + %{ + from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P + from starkware.cairo.common.cairo_secp.secp_utils import pack + value = pack(ids.x, PRIME) % SECP256R1_P + %} + let (res) = nondet_bigint3(); + return (res=res); +} + +func test_reduce_x{range_check_ptr: felt}() { + let x = UnreducedBigInt3(0, 0, 0); + let (reduce_a) = reduce_x(x); + assert reduce_a = BigInt3( + 0, 0, 0 + ); + + let y = UnreducedBigInt3(12354, 745634534, 81298789312879123); + let (reduce_b) = reduce_x(y); + assert reduce_b = BigInt3( + 12354, 745634534, 81298789312879123 + ); + + let z = UnreducedBigInt3(12354812987893128791212331231233, 7453123123123123312634534, 8129224990312325879); + let (reduce_c) = reduce_x(z); + assert reduce_c = BigInt3( + 16653320122975184709085185, 7453123123123123312794216, 8129224990312325879 + ); + return (); +} + +func main{range_check_ptr: felt}() { + test_reduce_x(); + return (); +} diff --git a/cairo_programs/cairo-1-programs/circuit.cairo b/cairo_programs/cairo-1-programs/circuit.cairo new file mode 100644 index 0000000000..5624c92ccc --- /dev/null +++ b/cairo_programs/cairo-1-programs/circuit.cairo @@ -0,0 +1,30 @@ +use core::circuit::{ + RangeCheck96, AddMod, MulMod, u96, CircuitElement, CircuitInput, circuit_add, + circuit_sub, circuit_mul, circuit_inverse, EvalCircuitTrait, u384, + CircuitOutputsTrait, CircuitModulus, AddInputResultTrait, CircuitInputs, +}; + +fn main() -> u384 { + let in1 = CircuitElement::> {}; + let in2 = CircuitElement::> {}; + let add1 = circuit_add(in1, in2); + let mul1 = circuit_mul(add1, in1); + let mul2 = circuit_mul(mul1, add1); + let inv1 = circuit_inverse(mul2); + let sub1 = circuit_sub(inv1, in2); + let sub2 = circuit_sub(sub1, mul2); + let inv2 = circuit_inverse(sub2); + let add2 = circuit_add(inv2, inv2); + + let modulus = TryInto::<_, CircuitModulus>::try_into([17, 14, 14, 14]).unwrap(); + + let outputs = (add2,) + .new_inputs() + .next([9, 2, 9, 3]) + .next([5, 7, 0, 8]) + .done() + .eval(modulus) + .unwrap(); + + outputs.get_output(add2) +} diff --git a/cairo_programs/cairo-1-programs/serialized_output/circuit.cairo b/cairo_programs/cairo-1-programs/serialized_output/circuit.cairo new file mode 100644 index 0000000000..85e9f7e9a6 --- /dev/null +++ b/cairo_programs/cairo-1-programs/serialized_output/circuit.cairo @@ -0,0 +1,40 @@ +use core::circuit::{ + RangeCheck96, AddMod, MulMod, u96, CircuitElement, CircuitInput, circuit_add, + circuit_sub, circuit_mul, circuit_inverse, EvalCircuitTrait, u384, + CircuitOutputsTrait, CircuitModulus, AddInputResultTrait, CircuitInputs, +}; +use array::ArrayTrait; + +fn main() -> Array { + let in1 = CircuitElement::> {}; + let in2 = CircuitElement::> {}; + let add1 = circuit_add(in1, in2); + let mul1 = circuit_mul(add1, in1); + let mul2 = circuit_mul(mul1, add1); + let inv1 = circuit_inverse(mul2); + let sub1 = circuit_sub(inv1, in2); + let sub2 = circuit_sub(sub1, mul2); + let inv2 = circuit_inverse(sub2); + let add2 = circuit_add(inv2, inv2); + + let modulus = TryInto::<_, CircuitModulus>::try_into([17, 14, 14, 14]).unwrap(); + + let outputs = (add2,) + .new_inputs() + .next([9, 2, 9, 3]) + .next([5, 7, 0, 8]) + .done() + .eval(modulus) + .unwrap(); + + let circuit_output = outputs.get_output(add2); + + let mut output: Array = ArrayTrait::new(); + + circuit_output.limb0.serialize(ref output); + circuit_output.limb1.serialize(ref output); + circuit_output.limb2.serialize(ref output); + circuit_output.limb3.serialize(ref output); + + output +} diff --git a/docs/README.md b/docs/README.md index 5d70a46fea..c781d4d706 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,6 @@ # Documentation + +* [How does the Cairo VM work?](./vm/) * [How does the original Cairo VM work?](./python_vm/) * [Benchmarks](./benchmarks/) * [Custom Hint Processor](./hint_processor/) @@ -6,3 +8,11 @@ * [References parsing](./references_parsing/) * [Tracer](./tracer/) * [Debugging](./debugging.md) + +## Tooling + +* [cairo1-run](/cairo1-run): Execute Cairo 1 programs +* [cairo-vm-cli](/cairo-vm-cli): Execute Cairo 0 programs +* [cairo-vm-tracer](/cairo-vm-tracer) +* [fuzzer](/fuzzer) +* [hint_accountant](/hint_accountant) diff --git a/docs/vm/README.md b/docs/vm/README.md new file mode 100644 index 0000000000..f340a4b8c2 --- /dev/null +++ b/docs/vm/README.md @@ -0,0 +1,361 @@ + + +# How does the Cairo VM work? + +## High Level Overview + +The Cairo virtual machine is meant to be used in the context of STARK validity proofs. What this means is that the point of Cairo is not just to execute some code and get a result, but to *prove* to someone else that said execution was done correctly, without them having to re-execute the entire thing. The rough flow for it looks like this: + +- A user writes a Cairo program. +- The program is compiled into Cairo's VM bytecode. +- The VM executes said code and provides a *trace* of execution, i.e. a record of the state of the machine and its memory *at every step of the computation*. +- This trace is passed on to a STARK prover, which creates a cryptographic proof from it, attesting to the correct execution of the program. +- The proof is passed to a verifier, who checks that the proof is valid in a fraction of a second, without re-executing. + +The main three components of this flow are: + +- A Cairo compiler to turn a program written in the [Cairo programming language](https://www.cairo-lang.org/) into bytecode. +- A Cairo VM to then execute it and generate a trace. +- [A STARK prover and verifier](https://github.com/starkware-libs/stone-prover/tree/main) so one party can prove correct execution, while another can verify it. + +While this repo is only concerned with the second component, it's important to keep in mind the other two; especially important are the prover and verifier that this VM feeds its trace to, as a lot of its design decisions come from them. This virtual machine is designed to make proving and verifying both feasible and fast, and that makes it quite different from most other VMs you are probably used to. + +## Basic VM flow + +Our virtual machine has a very simple flow: + +- Take a compiled cairo program as input. You can check out an example program [here](https://github.com/lambdaclass/cairo-vm.go/blob/main/cairo_programs/fibonacci.cairo). +- Run the bytecode from the compiled program, doing the usual `fetch->decode->execute` loop, running until program termination. +- On every step of the execution, record the values of each register. +- Take the register values and memory at every step and write them to a file, called the `execution trace`. + +Barring some simplifications we made, this is all the Cairo VM does. The two main things that stand out as radically different are the memory model and the use of `Field Elements` to perform arithmetic. Below we go into more detail on each step, and in the process explain the ommisions we made. + +## Memory + +The Cairo virtual machine uses a Von Neumann architecture with a Non-deterministic read-only memory. What this means, roughly, is that memory is immutable after you've written to it (i.e. you can only write to it once); this is to make the STARK proving easier, but we won't go into that here. + +### Memory Segments and Relocation + +The process of memory allocation in a contiguous write-once memory region can get pretty complicated. Imagine you want to have a regular call stack, with a stack pointer pointing to the top of it and allocation and deallocation of stack frames and local variables happening throughout execution. Because memory is immutable, this cannot be done the usual way; once you allocate a new stack frame that memory is set, it can't be reused for another one later on. + +Because of this, memory in Cairo is divided into `segments`. This is just a way of organizing memory more conveniently for this write-once model. Each segment is nothing more than a contiguous memory region. Segments are identified by an `index`, an integer value that uniquely identifies them. + +Memory `cells` (i.e. values in memory) are identified by the index of the segment they belong to and an `offset` into said segment. Thus, the memory cell `{2,0}` is the first cell of segment number `2`. + +Even though this segment model is extremely convenient for the VM's execution, the STARK prover needs to have the memory as just one contiguous region. Because of this, once execution of a Cairo program finishes, all the memory segments are collapsed into one; this process is called `Relocation`. We will go into more detail on all of this below. + +The first segment (index 0) is the program segment, which stores the instructions of a cairo program. The following segment (index 1) is the execution segment, which holds the values that are created along the execution of the vm, for example, when we call a function, a pointer to the next instruction after the call instruction will be stored in the execution segment which will then be used to find the next instruction after the function returns. + +The following group of segments are the builtin segments, one for each builtin used by the program, and which hold values used by the builtin runners. + +The last group of segments are the user segments, which represent data structures created by the user, for example, when creating an array on a cairo program, that array will be represented in memory as its own segment. + +While memory is continous, some gaps may be present. These gaps can be created on purpose by the user, for example by executing the following CASM: + +```text +[ap + 1] = 2; +``` + +These gaps may also be created indireclty by diverging branches, as for example one branch may declare a variable that the other branch doesn't, as memory needs to be allocated for both cases if the second case is ran then a gap is left where the variable should have been written. + +### Felts + +Felts, or Field Elements, are cairo's basic integer type. Every variable in a cairo vm that is not a pointer is a felt. From our point of view we could say a felt in cairo is an unsigned integer in the range [0, P), where P is a very large prime currently equal to `2^251+17*2^192+1`. This means that all operations are done modulo P. + +### Relocatable + +An address (or pointer) in cairo is represented as a `Relocatable` value, which is made up of a `segment_index` and an `offset`, the `segment_index` tells us which segment the value is stored in and the `offset` tells us how many values exist between the start of the segment and the value. + +As the cairo memory can hold both felts and pointers, the basic memory unit is a `MaybeRelocatable`, a variable that can be either a `Relocatable` or a `Felt`. + +### Memory Relocation + +During execution, the memory consists of segments of varying length, and they can be accessed by indicating their segment index, and the offset within that segment. When the run is finished, a relocation process takes place, which transforms this segmented memory into a contiguous list of values. The relocation process works as follows: + +1. The size of each segment is calculated as the highest offset within the segment + 1. +2. A base is assigned to each segment by accumulating the size of the previous segment. The first segment's base is set to 1. +3. All `relocatable` values are converted into a single integer by adding their `offset` value to their segment's base calculated in the previous step + +For example, if we have this memory represented by address, value pairs: + +```text + 0:0 -> 1 + 0:1 -> 4 + 0:2 -> 7 + 1:0 -> 8 + 1:1 -> 0:2 + 1:4 -> 0:1 + 2:0 -> 1 +``` + +Then, to relocate: + +1. Calculate segment sizes: + ```text + 0 --(has size)--> 3 + 1 --(has size)--> 5 + 2 --(has size)--> 1 + ``` + +2. Assign a base to each segment: + ```text + 0 --(has base value)--> 1 + 1 --(has base value)--> 4 (that is: 1 + 3) + 2 --(has base value)--> 9 (that is: 4 + 5) + ``` + +3. Convert relocatables to integers + ```text + 1 (base[0] + 0) -> 1 + 2 (base[0] + 1) -> 4 + 3 (base[0] + 2) -> 7 + 4 (base[1] + 0) -> 8 + 5 (base[1] + 1) -> 3 (that is: base[0] + 2) + .... (memory gaps) + 8 (base[1] + 4) -> 2 (that is: base[0] + 1) + 9 (base[2] + 0) -> 1 + ``` + +## Instruction Set + +### Registers + +There are only three registers in the Cairo VM: + +- The program counter `pc`, which points to the next instruction to be executed. +- The allocation pointer `ap`, pointing to the next unused memory cell. +- The frame pointer `fp`, pointing to the base of the current stack frame. When a new function is called, `fp` is set to the current `ap`. When the function returns, `fp` goes back to its previous value. The VM creates new segments whenever dynamic allocation is needed, so for example the cairo analog to a Rust `Vec` will have its own segment. Relocation at the end meshes everything together. + +### Instruction Format + +CASM instruction have the following format. If the instruction uses an immediate operand, it's value is taken from the next cell of the program segment. + +``` +// Structure of the 63-bit that form the first word of each instruction. +// See Cairo whitepaper, page 32 - https://eprint.iacr.org/2021/1063.pdf. +┌─────────────────────────────────────────────────────────────────────────┐ +│ off_dst (biased representation) │ +├─────────────────────────────────────────────────────────────────────────┤ +│ off_op0 (biased representation) │ +├─────────────────────────────────────────────────────────────────────────┤ +│ off_op1 (biased representation) │ +├─────┬─────┬───────┬───────┬───────────┬────────┬───────────────────┬────┤ +│ dst │ op0 │ op1 │ res │ pc │ ap │ opcode │ 0 │ +│ reg │ reg │ src │ logic │ update │ update │ │ │ +├─────┼─────┼───┬───┼───┬───┼───┬───┬───┼───┬────┼────┬────┬────┬────┼────┤ +│ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ 12 │ 13 │ 14 │ 15 │ +└─────┴─────┴───┴───┴───┴───┴───┴───┴───┴───┴────┴────┴────┴────┴────┴────┘ +``` + +- The first 6 fields: `off_dst`, `off_op0`, `off_op1`, `dst_reg`, `op0_reg`, `op1_src` determine the memory locations of the operands, and the destination of the result (which is not always used). + - `op1_src` occupies 2 bits, as it can also be an immediate, in which case the value will be taken from the next instruction (`[pc + 1]`). +- The `res_logic` field determines how to compute the result (op1, sum, multiplication). The usage of the result depends on the following fields. +- The `opcode` field describes which operation is been performed (noop, assert, call, ret). It modifies the meaning of the following fields. +- The `pc_update` field determines how to update the program counter (advance, jump, branch, etc.). +- The `ap_update` field determines how to update the allocation pointer. + +For an in-depth explanation, you can see Cairo whitepaper, page 33 - https://eprint.iacr.org/2021/1063.pdf, or checkout [our implementation](/vm/src/vm/vm_core.rs). + +Take, for example, the following instruction: + +``` +[ap + 1] = [fp + 2] + 3 +``` + +The instruction (at `[pc]`) will be encoded as: + +``` +off_dst = 1 +off_op0 = 2 +off_op1 = 1 # It's always 1 when op1 is an immediate +dst_reg = 0 # AP +op0_reg = 1 # FP +op1_src = 1 # Immediate +res_logic = 1 # Add +pc_update = 0 # Regular (advance) +ap_update = 0 # Regular (no update) +opcode = 4 # Assert +``` + +The next instruction (at `[pc + 1]`) will be the immediate, and it will have a value of `3`. + +Given the following initial register values: +``` +fp = 5 +ap = 10 +pc = 15 +``` +Then: +- `op1` is `[fp + 2]`, which is resolved to `[7]`. +- `op2` is `[pc + 1]`, which is resolved to `[16] == 3`. +- `dst` is `[ap + 1]`, which is resolved to `[11]` +- The result of `op1 + op2` is stored at `dst` +- The register `pc` is increased by 2, we skip the next instruction because it was the immediate. +- The register `fp` is not updated +- The register `ap` is not updated + +## Hints + +So far we have been thinking about the VM mostly abstracted from the prover and verifier it's meant to feed its results to. The last main feature we need to talk about, however, requires keeping this proving/verifying logic in mind. + +As a reminder, the whole point of the Cairo VM is to output a trace/memory file so that a `prover` can then create a cryptographic proof that the execution of the program was done correctly. A `verifier` can then take that proof and verify it in much less time than it would have taken to re-execute the entire program. + +In this model, the one actually using the VM to run a cairo program is *always the prover*. The verifier does not use the VM in any way, as that would defeat the entire purpose of validity proofs; they just get the program being run and the proof generated by the prover and run some cryptographic algorithm to check it. + +While the verifier does not execute the code, they do *check it*. As an example, if a cairo program computes a fibonacci number like this: + +``` +func main() { + // Call fib(1, 1, 10). + let result: felt = fib(1, 1, 10); +} +``` + +the verifier won't *run* this, but they will reject any incorrect execution of the call to `fib`. The correct value for `result` in this case is `144` (it's the 10th fibonacci number); any attempt by the prover to convince the verifier that `result` is not `144` will fail, because the call to the `fib` function is *being proven* and thus *seen* by the verifier. + +A `Hint` is a piece of code that is not proven, and therefore not seen by the verifier. If `fib` above were a hint, then the prover could convince the verifier that `result` is $144$, $0$, $1000$ or any other number. + +In cairo 0, hints are code written in `Python` and are surrounded by curly brackets. Here's an example from the `alloc` function, provided by the Cairo common library + +``` +func alloc() -> (ptr: felt*) { + %{ memory[ap] = segments.add() %} + ap += 1; + return (ptr=cast([ap - 1], felt*)); +} +``` + +The first line of the function, + +``` +%{ memory[ap] = segments.add() %} +``` + +is a hint called `ADD_SEGMENT`. All it does is create a new memory segment, then write its base to the current value of `ap`. This is python code that is being run in the context of the VM's execution; thus `memory` refers to the VM's current memory and `segments.add()` is just a function provided by the VM to allocate a new segment. + +At this point you might be wondering: why run code that's not being proven? Isn't the whole point of Cairo to prove correct execution? There are (at least) two reasons for hints to exist. + +### Nothing to prove + +For some operations there's simply nothing to prove, as they are just convenient things one wants to do during execution. The `ADD_SEGMENT` hint shown above is a good example of that. When proving execution, the program's memory is presented as one relocated continuous segment, it does not matter at all which segment a cell was in, or when that segment was added. The verifier doesn't care. + +Because of this, there's no reason to make `ADD_SEGMENT` a part of the cairo language and have an instruction for it. + +### Optimization + +Certain operations can be very expensive, in the sense that they might involve a huge amount of instructions or memory usage, and therefore contribute heavily to the proving time. For certain calculations, there are two ways to convince the verifier that it was done correctly: + +- Write the entire calculation in Cairo/Cairo Assembly. This makes it show up in the trace and therefore get proven. +- *Present the result of the calculation to the verifier through a hint*, then show said result indeed satisfies the relevant condition that makes it the actual result. + +To make this less abstract, let's show two examples. + +#### Square root + +Let's say the calculation in question is to compute the square root of a number `x`. The two ways to do it then become: + +- Write the usual square root algorithm in Cairo to compute `sqrt(x)`. +- Write a hint that computes `sqrt(x)`, then immediately after calling the hint show __in Cairo__ that `(sqrt(x))^2 = x`. + +The second approach is exactly what the `sqrt` function in the Cairo common library does: + +``` +// Returns the floor value of the square root of the given value. +// Assumptions: 0 <= value < 2**250. +func sqrt{range_check_ptr}(value) -> felt { + alloc_locals; + local root: felt; + + %{ + from starkware.python.math_utils import isqrt + value = ids.value % PRIME + assert value < 2 ** 250, f"value={value} is outside of the range [0, 2**250)." + assert 2 ** 250 < PRIME + ids.root = isqrt(value) + %} + + assert_nn_le(root, 2 ** 125 - 1); + tempvar root_plus_one = root + 1; + assert_in_range(value, root * root, root_plus_one * root_plus_one); + + return root; +} +``` + +If you read it carefully, you'll see that the hint in this function computes the square root in python, then this line + +``` +assert_in_range(value, root * root, root_plus_one * root_plus_one); +``` + +asserts __in Cairo__ that `(sqrt(x))^2 = x`. + +This is done this way because it is much cheaper, in terms of the generated trace (and thus proving time), to square a number than compute its square root. + +Notice that the last assert is absolutely mandatory to make this safe. If you forget to write it, the square root calculation does not get proven, and anyone could convince the verifier that the result of `sqrt(x)` is any number they like. + +#### Linear search turned into an O(1) lookup + +This example is taken from the [Cairo documentation](https://docs.cairo-lang.org/0.12.0/hello_cairo/program_input.html). + +Given a list of `(key, value)` pairs, if we want to write a `get_value_by_key` function that returns the value associated to a given key, there are two ways to do it: + +- Write a linear search in Cairo, iterating over each key until you find the requested one. +- Do that exact same linear search *inside a hint*, find the result, then show that the result's key is the one requested. + +Again, the second approach makes the resulting trace and proving much faster, because it's just a lookup; there's no linear search. Notice this only applies to proving, the VM has to execute the hint, so there's still a linear search when executing to generate the trace. In fact, the second approach is more expensive for the VM than the first one. It has to do both a linear search and a lookup. This is a tradeoff in favor of proving time. + +Also note that, as in the square root example, when writing this logic you need to remember to show the hint's result is the correct one in Cairo. If you don't, your code is not being proven. + +### Non-determinism + +The Cairo paper and documentation refers to this second approach to calculating things through hints as *non-determinism*. The reason for this is that sometimes there is more than one result that satisfies a certain condition. This means that cairo execution becomes non deterministic; a hint could output multiple values, and in principle there is no way to know which one it's going to be. Running the same code multiple times could give different results. + +The square root is an easy example of this. The condition `(sqrt(x))^2 = x` is not unique, there are two solutions to it. Without the hint, this is non-deterministic, `x` could have multiple values; the hint resolves that by choosing a specific value when being run. + +### Common Library and Hints + +As explained above, using hints in your code is highly unsafe. Forgetting to add a check after calling them can make your code vulnerable to any sorts of attacks, as your program will not prove what you think it proves. + +Because of this, most hints in Cairo 0 are wrapped around or used by functions in the Cairo common library that do the checks for you, thus making them safe to use. Ideally, Cairo developers should not be using hints on their own; only transparently through Cairo library functions they call. + +### Whitelisted Hints + +In Cairo, a hint could be any Python code you like. In the context of it as just another language someone might want to use, this is fine. In the context of Cairo as a programming language used to write smart contracts deployed on a blockchain, it's not. Users could deploy contracts with hints that simply do + +```python +while true: + pass +``` + +and grind the network down to a halt, as nodes get stuck executing an infinite loop when calling the contract. + +To address this, the starknet network maintains a list of *whitelisted* hints, which are the only ones that can be used in starknet contracts. These are the ones implemented in this VM. + +## Builtins + +A builtin is a low level optimization integrated into the core loop of the VM that allows otherwise expensive computation to be performed more efficiently. Builtins have two ways to operate: via validation rules and via auto-deduction rules. + +- Validation rules are applied to every element that is inserted into a builtin's segment. For example, if I want to verify an ecdsa signature, I can insert it into the ecdsa builtin's segment and let a validation rule take care of verifying the signature. +- Auto-deduction rules take over during instruction execution, when we can't compute the value of an operand who's address belongs to a builtin segment, we can use that builtin's auto-deduction rule to calculate the value of the operand. For example, If I want to calculate the pedersen hash of two values, I can write the values into the pedersen builtin's segment and then ask for the next memory cell, without builtins, this instruction would have failed, as there is no value stored in that cell, but now we can use auto-deduction rules to calculate the hash and fill in that memory cell. + +## Program parsing + +The input of the Virtual Machine is a compiled Cairo program in Json format. The main parts of the file are listed below: + +- **data:** List of hexadecimal values that represent the instructions and immediate values defined in the cairo program. Each hexadecimal value is stored as a maybe_relocatable element in memory, but they can only be felts because the decoder has to be able to get the instruction fields in its bit representation. +- **debug_info:** This field provides information about the instructions defined in the data list. Each one is identified with its index inside the data list. For each one it contains information about the cairo variables in scope, the hints executed before that instruction if any, and its location inside the cairo program. +- **hints:** All the hints used in the program, ordered by the pc offset at which they should be executed. +- **identifiers:** User-defined symbols in the Cairo code representing variables, functions, classes, etc. with unique names. The expected offset, type and its corresponding information is provided for each identifier + + For example, the identifier representing the main function (usually the entrypoint of the program) is of `function` type, and a list of decorators wrappers (if there are any) are provided as additional information. + Another example is a user defined struct, is of `struct` type, it provides its size, the members it contains (with its information) and more. + +- **main_scope:** Usually something like `__main__`. All the identifiers associated with main function will be identified as `__main__`.identifier_name. Useful to identify the entrypoint of the program. +- **prime:** The cairo prime in hexadecimal format. As explained above, all arithmetic operations are done over a base field, modulo this primer number. +- **reference_manager:** Contains information about cairo variables. This information is useful to access to variables when executing cairo hints. + +# Code Walkthrough + +If you want a step by step guide on how to implement your own VM you can read our [code walkthrough in Go](https://github.com/lambdaclass/cairo-vm_in_go?tab=readme-ov-file#code-walkthroughwrite-your-own-cairo-vm) diff --git a/fuzzer/Cargo.lock b/fuzzer/Cargo.lock index e1ccedbfb8..e1ef312c3a 100644 --- a/fuzzer/Cargo.lock +++ b/fuzzer/Cargo.lock @@ -216,7 +216,7 @@ dependencies = [ [[package]] name = "cairo-vm" -version = "1.0.0-rc5" +version = "1.0.1" dependencies = [ "anyhow", "arbitrary", diff --git a/requirements.txt b/requirements.txt index a47d1c9122..d1eb811f6d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ bitarray==2.7.3 fastecdsa==2.3.0 sympy==1.11.1 typeguard==2.13.3 -cairo-lang==0.13.2 +cairo-lang==0.13.3 diff --git a/rust-toolchain b/rust-toolchain index ff6d7a9a85..5988090d17 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "1.80.0" +channel = "1.81.0" components = ["rustfmt", "clippy"] profile = "minimal" diff --git a/vm/Cargo.toml b/vm/Cargo.toml index 07bcde08cd..a01a0ce2c0 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -29,6 +29,8 @@ cairo-1-hints = [ ] tracer = [] mod_builtin = [] +cairo-0-secp-hints = [] +cairo-0-data-availability-hints = [] # Note that these features are not retro-compatible with the cairo Python VM. test_utils = ["std", "dep:arbitrary", "starknet-types-core/arbitrary", "starknet-types-core/std"] # This feature will reference every test-oriented feature @@ -89,6 +91,8 @@ num-prime = { version = "0.4.3", features = ["big-int"] } [target.'cfg(target_arch = "wasm32")'.dev-dependencies] wasm-bindgen-test = "0.3.34" +# The js feature needs to be enabled +getrandom = { version = "0.2", features = ["js"]} [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] iai-callgrind = "0.3.1" diff --git a/vm/src/air_private_input.rs b/vm/src/air_private_input.rs index b76575eaff..421c1b3fa0 100644 --- a/vm/src/air_private_input.rs +++ b/vm/src/air_private_input.rs @@ -126,6 +126,8 @@ pub struct ModInputInstance { pub values_ptr: usize, pub offsets_ptr: usize, pub n: usize, + #[serde(deserialize_with = "mod_input_instance_batch_serde::deserialize")] + #[serde(serialize_with = "mod_input_instance_batch_serde::serialize")] pub batch: BTreeMap, } @@ -205,6 +207,88 @@ impl AirPrivateInputSerializable { } } +mod mod_input_instance_batch_serde { + use super::*; + + use serde::{Deserializer, Serializer}; + + pub(crate) fn serialize( + value: &BTreeMap, + s: S, + ) -> Result { + let value = value.iter().map(|v| v.1).collect::>(); + + value.serialize(s) + } + + pub(crate) fn deserialize<'de, D: Deserializer<'de>>( + d: D, + ) -> Result, D::Error> { + let value = Vec::::deserialize(d)?; + + Ok(value.into_iter().enumerate().collect()) + } + + #[cfg(feature = "std")] + #[test] + fn test_serde() { + let input_value = vec![ + ( + 0, + ModInputMemoryVars { + a_offset: 5, + b_offset: 5, + c_offset: 5, + a0: Felt252::from(5u32), + a1: Felt252::from(5u32), + a2: Felt252::from(5u32), + a3: Felt252::from(5u32), + b0: Felt252::from(5u32), + b1: Felt252::from(5u32), + b2: Felt252::from(5u32), + b3: Felt252::from(5u32), + c0: Felt252::from(5u32), + c1: Felt252::from(5u32), + c2: Felt252::from(5u32), + c3: Felt252::from(5u32), + }, + ), + ( + 1, + ModInputMemoryVars { + a_offset: 7, + b_offset: 7, + c_offset: 7, + a0: Felt252::from(7u32), + a1: Felt252::from(7u32), + a2: Felt252::from(7u32), + a3: Felt252::from(7u32), + b0: Felt252::from(7u32), + b1: Felt252::from(7u32), + b2: Felt252::from(7u32), + b3: Felt252::from(7u32), + c0: Felt252::from(7u32), + c1: Felt252::from(7u32), + c2: Felt252::from(7u32), + c3: Felt252::from(7u32), + }, + ), + ] + .into_iter() + .collect::>(); + + let bytes = Vec::new(); + let mut serializer = serde_json::Serializer::new(bytes); + serialize(&input_value, &mut serializer).unwrap(); + let bytes = serializer.into_inner(); + + let mut deserializer = serde_json::Deserializer::from_slice(&bytes); + let output_value = deserialize(&mut deserializer).unwrap(); + + assert_eq!(input_value, output_value); + } +} + #[cfg(test)] mod tests { use crate::types::layout_name::LayoutName; diff --git a/vm/src/cairo_run.rs b/vm/src/cairo_run.rs index de2540fb2e..0e61856a3b 100644 --- a/vm/src/cairo_run.rs +++ b/vm/src/cairo_run.rs @@ -1,6 +1,9 @@ use crate::{ hint_processor::hint_processor_definition::HintProcessor, - types::{builtin_name::BuiltinName, layout_name::LayoutName, program::Program}, + types::{ + builtin_name::BuiltinName, layout::CairoLayoutParams, layout_name::LayoutName, + program::Program, + }, vm::{ errors::{ cairo_run_errors::CairoRunError, runner_errors::RunnerError, vm_exception::VmException, @@ -26,6 +29,9 @@ pub struct CairoRunConfig<'a> { pub trace_enabled: bool, pub relocate_mem: bool, pub layout: LayoutName, + /// The `dynamic_layout_params` argument should only be used with dynamic layout. + /// It is ignored otherwise. + pub dynamic_layout_params: Option, pub proof_mode: bool, pub secure_run: Option, pub disable_trace_padding: bool, @@ -43,6 +49,7 @@ impl<'a> Default for CairoRunConfig<'a> { secure_run: None, disable_trace_padding: false, allow_missing_builtins: None, + dynamic_layout_params: None, } } } @@ -65,6 +72,7 @@ pub fn cairo_run_program_with_initial_scope( let mut cairo_runner = CairoRunner::new( program, cairo_run_config.layout, + cairo_run_config.dynamic_layout_params.clone(), cairo_run_config.proof_mode, cairo_run_config.trace_enabled, )?; @@ -151,6 +159,7 @@ pub fn cairo_run_pie( let mut cairo_runner = CairoRunner::new( &program, cairo_run_config.layout, + cairo_run_config.dynamic_layout_params.clone(), false, cairo_run_config.trace_enabled, )?; @@ -223,6 +232,7 @@ pub fn cairo_run_fuzzed_program( let mut cairo_runner = CairoRunner::new( &program, cairo_run_config.layout, + cairo_run_config.dynamic_layout_params.clone(), cairo_run_config.proof_mode, cairo_run_config.trace_enabled, )?; 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 fcc6ad0dcc..f5ae7a96c4 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 @@ -1,3 +1,5 @@ +#[cfg(feature = "cairo-0-secp-hints")] +use super::secp::cairo0_hints; use super::{ blake2s_utils::finalize_blake2s_v3, ec_recover::{ @@ -874,6 +876,105 @@ impl HintProcessorLogic for BuiltinHintProcessor { constants, exec_scopes, ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::COMPUTE_Q_MOD_PRIME => cairo0_hints::compute_q_mod_prime( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::COMPUTE_IDS_HIGH_LOW => cairo0_hints::compute_ids_high_low( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::SECP_DOUBLE_ASSIGN_NEW_X => cairo0_hints::secp_double_assign_new_x( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::FAST_SECP_ADD_ASSIGN_NEW_Y => cairo0_hints::fast_secp_add_assign_new_y( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::COMPUTE_VALUE_DIV_MOD => cairo0_hints::compute_value_div_mod( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::GENERATE_NIBBLES => cairo0_hints::generate_nibbles( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::WRITE_NIBBLES_TO_MEM => cairo0_hints::write_nibbles_to_mem( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::IS_ON_CURVE_2 => cairo0_hints::is_on_curve_2( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::SECP_R1_GET_POINT_FROM_X => cairo0_hints::r1_get_point_from_x( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::SECP_REDUCE => cairo0_hints::reduce_value( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-secp-hints")] + cairo0_hints::SECP_REDUCE_X => cairo0_hints::reduce_x( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + #[cfg(feature = "cairo-0-data-availability-hints")] + super::kzg_da::WRITE_DIVMOD_SEGMENT => super::kzg_da::write_div_mod_segment( + vm, + exec_scopes, + &hint_data.ids_data, + &hint_data.ap_tracking, + constants, + ), + code => Err(HintError::UnknownHint(code.to_string().into_boxed_str())), } } 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 74caa13473..e104cb7e86 100644 --- a/vm/src/hint_processor/builtin_hint_processor/hint_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/hint_utils.rs @@ -149,7 +149,7 @@ mod tests { fn get_ptr_from_var_name_immediate_value() { let mut vm = vm!(); vm.segments = segments![((1, 0), (0, 0))]; - let mut hint_ref = HintReference::new(0, 0, true, false); + let mut hint_ref = HintReference::new(0, 0, true, false, true); hint_ref.offset2 = OffsetValue::Value(2); let ids_data = HashMap::from([("imm".to_string(), hint_ref)]); diff --git a/vm/src/hint_processor/builtin_hint_processor/kzg_da/mod.rs b/vm/src/hint_processor/builtin_hint_processor/kzg_da/mod.rs new file mode 100644 index 0000000000..8da7274056 --- /dev/null +++ b/vm/src/hint_processor/builtin_hint_processor/kzg_da/mod.rs @@ -0,0 +1,108 @@ +use core::str::FromStr; + +use super::{ + hint_utils::get_relocatable_from_var_name, + secp::{bigint_utils::BigInt3, secp_utils::SECP_P}, +}; +use crate::{ + hint_processor::hint_processor_definition::HintReference, + serde::deserialize_program::ApTracking, + types::relocatable::MaybeRelocatable, + vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, + Felt252, +}; +use crate::{ + stdlib::{collections::HashMap, ops::Deref, prelude::*}, + types::exec_scope::ExecutionScopes, +}; +use lazy_static::lazy_static; +use num_bigint::BigInt; +use num_integer::Integer; +use num_traits::FromPrimitive; +use num_traits::Zero; + +lazy_static! { + static ref BLS_BASE: BigInt = BigInt::from_u64(2).unwrap().pow(86); + static ref BLS_PRIME: BigInt = BigInt::from_str( + "52435875175126190479447740508185965837690552500527637822603658699938581184513" + ) + .unwrap(); +} +pub const WRITE_DIVMOD_SEGMENT: &str = r#"from starkware.starknet.core.os.data_availability.bls_utils import BLS_PRIME, pack, split + +a = pack(ids.a, PRIME) +b = pack(ids.b, PRIME) + +q, r = divmod(a * b, BLS_PRIME) + +# By the assumption: |a|, |b| < 2**104 * ((2**86) ** 2 + 2**86 + 1) < 2**276.001. +# Therefore |q| <= |ab| / BLS_PRIME < 2**299. +# Hence the absolute value of the high limb of split(q) < 2**127. +segments.write_arg(ids.q.address_, split(q)) +segments.write_arg(ids.res.address_, split(r))"#; + +pub fn write_div_mod_segment( + vm: &mut VirtualMachine, + _exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let a = bls_pack( + &BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?, + &SECP_P, + ); + let b = bls_pack( + &BigInt3::from_var_name("b", vm, ids_data, ap_tracking)?, + &SECP_P, + ); + let (q, r) = (a * b).div_mod_floor(&BLS_PRIME); + let q_reloc = get_relocatable_from_var_name("q", vm, ids_data, ap_tracking)?; + let res_reloc = get_relocatable_from_var_name("res", vm, ids_data, ap_tracking)?; + + let q_arg: Vec = bls_split(q) + .into_iter() + .map(|ref n| Felt252::from(n).into()) + .collect::>(); + let res_arg: Vec = bls_split(r) + .into_iter() + .map(|ref n| Felt252::from(n).into()) + .collect::>(); + vm.write_arg(q_reloc, &q_arg).map_err(HintError::Memory)?; + vm.write_arg(res_reloc, &res_arg) + .map_err(HintError::Memory)?; + Ok(()) +} + +fn bls_split(mut num: BigInt) -> Vec { + use num_traits::Signed; + let mut a = Vec::new(); + for _ in 0..2 { + let residue = &num % BLS_BASE.deref(); + num /= BLS_BASE.deref(); + a.push(residue); + } + assert!(num.abs() < BigInt::from_u128(1 << 127).unwrap()); + a.push(num); + a +} + +fn as_int(value: BigInt, prime: &BigInt) -> BigInt { + let half_prime = prime / 2u32; + if value > half_prime { + value - prime + } else { + value + } +} + +fn bls_pack(z: &BigInt3, prime: &BigInt) -> BigInt { + let limbs = &z.limbs; + limbs + .iter() + .enumerate() + .fold(BigInt::zero(), |acc, (i, limb)| { + let limb_as_int = as_int(limb.to_bigint(), prime); + acc + limb_as_int * &BLS_BASE.pow(i as u32) + }) +} 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 e10467b6ee..eb65aed496 100644 --- a/vm/src/hint_processor/builtin_hint_processor/math_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/math_utils.rs @@ -2053,8 +2053,14 @@ mod tests { //Create ids let ids_data = HashMap::from([ ("value".to_string(), HintReference::new_simple(-4)), - ("low".to_string(), HintReference::new(-3, 0, true, true)), - ("high".to_string(), HintReference::new(-3, 1, true, true)), + ( + "low".to_string(), + HintReference::new(-3, 0, true, true, true), + ), + ( + "high".to_string(), + HintReference::new(-3, 1, true, true, true), + ), ]); //Execute the hint assert_matches!( @@ -2131,8 +2137,14 @@ mod tests { //Create ids_data & hint_data let ids_data = HashMap::from([ ("value".to_string(), HintReference::new_simple(-4)), - ("low".to_string(), HintReference::new(-3, 0, true, true)), - ("high".to_string(), HintReference::new(-3, 1, true, true)), + ( + "low".to_string(), + HintReference::new(-3, 0, true, true, true), + ), + ( + "high".to_string(), + HintReference::new(-3, 1, true, true, true), + ), ]); //Execute the hint @@ -2175,8 +2187,14 @@ mod tests { //Create ids_data & hint_data let ids_data = HashMap::from([ ("value".to_string(), HintReference::new_simple(-4)), - ("low".to_string(), HintReference::new(-3, 0, true, true)), - ("high".to_string(), HintReference::new(-3, 1, true, true)), + ( + "low".to_string(), + HintReference::new(-3, 0, true, true, true), + ), + ( + "high".to_string(), + HintReference::new(-3, 1, true, true, true), + ), ]); //Execute the hint assert_matches!( @@ -2213,8 +2231,14 @@ mod tests { //Create ids_data & hint_data let ids_data = HashMap::from([ ("value".to_string(), HintReference::new_simple(-4)), - ("low".to_string(), HintReference::new(-3, 0, true, true)), - ("high".to_string(), HintReference::new(-3, 1, true, true)), + ( + "low".to_string(), + HintReference::new(-3, 0, true, true, true), + ), + ( + "high".to_string(), + HintReference::new(-3, 1, true, true, true), + ), ]); //Execute the hint assert_matches!( @@ -2251,8 +2275,14 @@ mod tests { //Create ids let ids_data = HashMap::from([ ("value".to_string(), HintReference::new_simple(-4)), - ("low".to_string(), HintReference::new(-3, 0, true, true)), - ("high".to_string(), HintReference::new(-3, 1, true, true)), + ( + "low".to_string(), + HintReference::new(-3, 0, true, true, true), + ), + ( + "high".to_string(), + HintReference::new(-3, 1, true, true, true), + ), ]); //Execute the hint assert_matches!( @@ -2277,8 +2307,14 @@ mod tests { //Create ids let ids_data = HashMap::from([ ("value".to_string(), HintReference::new_simple(-4)), - ("low".to_string(), HintReference::new(-3, 0, true, true)), - ("high".to_string(), HintReference::new(-3, 1, true, true)), + ( + "low".to_string(), + HintReference::new(-3, 0, true, true, true), + ), + ( + "high".to_string(), + HintReference::new(-3, 1, true, true, true), + ), ]); //Execute the hint assert_matches!( @@ -2315,8 +2351,14 @@ mod tests { //Create ids let ids_data = HashMap::from([ ("value".to_string(), HintReference::new_simple(-4)), - ("low".to_string(), HintReference::new(-3, 0, true, true)), - ("high".to_string(), HintReference::new(-3, 1, true, true)), + ( + "low".to_string(), + HintReference::new(-3, 0, true, true, true), + ), + ( + "high".to_string(), + HintReference::new(-3, 1, true, true, true), + ), ]); //Execute the hint assert_matches!( diff --git a/vm/src/hint_processor/builtin_hint_processor/mod.rs b/vm/src/hint_processor/builtin_hint_processor/mod.rs index dd647ca944..497c556913 100644 --- a/vm/src/hint_processor/builtin_hint_processor/mod.rs +++ b/vm/src/hint_processor/builtin_hint_processor/mod.rs @@ -38,3 +38,7 @@ pub mod uint384_extension; pub mod uint_utils; pub mod usort; pub mod vrf; + +#[cfg(feature = "cairo-0-data-availability-hints")] +#[cfg_attr(docsrs, doc(cfg(feature = "cairo-0-data-availability-hints")))] +pub mod kzg_da; diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs b/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs new file mode 100644 index 0000000000..6fec9cb10f --- /dev/null +++ b/vm/src/hint_processor/builtin_hint_processor/secp/cairo0_hints.rs @@ -0,0 +1,671 @@ +use crate::stdlib::{ + collections::HashMap, + ops::Deref, + ops::{Add, Mul, Rem}, + prelude::*, +}; + +use crate::hint_processor::builtin_hint_processor::hint_utils::{ + get_constant_from_var_name, get_integer_from_var_name, insert_value_from_var_name, +}; +use crate::hint_processor::builtin_hint_processor::uint256_utils::Uint256; +use crate::hint_processor::hint_processor_definition::HintReference; +use crate::math_utils::{div_mod, signed_felt}; +use crate::serde::deserialize_program::ApTracking; +use crate::types::errors::math_errors::MathError; +use crate::types::exec_scope::ExecutionScopes; +use crate::vm::errors::hint_errors::HintError; +use crate::vm::vm_core::VirtualMachine; +use crate::Felt252; +use num_bigint::{BigInt, BigUint}; +use num_integer::Integer; +use num_traits::One; +use num_traits::Zero; + +use super::bigint_utils::{BigInt3, Uint384}; +use super::ec_utils::EcPoint; +use super::secp_utils::{SECP256R1_ALPHA, SECP256R1_B, SECP256R1_P}; + +pub const SECP_REDUCE: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack +value = pack(ids.x, PRIME) % SECP256R1_P"#; +pub fn reduce_value( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)?.pack86(); + exec_scopes.insert_value("value", x.mod_floor(&SECP256R1_P)); + Ok(()) +} + +pub const SECP_REDUCE_X: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack + +x = pack(ids.x, PRIME) % SECP256R1_P"#; +pub fn reduce_x( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)?.pack86(); + exec_scopes.insert_value("x", x.mod_floor(&SECP256R1_P)); + Ok(()) +} + +pub const COMPUTE_Q_MOD_PRIME: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack + +q, r = divmod(pack(ids.val, PRIME), SECP256R1_P) +assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." +ids.q = q % PRIME"#; +pub fn compute_q_mod_prime( + vm: &mut VirtualMachine, + _exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let val = Uint384::from_var_name("val", vm, ids_data, ap_tracking)?.pack86(); + let (q, r) = val.div_mod_floor(&SECP256R1_P); + if !r.is_zero() { + return Err(HintError::SecpVerifyZero(Box::new(val))); + } + insert_value_from_var_name("q", Felt252::from(&q), vm, ids_data, ap_tracking)?; + Ok(()) +} + +pub const COMPUTE_IDS_HIGH_LOW: &str = r#"from starkware.cairo.common.math_utils import as_int + +# Correctness check. +value = as_int(ids.value, PRIME) % PRIME +assert value < ids.UPPER_BOUND, f'{value} is outside of the range [0, 2**165).' + +# Calculation for the assertion. +ids.high, ids.low = divmod(ids.value, ids.SHIFT)"#; +pub fn compute_ids_high_low( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + constants: &HashMap, +) -> Result<(), HintError> { + exec_scopes.insert_value::("SECP256R1_P", SECP256R1_P.clone()); + + const UPPER_BOUND: &str = "starkware.cairo.common.math.assert_250_bit.UPPER_BOUND"; + const SHIFT: &str = "starkware.cairo.common.math.assert_250_bit.SHIFT"; + + let upper_bound = constants + .get(UPPER_BOUND) + .map_or_else(|| get_constant_from_var_name("UPPER_BOUND", constants), Ok)?; + 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( + "value", + vm, + ids_data, + ap_tracking, + )?)); + if &value > upper_bound { + return Err(HintError::ValueOutside250BitRange(Box::new(value))); + } + + let (high, low) = value.div_rem(&shift.try_into().map_err(|_| MathError::DividedByZero)?); + insert_value_from_var_name("high", high, vm, ids_data, ap_tracking)?; + insert_value_from_var_name("low", low, vm, ids_data, ap_tracking)?; + Ok(()) +} + +pub const SECP_R1_GET_POINT_FROM_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP256R1, pack +from starkware.python.math_utils import y_squared_from_x + +y_square_int = y_squared_from_x( + x=pack(ids.x, SECP256R1.prime), + alpha=SECP256R1.alpha, + beta=SECP256R1.beta, + field_prime=SECP256R1.prime, +) + +# Note that (y_square_int ** ((SECP256R1.prime + 1) / 4)) ** 2 = +# = y_square_int ** ((SECP256R1.prime + 1) / 2) = +# = y_square_int ** ((SECP256R1.prime - 1) / 2 + 1) = +# = y_square_int * y_square_int ** ((SECP256R1.prime - 1) / 2) = y_square_int * {+/-}1. +y = pow(y_square_int, (SECP256R1.prime + 1) // 4, SECP256R1.prime) + +# We need to decide whether to take y or prime - y. +if ids.v % 2 == y % 2: + value = y +else: + value = (-y) % SECP256R1.prime"#; + +pub fn r1_get_point_from_x( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + exec_scopes.insert_value::("SECP256R1_P", SECP256R1_P.clone()); + + // def y_squared_from_x(x: int, alpha: int, beta: int, field_prime: int) -> int: + // """ + // Computes y^2 using the curve equation: + // y^2 = x^3 + alpha * x + beta (mod field_prime) + // """ + // return (pow(x, 3, field_prime) + alpha * x + beta) % field_prime + fn y_squared_from_x(x: &BigInt, alpha: &BigInt, beta: &BigInt, field_prime: &BigInt) -> BigInt { + // Compute x^3 (mod field_prime) + let x_cubed = x.modpow(&BigInt::from(3), field_prime); + + // Compute alpha * x + let alpha_x = alpha.mul(x); + + // Compute y^2 = (x^3 + alpha * x + beta) % field_prime + x_cubed.add(&alpha_x).add(beta).rem(field_prime) + } + + // prime = curve.prime + // y_squared = y_squared_from_x( + // x=x, + // alpha=curve.alpha, + // beta=curve.beta, + // field_prime=prime, + // ) + + // y = pow(y_squared, (prime + 1) // 4, prime) + // if (y & 1) != request.y_parity: + // y = (-y) % prime + + let x = Uint384::from_var_name("x", vm, ids_data, ap_tracking)? + .pack86() + .mod_floor(&SECP256R1_P); + + let y_square_int = y_squared_from_x(&x, &SECP256R1_ALPHA, &SECP256R1_B, &SECP256R1_P); + exec_scopes.insert_value::("y_square_int", y_square_int.clone()); + + // Calculate (prime + 1) // 4 + let exp = (SECP256R1_P.to_owned() + BigInt::one()).div_floor(&BigInt::from(4)); + // Calculate pow(y_square_int, exp, prime) + let y = y_square_int.modpow(&exp, &SECP256R1_P); + exec_scopes.insert_value::("y", y.clone()); + + let v = get_integer_from_var_name("v", vm, ids_data, ap_tracking)?.to_biguint(); + if v.is_even() == y.is_even() { + exec_scopes.insert_value("value", y); + } else { + let value = (-y).mod_floor(&SECP256R1_P); + exec_scopes.insert_value("value", value); + } + Ok(()) +} + +pub const IS_ON_CURVE_2: &str = r#"ids.is_on_curve = (y * y) % SECP256R1.prime == y_square_int"#; + +pub fn is_on_curve_2( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let y: BigInt = exec_scopes.get("y")?; + let y_square_int: BigInt = exec_scopes.get("y_square_int")?; + + let is_on_curve = ((y.pow(2)) % SECP256R1_P.to_owned()) == y_square_int; + insert_value_from_var_name( + "is_on_curve", + Felt252::from(is_on_curve), + vm, + ids_data, + ap_tracking, + )?; + + Ok(()) +} + +pub const SECP_DOUBLE_ASSIGN_NEW_X: &str = r#"from starkware.cairo.common.cairo_secp.secp256r1_utils import SECP256R1_P +from starkware.cairo.common.cairo_secp.secp_utils import pack + +slope = pack(ids.slope, SECP256R1_P) +x = pack(ids.point.x, SECP256R1_P) +y = pack(ids.point.y, SECP256R1_P) + +value = new_x = (pow(slope, 2, SECP256R1_P) - 2 * x) % SECP256R1_P"#; + +pub fn secp_double_assign_new_x( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + exec_scopes.insert_value::("SECP256R1_P", SECP256R1_P.clone()); + //ids.slope + let slope = BigInt3::from_var_name("slope", vm, ids_data, ap_tracking)?; + //ids.point + let point = EcPoint::from_var_name("point", vm, ids_data, ap_tracking)?; + + let slope = slope.pack86().mod_floor(&SECP256R1_P); + let x = point.x.pack86().mod_floor(&SECP256R1_P); + let y = point.y.pack86().mod_floor(&SECP256R1_P); + + let value = + (slope.modpow(&(2usize.into()), &SECP256R1_P) - (&x << 1u32)).mod_floor(&SECP256R1_P); + + //Assign variables to vm scope + exec_scopes.insert_value("slope", slope); + exec_scopes.insert_value("x", x); + exec_scopes.insert_value("y", y); + exec_scopes.insert_value("value", value.clone()); + exec_scopes.insert_value("new_x", value); + Ok(()) +} + +pub const GENERATE_NIBBLES: &str = r#"num = (ids.scalar.high << 128) + ids.scalar.low +nibbles = [(num >> i) & 0xf for i in range(0, 256, 4)] +ids.first_nibble = nibbles.pop() +ids.last_nibble = nibbles[0]"#; +pub fn generate_nibbles( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + ids_data: &HashMap, + ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let num = Uint256::from_var_name("scalar", vm, ids_data, ap_tracking)?.pack(); + + // Generate nibbles + let mut nibbles: Vec = (0..256) + .step_by(4) + .map(|i| ((&num >> i) & BigUint::from(0xfu8))) + .map(|s: BigUint| s.into()) + .collect(); + + // ids.first_nibble = nibbles.pop() + let first_nibble = nibbles.pop().ok_or(HintError::EmptyNibbles)?; + + insert_value_from_var_name("first_nibble", first_nibble, vm, ids_data, ap_tracking)?; + + // ids.last_nibble = nibbles[0] + let last_nibble = *nibbles.first().ok_or(HintError::EmptyNibbles)?; + insert_value_from_var_name("last_nibble", last_nibble, vm, ids_data, ap_tracking)?; + exec_scopes.insert_value("nibbles", nibbles); + Ok(()) +} + +pub const FAST_SECP_ADD_ASSIGN_NEW_Y: &str = + r#"value = new_y = (slope * (x - new_x) - y) % SECP256R1_P"#; +pub fn fast_secp_add_assign_new_y( + _vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + _ids_data: &HashMap, + _ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + //Get variables from vm scope + let (slope, x, new_x, y, secp_p) = ( + exec_scopes.get::("slope")?, + exec_scopes.get::("x")?, + exec_scopes.get::("new_x")?, + exec_scopes.get::("y")?, + SECP256R1_P.deref(), + ); + let value = (slope * (x - new_x) - y).mod_floor(secp_p); + exec_scopes.insert_value("value", value.clone()); + exec_scopes.insert_value("new_y", value); + + Ok(()) +} + +pub const WRITE_NIBBLES_TO_MEM: &str = r#"memory[fp + 0] = to_felt_or_relocatable(nibbles.pop())"#; + +pub fn write_nibbles_to_mem( + vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + _ids_data: &HashMap, + _ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + let nibbles: &mut Vec = exec_scopes.get_mut_list_ref("nibbles")?; + let nibble = nibbles.pop().ok_or(HintError::EmptyNibbles)?; + vm.insert_value((vm.get_fp() + 0)?, nibble)?; + + Ok(()) +} + +pub const COMPUTE_VALUE_DIV_MOD: &str = r#"from starkware.python.math_utils import div_mod + +value = div_mod(1, x, SECP256R1_P)"#; +pub fn compute_value_div_mod( + _vm: &mut VirtualMachine, + exec_scopes: &mut ExecutionScopes, + _ids_data: &HashMap, + _ap_tracking: &ApTracking, + _constants: &HashMap, +) -> Result<(), HintError> { + //Get variables from vm scope + let x = exec_scopes.get_ref::("x")?; + + let value = div_mod(&BigInt::one(), x, &SECP256R1_P)?; + exec_scopes.insert_value("value", value); + + Ok(()) +} + +#[cfg(test)] +mod tests { + + use assert_matches::assert_matches; + + use crate::utils::test_utils::*; + + use super::*; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::*; + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_is_on_curve_2() { + let mut vm = VirtualMachine::new(false); + vm.set_fp(1); + let ids_data = non_continuous_ids_data![("is_on_curve", -1)]; + vm.segments = segments![((1, 0), 1)]; + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + let y = BigInt::from(1234); + let y_square_int = y.clone() * y.clone(); + + exec_scopes.insert_value("y", y); + exec_scopes.insert_value("y_square_int", y_square_int); + + is_on_curve_2( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &Default::default(), + ) + .expect("is_on_curve2() failed"); + + let is_on_curve: Felt252 = + get_integer_from_var_name("is_on_curve", &vm, &ids_data, &ap_tracking) + .expect("is_on_curve2 should be put in ids_data"); + assert_eq!(is_on_curve, 1.into()); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_compute_q_mod_prime() { + let mut vm = VirtualMachine::new(false); + + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + vm.run_context.fp = 9; + //Create hint data + let ids_data = non_continuous_ids_data![("val", -5), ("q", 0)]; + vm.segments = segments![((1, 4), 0), ((1, 5), 0), ((1, 6), 0)]; + compute_q_mod_prime( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &Default::default(), + ) + .expect("compute_q_mod_prime() failed"); + + let q: Felt252 = get_integer_from_var_name("q", &vm, &ids_data, &ap_tracking) + .expect("compute_q_mod_prime should have put 'q' in ids_data"); + assert_eq!(q, Felt252::from(0)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_compute_ids_high_low() { + let mut vm = VirtualMachine::new(false); + + let value = BigInt::from(25); + let shift = BigInt::from(12); + + vm.set_fp(14); + let ids_data = non_continuous_ids_data![ + ("UPPER_BOUND", -14), + ("value", -11), + ("high", -8), + ("low", -5), + ("SHIFT", -2) + ]; + + vm.segments = segments!( + //UPPER_BOUND + ((1, 0), 18446744069414584321), + ((1, 1), 0), + ((1, 2), 0), + //value + ((1, 3), 25), + ((1, 4), 0), + ((1, 5), 0), + //high + ((1, 6), 2), + ((1, 7), 0), + ((1, 8), 0), + //low + ((1, 9), 1), + ((1, 10), 0), + ((1, 11), 0), + //SHIFT + ((1, 12), 12), + ((1, 13), 0), + ((1, 14), 0) + ); + + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + let constants = HashMap::from([ + ( + "UPPER_BOUND".to_string(), + Felt252::from(18446744069414584321_u128), + ), + ("SHIFT".to_string(), Felt252::from(12)), + ]); + compute_ids_high_low( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &constants, + ) + .expect("compute_ids_high_low() failed"); + + let high: Felt252 = get_integer_from_var_name("high", &vm, &ids_data, &ap_tracking) + .expect("compute_ids_high_low should have put 'high' in ids_data"); + let low: Felt252 = get_integer_from_var_name("low", &vm, &ids_data, &ap_tracking) + .expect("compute_ids_high_low should have put 'low' in ids_data"); + + let (expected_high, expected_low) = value.div_rem(&shift); + assert_eq!(high, Felt252::from(expected_high)); + assert_eq!(low, Felt252::from(expected_low)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_r1_get_point_from_x() { + let mut vm = VirtualMachine::new(false); + vm.set_fp(10); + + let ids_data = non_continuous_ids_data![("x", -10), ("v", -7)]; + vm.segments = segments!( + // X + ((1, 0), 18446744069414584321), + ((1, 1), 0), + ((1, 2), 0), + // v + ((1, 3), 1), + ((1, 4), 0), + ((1, 5), 0), + ); + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + let x = BigInt::from(18446744069414584321u128); // Example x value + let v = BigInt::from(1); // Example v value (must be 0 or 1 for even/odd check) + + let constants = HashMap::new(); + + r1_get_point_from_x( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &constants, + ) + .expect("calculate_value() failed"); + + let value: BigInt = exec_scopes + .get("value") + .expect("value should be calculated and stored in exec_scopes"); + + // Compute y_squared_from_x(x) + let y_square_int = (x.modpow(&BigInt::from(3), &SECP256R1_P) + + SECP256R1_ALPHA.deref() * &x + + SECP256R1_B.deref()) + .mod_floor(&SECP256R1_P); + + // Calculate y = pow(y_square_int, (SECP256R1_P + 1) // 4, SECP256R1_P) + let exp = (SECP256R1_P.deref() + BigInt::one()).div_floor(&BigInt::from(4)); + let y = y_square_int.modpow(&exp, &SECP256R1_P); + + // Determine the expected value based on the parity of v and y + let expected_value = if v.is_even() == y.is_even() { + y + } else { + (-y).mod_floor(&SECP256R1_P) + }; + + assert_eq!(value, expected_value); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_reduce_value() { + let mut vm = VirtualMachine::new(false); + + //Initialize fp + vm.run_context.fp = 10; + + //Create hint data + let ids_data = non_continuous_ids_data![("x", -5)]; + + vm.segments = segments![ + ( + (1, 5), + ( + "1113660525233188137217661511617697775365785011829423399545361443", + 10 + ) + ), + ( + (1, 6), + ( + "1243997169368861650657124871657865626433458458266748922940703512", + 10 + ) + ), + ( + (1, 7), + ( + "1484456708474143440067316914074363277495967516029110959982060577", + 10 + ) + ) + ]; + + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + reduce_value( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &Default::default(), + ) + .expect("reduce_value() failed"); + + assert_matches!( + exec_scopes.get::("value"), + Ok(x) if x == bigint_str!( + "78544963828434122936060793808853327022047551513756524908970552805092599079793" + ) + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_reduce_x() { + let mut vm = VirtualMachine::new(false); + + //Initialize fp + vm.run_context.fp = 10; + + //Create hint data + let ids_data = non_continuous_ids_data![("x", -5)]; + + vm.segments = segments![ + ( + (1, 5), + ( + "1113660525233188137217661511617697775365785011829423399545361443", + 10 + ) + ), + ( + (1, 6), + ( + "1243997169368861650657124871657865626433458458266748922940703512", + 10 + ) + ), + ( + (1, 7), + ( + "1484456708474143440067316914074363277495967516029110959982060577", + 10 + ) + ) + ]; + + let ap_tracking = ApTracking::default(); + + let mut exec_scopes = ExecutionScopes::new(); + + reduce_x( + &mut vm, + &mut exec_scopes, + &ids_data, + &ap_tracking, + &Default::default(), + ) + .expect("x() failed"); + + assert_matches!( + exec_scopes.get::("x"), + Ok(x) if x == bigint_str!( + "78544963828434122936060793808853327022047551513756524908970552805092599079793" + ) + ); + } +} 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 fdafd49e07..4eb99ac6d4 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 @@ -27,12 +27,12 @@ use num_traits::{One, ToPrimitive, Zero}; use super::secp_utils::SECP256R1_P; #[derive(Debug, PartialEq)] -struct EcPoint<'a> { - x: BigInt3<'a>, - y: BigInt3<'a>, +pub(crate) struct EcPoint<'a> { + pub(crate) x: BigInt3<'a>, + pub(crate) y: BigInt3<'a>, } impl EcPoint<'_> { - fn from_var_name<'a>( + pub(crate) fn from_var_name<'a>( name: &'a str, vm: &'a VirtualMachine, ids_data: &'a HashMap, diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/field_utils.rs b/vm/src/hint_processor/builtin_hint_processor/secp/field_utils.rs index fb620f6eab..d521a4b867 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/field_utils.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/field_utils.rs @@ -36,7 +36,7 @@ pub fn verify_zero( ) -> Result<(), HintError> { exec_scopes.insert_value("SECP_P", secp_p.clone()); let val = Uint384::from_var_name("val", vm, ids_data, ap_tracking)?.pack86(); - let (q, r) = val.div_rem(secp_p); + let (q, r) = val.div_mod_floor(secp_p); if !r.is_zero() { return Err(HintError::SecpVerifyZero(Box::new(val))); } @@ -62,7 +62,7 @@ pub fn verify_zero_with_external_const( ) -> Result<(), HintError> { let secp_p = exec_scopes.get_ref("SECP_P")?; let val = Uint384::from_var_name("val", vm, ids_data, ap_tracking)?.pack86(); - let (q, r) = val.div_rem(secp_p); + let (q, r) = val.div_mod_floor(secp_p); if !r.is_zero() { return Err(HintError::SecpVerifyZero(Box::new(val))); } diff --git a/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs b/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs index bb98b7868a..c47d6e5628 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/mod.rs @@ -1,4 +1,6 @@ pub mod bigint_utils; +#[cfg(feature = "cairo-0-secp-hints")] +pub mod cairo0_hints; pub mod ec_utils; pub mod field_utils; pub mod secp_utils; 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 4457c975b3..4d5ffaf8b3 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 @@ -6,7 +6,7 @@ use crate::vm::errors::hint_errors::HintError; use lazy_static::lazy_static; use num_bigint::{BigInt, BigUint}; -use num_traits::Zero; +use num_traits::{Num, Zero}; // Constants in package "starkware.cairo.common.cairo_secp.constants". pub const BASE_86: &str = "starkware.cairo.common.cairo_secp.constants.BASE"; @@ -66,6 +66,11 @@ lazy_static! { pub(crate) static ref SECP256R1_ALPHA: BigInt = BigInt::from_str( "115792089210356248762697446949407573530086143415290314195533631308867097853948" ).unwrap(); + pub(crate) static ref SECP256R1_B: BigInt = BigInt::from_str_radix( + "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", + 16, + ) + .unwrap(); } /* 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 57d72b5bba..b495ab073f 100644 --- a/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs +++ b/vm/src/hint_processor/builtin_hint_processor/secp/signature.rs @@ -118,8 +118,10 @@ pub fn get_point_from_x( .pack86() .mod_floor(&SECP_P); let y_cube_int = (x_cube_int + beta).mod_floor(&SECP_P); + exec_scopes.insert_value("y_square_int", y_cube_int.clone()); // Divide by 4 let mut y = y_cube_int.modpow(&(&*SECP_P + 1_u32).shr(2_u32), &SECP_P); + exec_scopes.insert_value::("y", y.clone()); let v = get_integer_from_var_name("v", vm, ids_data, ap_tracking)?.to_bigint(); if v.is_even() != y.is_even() { diff --git a/vm/src/hint_processor/builtin_hint_processor/segments.rs b/vm/src/hint_processor/builtin_hint_processor/segments.rs index c996c2f9b1..bc95c6c1ba 100644 --- a/vm/src/hint_processor/builtin_hint_processor/segments.rs +++ b/vm/src/hint_processor/builtin_hint_processor/segments.rs @@ -19,7 +19,10 @@ pub fn relocate_segment( ap_tracking: &ApTracking, ) -> Result<(), HintError> { let src_ptr = get_ptr_from_var_name("src_ptr", vm, ids_data, ap_tracking)?; + #[cfg(not(feature = "extensive_hints"))] let dest_ptr = get_ptr_from_var_name("dest_ptr", vm, ids_data, ap_tracking)?; + #[cfg(feature = "extensive_hints")] + let dest_ptr = crate::hint_processor::builtin_hint_processor::hint_utils::get_maybe_relocatable_from_var_name("dest_ptr", vm, ids_data, ap_tracking)?; vm.add_relocation_rule(src_ptr, dest_ptr) .map_err(HintError::Memory)?; diff --git a/vm/src/hint_processor/cairo_1_hint_processor/circuit.rs b/vm/src/hint_processor/cairo_1_hint_processor/circuit.rs new file mode 100644 index 0000000000..1b23a7f0e3 --- /dev/null +++ b/vm/src/hint_processor/cairo_1_hint_processor/circuit.rs @@ -0,0 +1,311 @@ +// Most of the `EvalCircuit` implementation is derived from the `cairo-lang-runner` crate. +// https://github.com/starkware-libs/cairo/blob/main/crates/cairo-lang-runner/src/casm_run/circuit.rs + +use core::ops::Deref; + +use ark_ff::{One, Zero}; +use num_bigint::{BigInt, BigUint, ToBigInt}; +use num_integer::Integer; +use num_traits::Signed; +use starknet_types_core::felt::Felt; + +use crate::{ + stdlib::boxed::Box, + types::relocatable::{MaybeRelocatable, Relocatable}, + vm::{ + errors::{hint_errors::HintError, memory_errors::MemoryError}, + vm_core::VirtualMachine, + }, +}; + +// A gate is defined by 3 offsets, the first two are the inputs and the third is the output. +const OFFSETS_PER_GATE: usize = 3; +// Represents the number of limbs use to represent a single value in a circuit +const LIMBS_COUNT: usize = 4; +// Representes the size of a MulMod and AddMod instance +const MOD_BUILTIN_INSTACE_SIZE: usize = 7; + +struct Circuit<'a> { + vm: &'a mut VirtualMachine, + values_ptr: Relocatable, + add_mod_offsets: Relocatable, + mul_mod_offsets: Relocatable, + modulus: BigUint, +} + +impl Circuit<'_> { + fn read_add_mod_value(&mut self, offset: usize) -> Result, MemoryError> { + self.read_circuit_value((self.add_mod_offsets + offset)?) + } + + fn read_mul_mod_value(&mut self, offset: usize) -> Result, MemoryError> { + self.read_circuit_value((self.mul_mod_offsets + offset)?) + } + + fn read_circuit_value(&mut self, offset: Relocatable) -> Result, MemoryError> { + let value_ptr = self.get_value_ptr(offset)?; + read_circuit_value(self.vm, value_ptr) + } + + fn write_add_mod_value(&mut self, offset: usize, value: BigUint) -> Result<(), MemoryError> { + self.write_circuit_value((self.add_mod_offsets + offset)?, value)?; + + Ok(()) + } + + fn write_mul_mod_value(&mut self, offset: usize, value: BigUint) -> Result<(), MemoryError> { + self.write_circuit_value((self.mul_mod_offsets + offset)?, value)?; + + Ok(()) + } + + fn write_circuit_value( + &mut self, + offset: Relocatable, + value: BigUint, + ) -> Result<(), MemoryError> { + let value_ptr = self.get_value_ptr(offset)?; + write_circuit_value(self.vm, value_ptr, value)?; + + Ok(()) + } + + fn get_value_ptr(&self, address: Relocatable) -> Result { + (self.values_ptr + self.vm.get_integer(address)?.as_ref()).map_err(MemoryError::Math) + } +} + +fn read_circuit_value( + vm: &mut VirtualMachine, + add: Relocatable, +) -> Result, MemoryError> { + let mut res = BigUint::zero(); + + for l in (0..LIMBS_COUNT).rev() { + let add_l = (add + l)?; + match vm.get_maybe(&add_l) { + Some(MaybeRelocatable::Int(limb)) => res = (res << 96) + limb.to_biguint(), + _ => return Ok(None), + } + } + + Ok(Some(res)) +} + +fn write_circuit_value( + vm: &mut VirtualMachine, + add: Relocatable, + mut value: BigUint, +) -> Result<(), MemoryError> { + for l in 0..LIMBS_COUNT { + // get the nth limb from a circuit value + let (new_value, rem) = value.div_rem(&(BigUint::one() << 96u8)); + vm.insert_value((add + l)?, Felt::from(rem))?; + value = new_value; + } + + Ok(()) +} + +// Finds the inverse of a value. +// +// If the value has no inverse, find a nullifier so that: +// value * nullifier = 0 (mod modulus) +fn find_inverse(value: BigUint, modulus: &BigUint) -> Result<(bool, BigUint), HintError> { + let ex_gcd = value + .to_bigint() + .ok_or(HintError::BigUintToBigIntFail)? + .extended_gcd(&modulus.to_bigint().ok_or(HintError::BigUintToBigIntFail)?); + + let gcd = ex_gcd + .gcd + .to_biguint() + .ok_or(HintError::BigIntToBigUintFail)?; + if gcd.is_one() { + return Ok((true, get_modulus(&ex_gcd.x, modulus))); + } + + let nullifier = modulus / gcd; + + Ok((false, nullifier)) +} + +fn get_modulus(value: &BigInt, modulus: &BigUint) -> BigUint { + let value_magnitud = value.magnitude().mod_floor(modulus); + if value.is_negative() { + modulus - value_magnitud + } else { + value_magnitud + } +} + +fn compute_gates( + vm: &mut VirtualMachine, + values_ptr: Relocatable, + add_mod_offsets: Relocatable, + n_add_mods: usize, + mul_mod_offsets: Relocatable, + n_mul_mods: usize, + modulus_ptr: Relocatable, +) -> Result { + let modulus = read_circuit_value(vm, modulus_ptr)?.ok_or(HintError::Memory( + MemoryError::ExpectedInteger(Box::from(modulus_ptr)), + ))?; + + let mut circuit = Circuit { + vm, + values_ptr, + add_mod_offsets, + mul_mod_offsets, + modulus, + }; + + let mut addmod_idx = 0; + let mut mulmod_idx = 0; + + // Only mul gates can make the evaluation fail + let mut first_failure_idx = n_mul_mods; + + loop { + while addmod_idx < n_add_mods { + let lhs = circuit.read_add_mod_value(3 * addmod_idx)?; + let rhs = circuit.read_add_mod_value(3 * addmod_idx + 1)?; + + match (lhs, rhs) { + (Some(l), Some(r)) => { + let res = (l + r) % &circuit.modulus; + circuit.write_add_mod_value(3 * addmod_idx + 2, res)?; + } + // sub gate: lhs = res - rhs + (None, Some(r)) => { + let Some(res) = circuit.read_add_mod_value(3 * addmod_idx + 2)? else { + break; + }; + let value = (res + &circuit.modulus - r) % &circuit.modulus; + circuit.write_add_mod_value(3 * addmod_idx, value)?; + } + _ => break, + } + + addmod_idx += 1; + } + + if mulmod_idx == n_mul_mods { + break; + } + + let lhs = circuit.read_mul_mod_value(3 * mulmod_idx)?; + let rhs = circuit.read_mul_mod_value(3 * mulmod_idx + 1)?; + + match (lhs, rhs) { + (Some(l), Some(r)) => { + let res = (l * r) % &circuit.modulus; + circuit.write_mul_mod_value(3 * mulmod_idx + 2, res)?; + } + // inverse gate: lhs = 1 / rhs + (None, Some(r)) => { + let (success, res) = find_inverse(r, &circuit.modulus)?; + circuit.write_mul_mod_value(3 * mulmod_idx, res)?; + + if !success { + first_failure_idx = mulmod_idx; + break; + } + } + _ => { + // this should be unreachable as it would mean that the + //circuit being evaluated is not complete and therefore invalid + return Err(HintError::CircuitEvaluationFailed(Box::from( + "Unexpected None value while filling mul_mod gate", + ))); + } + } + + mulmod_idx += 1; + } + + Ok(first_failure_idx) +} + +fn fill_instances( + vm: &mut VirtualMachine, + built_ptr: Relocatable, + n_instances: usize, + modulus: [Felt; LIMBS_COUNT], + values_ptr: Relocatable, + mut offsets_ptr: Relocatable, +) -> Result<(), HintError> { + for i in 0..n_instances { + let instance_ptr = (built_ptr + i * MOD_BUILTIN_INSTACE_SIZE)?; + + for (idx, value) in modulus.iter().enumerate() { + vm.insert_value((instance_ptr + idx)?, *value)?; + } + + vm.insert_value((instance_ptr + 4)?, values_ptr)?; + vm.insert_value((instance_ptr + 5)?, offsets_ptr)?; + offsets_ptr += OFFSETS_PER_GATE; + vm.insert_value((instance_ptr + 6)?, n_instances - i)?; + } + Ok(()) +} + +/// Computes the circuit. +/// +/// If theres a failure, it returs the index of the gate in which the failure occurred, else +/// returns the total amount of mul gates. +pub fn eval_circuit( + vm: &mut VirtualMachine, + n_add_mods: usize, + add_mod_builtin_address: Relocatable, + n_mul_mods: usize, + mul_mod_builtin_address: Relocatable, +) -> Result<(), HintError> { + let modulus_ptr = mul_mod_builtin_address; + let mul_mod_values_offset = 4; + let mul_mod_offset = 5; + + let values_ptr = vm.get_relocatable((mul_mod_builtin_address + mul_mod_values_offset)?)?; + let mul_mod_offsets = vm.get_relocatable((mul_mod_builtin_address + mul_mod_offset)?)?; + let add_mod_offsets = if n_add_mods == 0 { + mul_mod_offsets + } else { + vm.get_relocatable((add_mod_builtin_address + mul_mod_offset)?)? + }; + + let n_computed_gates = compute_gates( + vm, + values_ptr, + add_mod_offsets, + n_add_mods, + mul_mod_offsets, + n_mul_mods, + modulus_ptr, + )?; + + let modulus: [Felt; 4] = [ + *vm.get_integer(modulus_ptr)?.deref(), + *vm.get_integer((modulus_ptr + 1)?)?.deref(), + *vm.get_integer((modulus_ptr + 2)?)?.deref(), + *vm.get_integer((modulus_ptr + 3)?)?.deref(), + ]; + + fill_instances( + vm, + add_mod_builtin_address, + n_add_mods, + modulus, + values_ptr, + add_mod_offsets, + )?; + fill_instances( + vm, + mul_mod_builtin_address, + n_computed_gates, + modulus, + values_ptr, + mul_mod_offsets, + )?; + + Ok(()) +} 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 70648f1060..c022286cbc 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 @@ -1,3 +1,6 @@ +// Most of the structs and implementations of these Dictionaries are based on the `cairo-lang-runner` crate. +// Reference: https://github.com/starkware-libs/cairo/blob/main/crates/cairo-lang-runner/src/casm_run/dict_manager.rs + use num_traits::One; use crate::stdlib::collections::HashMap; @@ -140,7 +143,7 @@ impl DictManagerExecScope { .into_boxed_str(), )); } - vm.add_relocation_rule(tracker.start, prev_end)?; + vm.add_relocation_rule(tracker.start, prev_end.into())?; prev_end += (tracker.end.unwrap_or_default() - tracker.start)?; prev_end += 1; } diff --git a/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs b/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs index 98bb0a1547..9b6e4b8651 100644 --- a/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs +++ b/vm/src/hint_processor/cairo_1_hint_processor/hint_processor.rs @@ -1,3 +1,4 @@ +use super::circuit; use super::dict_manager::DictManagerExecScope; use super::hint_processor_utils::*; use crate::any_box; @@ -10,7 +11,7 @@ use crate::vm::runners::cairo_runner::RunResources; use crate::Felt252; use crate::{ hint_processor::hint_processor_definition::HintProcessorLogic, - types::exec_scope::ExecutionScopes, + types::{errors::math_errors::MathError, exec_scope::ExecutionScopes}, vm::errors::vm_errors::VirtualMachineError, vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; @@ -71,7 +72,9 @@ impl Cairo1HintProcessor { segment_arena_validations, } } - // Runs a single Hint + // Most of the Hints implementations are derived from the `cairo-lang-runner` crate. + // https://github.com/starkware-libs/cairo/blob/40a7b60687682238f7f71ef7c59c986cc5733915/crates/cairo-lang-runner/src/casm_run/mod.rs#L1681 + /// Runs a single Hint pub fn execute( &self, vm: &mut VirtualMachine, @@ -85,9 +88,12 @@ impl Cairo1HintProcessor { Hint::Core(CoreHintBase::Core(CoreHint::TestLessThan { lhs, rhs, dst })) => { self.test_less_than(vm, lhs, rhs, dst) } - Hint::Core(CoreHintBase::Core(CoreHint::TestLessThanOrEqual { lhs, rhs, dst })) => { - self.test_less_than_or_equal(vm, lhs, rhs, dst) - } + Hint::Core(CoreHintBase::Core(CoreHint::TestLessThanOrEqual { lhs, rhs, dst })) + | Hint::Core(CoreHintBase::Core(CoreHint::TestLessThanOrEqualAddress { + lhs, + rhs, + dst, + })) => self.test_less_than_or_equal(vm, lhs, rhs, dst), Hint::Core(CoreHintBase::Deprecated(DeprecatedHint::Felt252DictRead { dict_ptr, key, @@ -274,6 +280,12 @@ impl Cairo1HintProcessor { t_or_k0, t_or_k1, ), + Hint::Core(CoreHintBase::Core(CoreHint::EvalCircuit { + n_add_mods, + add_mod_builtin, + n_mul_mods, + mul_mod_builtin, + })) => self.eval_circuit(vm, n_add_mods, add_mod_builtin, n_mul_mods, mul_mod_builtin), Hint::Starknet(StarknetHint::Cheatcode { selector, .. }) => { let selector = &selector.value.to_bytes_be().1; let selector = crate::stdlib::str::from_utf8(selector).map_err(|_| { @@ -1192,6 +1204,45 @@ impl Cairo1HintProcessor { } Ok(()) } + fn eval_circuit( + &self, + vm: &mut VirtualMachine, + n_add_mods: &ResOperand, + add_mod_builtin_ptr: &ResOperand, + n_mul_mods: &ResOperand, + mul_mod_builtin_ptr: &ResOperand, + ) -> Result<(), HintError> { + let n_add_mods = get_val(vm, n_add_mods)?; + let n_add_mods = + n_add_mods + .to_usize() + .ok_or(HintError::Math(MathError::Felt252ToUsizeConversion( + Box::from(n_add_mods), + )))?; + let n_mul_mods = get_val(vm, n_mul_mods)?; + let n_mul_mods = + n_mul_mods + .to_usize() + .ok_or(HintError::Math(MathError::Felt252ToUsizeConversion( + Box::from(n_mul_mods), + )))?; + + let (add_mod_builtin_base, add_mod_builtin_offset) = extract_buffer(add_mod_builtin_ptr)?; + let (mul_mod_builtin_base, mul_mod_builtin_offset) = extract_buffer(mul_mod_builtin_ptr)?; + + let add_mod_builtin_address = get_ptr(vm, add_mod_builtin_base, &add_mod_builtin_offset)?; + let mul_mod_builtin_address = get_ptr(vm, mul_mod_builtin_base, &mul_mod_builtin_offset)?; + + circuit::eval_circuit( + vm, + n_add_mods, + add_mod_builtin_address, + n_mul_mods, + mul_mod_builtin_address, + )?; + + Ok(()) + } } impl HintProcessorLogic for Cairo1HintProcessor { diff --git a/vm/src/hint_processor/cairo_1_hint_processor/mod.rs b/vm/src/hint_processor/cairo_1_hint_processor/mod.rs index 29d5f47bd3..d6d964081e 100644 --- a/vm/src/hint_processor/cairo_1_hint_processor/mod.rs +++ b/vm/src/hint_processor/cairo_1_hint_processor/mod.rs @@ -1,3 +1,4 @@ +pub mod circuit; pub mod dict_manager; pub mod hint_processor; pub mod hint_processor_utils; diff --git a/vm/src/hint_processor/hint_processor_definition.rs b/vm/src/hint_processor/hint_processor_definition.rs index 496e8b1428..d4c871903a 100644 --- a/vm/src/hint_processor/hint_processor_definition.rs +++ b/vm/src/hint_processor/hint_processor_definition.rs @@ -112,7 +112,7 @@ pub struct HintReference { impl HintReference { pub fn new_simple(offset1: i32) -> Self { HintReference { - offset1: OffsetValue::Reference(Register::FP, offset1, false), + offset1: OffsetValue::Reference(Register::FP, offset1, false, true), offset2: OffsetValue::Value(0), ap_tracking_data: None, outer_dereference: true, @@ -121,9 +121,15 @@ impl HintReference { } } - pub fn new(offset1: i32, offset2: i32, inner_dereference: bool, dereference: bool) -> Self { + pub fn new( + offset1: i32, + offset2: i32, + inner_dereference: bool, + dereference: bool, + is_positive: bool, + ) -> Self { HintReference { - offset1: OffsetValue::Reference(Register::FP, offset1, inner_dereference), + offset1: OffsetValue::Reference(Register::FP, offset1, inner_dereference, is_positive), offset2: OffsetValue::Value(offset2), ap_tracking_data: None, outer_dereference: dereference, @@ -145,8 +151,8 @@ impl From for HintReference { &reference.value_address.offset1, &reference.value_address.offset2, ) { - (OffsetValue::Reference(Register::AP, _, _), _) - | (_, OffsetValue::Reference(Register::AP, _, _)) => { + (OffsetValue::Reference(Register::AP, _, _, _), _) + | (_, OffsetValue::Reference(Register::AP, _, _, _)) => { Some(reference.ap_tracking_data.clone()) } _ => None, diff --git a/vm/src/hint_processor/hint_processor_utils.rs b/vm/src/hint_processor/hint_processor_utils.rs index 8a0a3e9e58..3d442fbf7f 100644 --- a/vm/src/hint_processor/hint_processor_utils.rs +++ b/vm/src/hint_processor/hint_processor_utils.rs @@ -70,7 +70,12 @@ pub fn get_maybe_relocatable_from_reference( &hint_reference.ap_tracking_data, ap_tracking, )?; - let mut val = offset1.add(&offset2).ok()?; + let mut val = match hint_reference.offset2 { + OffsetValue::Reference(_, _, _, true) + | OffsetValue::Immediate(_) + | OffsetValue::Value(_) => offset1.add(&offset2).ok()?, + OffsetValue::Reference(_, _, _, false) => offset1.sub(&offset2).ok()?, + }; if hint_reference.inner_dereference && hint_reference.outer_dereference { val = vm.get_maybe(&val)?; } @@ -139,7 +144,7 @@ fn get_offset_value( match offset_value { OffsetValue::Immediate(f) => Some(f.into()), OffsetValue::Value(v) => Some(Felt252::from(*v).into()), - OffsetValue::Reference(register, offset, deref) => { + OffsetValue::Reference(register, offset, deref, _) => { let addr = (if matches!(register, Register::FP) { vm.get_fp() } else { @@ -176,7 +181,7 @@ mod tests { // Reference: cast(2, felt) let mut vm = vm!(); vm.segments = segments![((1, 0), 0)]; - let mut hint_ref = HintReference::new(0, 0, false, false); + let mut hint_ref = HintReference::new(0, 0, false, false, true); hint_ref.offset1 = OffsetValue::Immediate(Felt252::from(2)); assert_eq!( @@ -191,8 +196,8 @@ mod tests { fn get_offset_value_reference_valid() { let mut vm = vm!(); vm.segments = segments![((1, 0), 0)]; - let mut hint_ref = HintReference::new(0, 0, false, true); - hint_ref.offset1 = OffsetValue::Reference(Register::FP, 2_i32, false); + let mut hint_ref = HintReference::new(0, 0, false, true, true); + hint_ref.offset1 = OffsetValue::Reference(Register::FP, 2_i32, false, true); assert_matches!( get_offset_value(&vm, &hint_ref.offset1, &hint_ref.ap_tracking_data, &ApTracking::new()), @@ -205,8 +210,8 @@ mod tests { fn get_offset_value_invalid() { let mut vm = vm!(); vm.segments = segments![((1, 0), 0)]; - let mut hint_ref = HintReference::new(0, 0, false, true); - hint_ref.offset1 = OffsetValue::Reference(Register::FP, -2_i32, false); + let mut hint_ref = HintReference::new(0, 0, false, true, true); + hint_ref.offset1 = OffsetValue::Reference(Register::FP, -2_i32, false, true); assert_matches!( get_offset_value( @@ -228,7 +233,7 @@ mod tests { assert_matches!( get_ptr_from_reference( &vm, - &HintReference::new(0, 0, false, false), + &HintReference::new(0, 0, false, false, true), &ApTracking::new() ), Ok(x) if x == relocatable!(1, 0) @@ -244,7 +249,7 @@ mod tests { assert_matches!( get_ptr_from_reference( &vm, - &HintReference::new(0, 0, false, true), + &HintReference::new(0, 0, false, true, true), &ApTracking::new() ), Ok(x) if x == relocatable!(3, 0) @@ -256,7 +261,7 @@ mod tests { fn get_ptr_from_reference_with_dereference_and_imm() { let mut vm = vm!(); vm.segments = segments![((1, 0), (4, 0))]; - let mut hint_ref = HintReference::new(0, 0, true, false); + let mut hint_ref = HintReference::new(0, 0, true, false, true); hint_ref.offset2 = OffsetValue::Value(2); assert_matches!( @@ -270,7 +275,7 @@ mod tests { fn compute_addr_from_reference_no_regiter_in_reference() { let mut vm = vm!(); vm.segments = segments![((1, 0), (4, 0))]; - let mut hint_reference = HintReference::new(0, 0, false, false); + let mut hint_reference = HintReference::new(0, 0, false, false, true); hint_reference.offset1 = OffsetValue::Immediate(Felt252::from(2_i32)); assert!(compute_addr_from_reference(&hint_reference, &vm, &ApTracking::new()).is_none()); @@ -282,8 +287,8 @@ mod tests { let mut vm = vm!(); vm.segments = segments![((1, 0), 4)]; // vm.run_context.fp = -1; - let mut hint_reference = HintReference::new(0, 0, false, false); - hint_reference.offset1 = OffsetValue::Reference(Register::FP, -1, true); + let mut hint_reference = HintReference::new(0, 0, false, false, true); + hint_reference.offset1 = OffsetValue::Reference(Register::FP, -1, true, true); assert_matches!( compute_addr_from_reference(&hint_reference, &vm, &ApTracking::new()), @@ -356,7 +361,7 @@ mod tests { ((0, 5), 3) // [[[fp + 2] + 2]] -> [(0, 5)] -> 3 ]; let hint_ref = HintReference { - offset1: OffsetValue::Reference(Register::FP, 2, true), + offset1: OffsetValue::Reference(Register::FP, 2, true, true), offset2: OffsetValue::Value(2), outer_dereference: true, inner_dereference: true, @@ -381,7 +386,7 @@ mod tests { ]; // [fp + 4] + (-5) = 8 - 5 = 3 let hint_ref = HintReference { - offset1: OffsetValue::Reference(Register::FP, 4, true), + offset1: OffsetValue::Reference(Register::FP, 4, true, true), offset2: OffsetValue::Immediate(Felt252::from(-5)), outer_dereference: false, inner_dereference: false, diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 5d78058f13..22798e3f97 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -8,6 +8,8 @@ //! - the `skip_next_instruction()` hints; //! - implementations of [`arbitrary::Arbitrary`](https://docs.rs/arbitrary/latest/arbitrary/) for some structs. //! - `cairo-1-hints`: Enable hints that were introduced in Cairo 1. Not enabled by default. +//! - `cairo-0-secp-hints`: Enable secp hints that were introduced in Cairo 0. Not enabled by default. +//! - `cairo-0-data-availability-hints`: Enable data availability hints that were introduced in Cairo 0. Not enabled by default. #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(warnings)] diff --git a/vm/src/math_utils/is_prime.rs b/vm/src/math_utils/is_prime.rs index 0e02ef1705..a39a9978a1 100644 --- a/vm/src/math_utils/is_prime.rs +++ b/vm/src/math_utils/is_prime.rs @@ -9,7 +9,7 @@ mod with_std { use num_bigint::BigUint; pub fn is_prime(n: &BigUint) -> bool { - num_prime::nt_funcs::is_prime(n, None).probably() + num_prime::nt_funcs::is_prime::(n, None).probably() } } diff --git a/vm/src/program_hash.rs b/vm/src/program_hash.rs index 1b0851193d..2f31fe7d9f 100644 --- a/vm/src/program_hash.rs +++ b/vm/src/program_hash.rs @@ -1,4 +1,4 @@ -use starknet_crypto::{pedersen_hash, FieldElement}; +use starknet_crypto::pedersen_hash; use crate::Felt252; @@ -7,7 +7,7 @@ use crate::types::builtin_name::BuiltinName; use crate::types::relocatable::MaybeRelocatable; use crate::vm::runners::cairo_pie::StrippedProgram; -type HashFunction = fn(&FieldElement, &FieldElement) -> FieldElement; +type HashFunction = fn(&Felt252, &Felt252) -> Felt252; #[derive(thiserror_no_std::Error, Debug)] pub enum HashChainError { @@ -27,23 +27,15 @@ pub enum ProgramHashError { #[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])))). /// [cairo_lang reference](https://github.com/starkware-libs/cairo-lang/blob/efa9648f57568aad8f8a13fbf027d2de7c63c2c0/src/starkware/cairo/common/hash_chain.py#L6) -fn compute_hash_chain<'a, I>( - data: I, - hash_func: HashFunction, -) -> Result +fn compute_hash_chain<'a, I>(data: I, hash_func: HashFunction) -> Result where - I: Iterator + DoubleEndedIterator, + I: Iterator + DoubleEndedIterator, { match data.copied().rev().reduce(|x, y| hash_func(&y, &x)) { Some(result) => Ok(result), @@ -51,37 +43,27 @@ where } } -/// Creates an instance of `FieldElement` from a builtin name. +/// Creates an instance of `Felt252` 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_name_to_field_element( - builtin_name: &BuiltinName, -) -> Result { +fn builtin_name_to_field_element(builtin_name: &BuiltinName) -> Result { // The Python implementation uses the builtin name without suffix - FieldElement::from_byte_slice_be(builtin_name.to_str().as_bytes()) - .map_err(|_| ProgramHashError::InvalidProgramBuiltin(builtin_name.to_str())) -} - -/// 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) + Ok(Felt252::from_bytes_be_slice( + builtin_name.to_str().as_bytes(), + )) } -/// Converts a `MaybeRelocatable` into a `FieldElement` value. +/// Converts a `MaybeRelocatable` into a `Felt252` value. /// /// Returns `InvalidProgramData` if `maybe_relocatable` is not an integer fn maybe_relocatable_to_field_element( maybe_relocatable: &MaybeRelocatable, -) -> Result { - let felt = maybe_relocatable +) -> Result { + maybe_relocatable .get_int_ref() - .ok_or(ProgramHashError::InvalidProgramData)?; - felt_to_field_element(felt) + .copied() + .ok_or(ProgramHashError::InvalidProgramData) } /// Computes the Pedersen hash of a program. @@ -89,12 +71,12 @@ fn maybe_relocatable_to_field_element( pub fn compute_program_hash_chain( program: &StrippedProgram, bootloader_version: usize, -) -> Result { +) -> Result { let program_main = program.main; - let program_main = FieldElement::from(program_main); + let program_main = Felt252::from(program_main); // Convert builtin names to field elements - let builtin_list: Result, _> = program + let builtin_list: Result, _> = program .builtins .iter() .map(builtin_name_to_field_element) @@ -102,9 +84,9 @@ pub fn compute_program_hash_chain( let builtin_list = builtin_list?; let program_header = vec![ - FieldElement::from(bootloader_version), + Felt252::from(bootloader_version), program_main, - FieldElement::from(program.builtins.len()), + Felt252::from(program.builtins.len()), ]; let program_data: Result, _> = program @@ -115,7 +97,7 @@ pub fn compute_program_hash_chain( 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)]; + let data_chain_len_vec = vec![Felt252::from(data_chain_len)]; // Prepare a chain of iterators to feed to the hash function let data_chain = [ @@ -140,14 +122,14 @@ mod tests { #[test] fn test_compute_hash_chain() { - let data: Vec = vec![ - FieldElement::from(1u64), - FieldElement::from(2u64), - FieldElement::from(3u64), + let data: Vec = vec![ + Felt252::from(1u64), + Felt252::from(2u64), + Felt252::from(3u64), ]; let expected_hash = pedersen_hash( - &FieldElement::from(1u64), - &pedersen_hash(&FieldElement::from(2u64), &FieldElement::from(3u64)), + &Felt252::from(1u64), + &pedersen_hash(&Felt252::from(2u64), &Felt252::from(3u64)), ); let computed_hash = compute_hash_chain(data.iter(), pedersen_hash) .expect("Hash computation failed unexpectedly"); diff --git a/vm/src/serde/deserialize_program.rs b/vm/src/serde/deserialize_program.rs index 3fbd848932..8f0e9a0dee 100644 --- a/vm/src/serde/deserialize_program.rs +++ b/vm/src/serde/deserialize_program.rs @@ -102,6 +102,7 @@ pub struct Identifier { pub full_name: Option, pub members: Option>, pub cairo_type: Option, + pub size: Option, } #[cfg_attr(feature = "test_utils", derive(Arbitrary))] @@ -274,7 +275,7 @@ pub struct Reference { pub enum OffsetValue { Immediate(Felt252), Value(i32), - Reference(Register, i32, bool), + Reference(Register, i32, bool, bool), } #[cfg_attr(feature = "test_utils", derive(Arbitrary))] @@ -566,7 +567,7 @@ mod tests { "attributes": [], "debug_info": { "instruction_locations": {} - }, + }, "builtins": [], "data": [ "0x480680017fff8000", @@ -713,7 +714,7 @@ mod tests { }, pc: Some(0), value_address: ValueAddress { - offset1: OffsetValue::Reference(Register::FP, -4, false), + offset1: OffsetValue::Reference(Register::FP, -4, false, true), offset2: OffsetValue::Value(0), outer_dereference: true, inner_dereference: false, @@ -727,7 +728,7 @@ mod tests { }, pc: Some(0), value_address: ValueAddress { - offset1: OffsetValue::Reference(Register::FP, -3, false), + offset1: OffsetValue::Reference(Register::FP, -3, false, true), offset2: OffsetValue::Value(0), outer_dereference: true, inner_dereference: false, @@ -741,7 +742,7 @@ mod tests { }, pc: Some(0), value_address: ValueAddress { - offset1: OffsetValue::Reference(Register::FP, -3, true), + offset1: OffsetValue::Reference(Register::FP, -3, true, true), offset2: OffsetValue::Immediate(Felt252::from(2)), outer_dereference: false, inner_dereference: false, @@ -755,7 +756,7 @@ mod tests { }, pc: Some(0), value_address: ValueAddress { - offset1: OffsetValue::Reference(Register::FP, 0, false), + offset1: OffsetValue::Reference(Register::FP, 0, false, true), offset2: OffsetValue::Value(0), outer_dereference: true, inner_dereference: false, @@ -1012,6 +1013,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); identifiers.insert( @@ -1025,6 +1027,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); identifiers.insert( @@ -1036,6 +1039,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); identifiers.insert( @@ -1049,6 +1053,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); identifiers.insert( @@ -1060,6 +1065,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); identifiers.insert( @@ -1071,6 +1077,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); identifiers.insert( @@ -1082,6 +1089,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -1097,7 +1105,7 @@ mod tests { "attributes": [], "debug_info": { "instruction_locations": {} - }, + }, "builtins": [], "data": [ ], @@ -1178,10 +1186,10 @@ mod tests { "start_pc": 402, "value": "SafeUint256: subtraction overflow" } - ], + ], "debug_info": { "instruction_locations": {} - }, + }, "builtins": [], "data": [ ], @@ -1235,7 +1243,7 @@ mod tests { let valid_json = r#" { "prime": "0x800000000000011000000000000000000000000000000000000000000000001", - "attributes": [], + "attributes": [], "debug_info": { "file_contents": {}, "instruction_locations": { @@ -1286,7 +1294,7 @@ mod tests { } } } - }, + }, "builtins": [], "data": [ ], @@ -1344,7 +1352,7 @@ mod tests { let valid_json = r#" { "prime": "0x800000000000011000000000000000000000000000000000000000000000001", - "attributes": [], + "attributes": [], "debug_info": { "file_contents": {}, "instruction_locations": { @@ -1391,7 +1399,7 @@ mod tests { } } } - }, + }, "builtins": [], "data": [ ], @@ -1484,8 +1492,7 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_felt_from_number_with_scientific_notation() { - let n = Number::deserialize(serde_json::Value::from(1000000000000000000000000000_u128)) - .unwrap(); + let n = Number::deserialize(serde_json::Value::from(1e27)).unwrap(); assert_eq!(n.to_string(), "1e27".to_owned()); assert_matches!( diff --git a/vm/src/serde/deserialize_utils.rs b/vm/src/serde/deserialize_utils.rs index 8179605c52..7b4c7bb20e 100644 --- a/vm/src/serde/deserialize_utils.rs +++ b/vm/src/serde/deserialize_utils.rs @@ -125,7 +125,17 @@ fn inner_dereference(input: &str) -> IResult<&str, OffsetValue> { if input.is_empty() { return Ok(("", OffsetValue::Value(0))); } - let (input, _sign) = opt(alt((tag(" + "), tag(" - "))))(input)?; + let (input, sign) = opt(alt((tag(" + "), tag(" - "))))(input)?; + + let is_positive = match sign { + Some(s) => match s.trim() { + "+" => true, + "-" => false, + // can't happen since the alt parser only takes "+" or "-" + _ => unreachable!(), + }, + None => true, + }; map_res( delimited(tag("["), take_until("]"), tag("]")), @@ -135,7 +145,7 @@ fn inner_dereference(input: &str) -> IResult<&str, OffsetValue> { let (_, (register, offset)) = res; let offset_value = match register { None => OffsetValue::Value(offset), - Some(reg) => OffsetValue::Reference(reg, offset, true), + Some(reg) => OffsetValue::Reference(reg, offset, true, is_positive), }; (rem_input, offset_value) }) @@ -145,7 +155,7 @@ fn no_inner_dereference(input: &str) -> IResult<&str, OffsetValue> { let (rem_input, (register, offset)) = register_and_offset(input)?; let offset_value = match register { None => OffsetValue::Value(offset), - Some(reg) => OffsetValue::Reference(reg, offset, false), + Some(reg) => OffsetValue::Reference(reg, offset, false, true), }; Ok((rem_input, offset_value)) } @@ -177,13 +187,17 @@ pub(crate) fn parse_value(input: &str) -> IResult<&str, ValueAddress> { let offset1 = match fst_offset { OffsetValue::Immediate(imm) => OffsetValue::Immediate(imm), OffsetValue::Value(val) => OffsetValue::Immediate(Felt252::from(val)), - OffsetValue::Reference(reg, val, refe) => OffsetValue::Reference(reg, val, refe), + OffsetValue::Reference(reg, val, refe, is_positive) => { + OffsetValue::Reference(reg, val, refe, is_positive) + } }; let offset2 = match snd_offset { OffsetValue::Immediate(imm) => OffsetValue::Immediate(imm), OffsetValue::Value(val) => OffsetValue::Immediate(Felt252::from(val)), - OffsetValue::Reference(reg, val, refe) => OffsetValue::Reference(reg, val, refe), + OffsetValue::Reference(reg, val, refe, is_positive) => { + OffsetValue::Reference(reg, val, refe, is_positive) + } }; (offset1, offset2) @@ -368,7 +382,10 @@ mod tests { assert_eq!( parsed, - Ok((" + 2", OffsetValue::Reference(Register::FP, -1_i32, true))) + Ok(( + " + 2", + OffsetValue::Reference(Register::FP, -1_i32, true, true) + )) ); } @@ -380,7 +397,7 @@ mod tests { assert_eq!( parsed, - Ok(("", OffsetValue::Reference(Register::AP, 3_i32, false))) + Ok(("", OffsetValue::Reference(Register::AP, 3_i32, false, true))) ); } @@ -396,7 +413,7 @@ mod tests { "", ValueAddress { offset2: OffsetValue::Value(2), - offset1: OffsetValue::Reference(Register::FP, -1_i32, true), + offset1: OffsetValue::Reference(Register::FP, -1_i32, true, true), outer_dereference: true, inner_dereference: false, value_type: "felt".to_string(), @@ -405,6 +422,27 @@ mod tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn parse_value_with_neg_inner_dereference_test() { + let value = "cast(17 - [fp], felt)"; + let parsed = parse_value(value).unwrap(); + + assert_eq!( + parsed, + ( + "", + ValueAddress { + offset1: OffsetValue::Immediate(Felt252::from(17)), + offset2: OffsetValue::Reference(Register::FP, 0_i32, true, false), + outer_dereference: false, + inner_dereference: false, + value_type: "felt".to_string() + } + ) + ) + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn parse_value_with_no_inner_dereference_test() { @@ -416,7 +454,7 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::AP, 2_i32, false), + offset1: OffsetValue::Reference(Register::AP, 2_i32, false, true), offset2: OffsetValue::Value(0), outer_dereference: false, inner_dereference: false, @@ -457,7 +495,7 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::AP, 0_i32, false), + offset1: OffsetValue::Reference(Register::AP, 0_i32, false, true), offset2: OffsetValue::Value(-1), outer_dereference: true, inner_dereference: false, @@ -478,7 +516,7 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::AP, 0_i32, true), + offset1: OffsetValue::Reference(Register::AP, 0_i32, true, true), offset2: OffsetValue::Value(1), outer_dereference: true, inner_dereference: false, @@ -499,7 +537,7 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::AP, 0_i32, true), + offset1: OffsetValue::Reference(Register::AP, 0_i32, true, true), offset2: OffsetValue::Immediate(Felt252::ONE), outer_dereference: true, inner_dereference: false, @@ -520,7 +558,7 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::AP, 1_i32, true), + offset1: OffsetValue::Reference(Register::AP, 1_i32, true, true), offset2: OffsetValue::Value(1), outer_dereference: true, inner_dereference: false, @@ -541,8 +579,8 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::AP, 0_i32, true), - offset2: OffsetValue::Reference(Register::FP, 1_i32, true), + offset1: OffsetValue::Reference(Register::AP, 0_i32, true, true), + offset2: OffsetValue::Reference(Register::FP, 1_i32, true, true), outer_dereference: true, inner_dereference: false, value_type: "__main__.felt".to_string(), @@ -562,8 +600,8 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::AP, 1_i32, true), - offset2: OffsetValue::Reference(Register::FP, 1_i32, true), + offset1: OffsetValue::Reference(Register::AP, 1_i32, true, true), + offset2: OffsetValue::Reference(Register::FP, 1_i32, true, true), outer_dereference: true, inner_dereference: false, value_type: "__main__.felt".to_string(), @@ -604,7 +642,7 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::AP, 0_i32, true), + offset1: OffsetValue::Reference(Register::AP, 0_i32, true, true), offset2: OffsetValue::Value(1), outer_dereference: true, inner_dereference: false, @@ -625,7 +663,7 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::AP, 0_i32, true), + offset1: OffsetValue::Reference(Register::AP, 0_i32, true, true), offset2: OffsetValue::Value(1), outer_dereference: true, inner_dereference: false, @@ -646,8 +684,8 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::AP, 0_i32, true), - offset2: OffsetValue::Reference(Register::AP, 0_i32, true), + offset1: OffsetValue::Reference(Register::AP, 0_i32, true, true), + offset2: OffsetValue::Reference(Register::AP, 0_i32, true, true), outer_dereference: true, inner_dereference: false, value_type: "felt".to_string(), @@ -667,7 +705,7 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::FP, -3_i32, true), + offset1: OffsetValue::Reference(Register::FP, -3_i32, true, true), offset2: OffsetValue::Value(5), outer_dereference: true, inner_dereference: true, @@ -688,8 +726,8 @@ mod tests { Ok(( "", ValueAddress { - offset1: OffsetValue::Reference(Register::AP, 1_i32, true), - offset2: OffsetValue::Reference(Register::AP, 2_i32, true), + offset1: OffsetValue::Reference(Register::AP, 1_i32, true, true), + offset2: OffsetValue::Reference(Register::AP, 2_i32, true, true), outer_dereference: true, inner_dereference: false, value_type: "felt".to_string(), diff --git a/vm/src/serde/serialize_program.rs b/vm/src/serde/serialize_program.rs index f8bd0d076a..1e9f83e8d1 100644 --- a/vm/src/serde/serialize_program.rs +++ b/vm/src/serde/serialize_program.rs @@ -127,6 +127,7 @@ pub(crate) struct IdentifierSerializer { pub full_name: Option, pub members: Option>, pub cairo_type: Option, + pub size: Option, } impl From for Identifier { @@ -138,6 +139,7 @@ impl From for Identifier { full_name: identifier_serialer.full_name, members: identifier_serialer.members, cairo_type: identifier_serialer.cairo_type, + size: identifier_serialer.size, } } } @@ -151,6 +153,7 @@ impl From for IdentifierSerializer { full_name: identifier_serialer.full_name, members: identifier_serialer.members, cairo_type: identifier_serialer.cairo_type, + size: identifier_serialer.size, } } } diff --git a/vm/src/tests/cairo_1_run_from_entrypoint_tests.rs b/vm/src/tests/cairo_1_run_from_entrypoint_tests.rs index 4ed670c679..aa1807d26c 100644 --- a/vm/src/tests/cairo_1_run_from_entrypoint_tests.rs +++ b/vm/src/tests/cairo_1_run_from_entrypoint_tests.rs @@ -39,7 +39,7 @@ fn test_uint256_div_mod_hint() { run_cairo_1_entrypoint( program_data.as_slice(), - 208, + 219, &[36_usize.into(), 2_usize.into()], &[Felt252::from(18_usize)], ); diff --git a/vm/src/tests/cairo_layout_params_file.json b/vm/src/tests/cairo_layout_params_file.json new file mode 100644 index 0000000000..f4d0d736f5 --- /dev/null +++ b/vm/src/tests/cairo_layout_params_file.json @@ -0,0 +1,29 @@ +{ + "rc_units": 4, + "log_diluted_units_per_step": 4, + "cpu_component_step": 8, + "memory_units_per_step": 8, + "uses_pedersen_builtin": true, + "pedersen_ratio": 256, + "uses_range_check_builtin": true, + "range_check_ratio": 8, + "uses_ecdsa_builtin": true, + "ecdsa_ratio": 2048, + "uses_bitwise_builtin": true, + "bitwise_ratio": 16, + "uses_ec_op_builtin": true, + "ec_op_ratio": 1024, + "uses_keccak_builtin": true, + "keccak_ratio": 2048, + "uses_poseidon_builtin": true, + "poseidon_ratio": 256, + "uses_range_check96_builtin": true, + "range_check96_ratio": 8, + "range_check96_ratio_den": 1, + "uses_add_mod_builtin": true, + "add_mod_ratio": 128, + "add_mod_ratio_den": 1, + "uses_mul_mod_builtin": true, + "mul_mod_ratio": 256, + "mul_mod_ratio_den": 1 +} diff --git a/vm/src/tests/cairo_run_test.rs b/vm/src/tests/cairo_run_test.rs index ce42ca31dd..d847852929 100644 --- a/vm/src/tests/cairo_run_test.rs +++ b/vm/src/tests/cairo_run_test.rs @@ -1182,6 +1182,7 @@ fn run_program_with_custom_mod_builtin_params( let mut cairo_runner = CairoRunner::new( &program, cairo_run_config.layout, + cairo_run_config.dynamic_layout_params, cairo_run_config.proof_mode, cairo_run_config.trace_enabled, ) @@ -1239,3 +1240,80 @@ fn cairo_run_apply_poly_proof() { include_bytes!("../../../cairo_programs/mod_builtin_feature/proof/apply_poly.json"); run_program(program_data, true, None, None, None); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_reduce_value() { + let program_data = include_bytes!( + "../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_value.json" + ); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_ec() { + let program_data = + include_bytes!("../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec.json"); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_reduce_x() { + let program_data = include_bytes!( + "../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_reduce_x.json" + ); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_get_point_from_x() { + let program_data = include_bytes!( + "../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_get_point_from_x.json" + ); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_compute_q_mod_prime() { + let program_data = include_bytes!( + "../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_compute_q_mod_prime.json" + ); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_ec_double_assign_new_x() { + let program_data = + include_bytes!("../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_double_assign_new_x.json"); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-secp-hints")] +fn cairo_run_secp_cairo0_ec_mul_by_uint256() { + let program_data = include_bytes!( + "../../../cairo_programs/cairo-0-secp-hints-feature/secp_cairo0_ec_mul_by_uint256.json" + ); + run_program_simple(program_data.as_slice()); +} + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +#[cfg(feature = "cairo-0-data-availability-hints")] +fn cairo_run_data_availability_reduced_mul() { + let program_data = + include_bytes!("../../../cairo_programs/cairo-0-kzg-da-hints/reduced_mul.json"); + run_program_simple(program_data.as_slice()); +} diff --git a/vm/src/tests/compare_outputs_dynamic_layouts.sh b/vm/src/tests/compare_outputs_dynamic_layouts.sh new file mode 100755 index 0000000000..41c7bf0f17 --- /dev/null +++ b/vm/src/tests/compare_outputs_dynamic_layouts.sh @@ -0,0 +1,300 @@ +#!/usr/bin/env bash +# +# Compares programs with different dynamic layouts against cairo-lang + + +# Build temporary dynamic layout params files +TEMP_FOLDER=$(mktemp -d) +cat < "$TEMP_FOLDER/all_cairo.json" +{ + "rc_units": 4, + "log_diluted_units_per_step": 4, + "cpu_component_step": 8, + "memory_units_per_step": 8, + "uses_pedersen_builtin": true, + "pedersen_ratio": 256, + "uses_range_check_builtin": true, + "range_check_ratio": 8, + "uses_ecdsa_builtin": true, + "ecdsa_ratio": 2048, + "uses_bitwise_builtin": true, + "bitwise_ratio": 16, + "uses_ec_op_builtin": true, + "ec_op_ratio": 1024, + "uses_keccak_builtin": true, + "keccak_ratio": 2048, + "uses_poseidon_builtin": true, + "poseidon_ratio": 256, + "uses_range_check96_builtin": true, + "range_check96_ratio": 8, + "range_check96_ratio_den": 1, + "uses_add_mod_builtin": true, + "add_mod_ratio": 128, + "add_mod_ratio_den": 1, + "uses_mul_mod_builtin": true, + "mul_mod_ratio": 256, + "mul_mod_ratio_den": 1 +} +EOF +cat < "$TEMP_FOLDER/double_all_cairo.json" +{ + "rc_units": 8, + "log_diluted_units_per_step": 8, + "cpu_component_step": 16, + "memory_units_per_step": 16, + "uses_pedersen_builtin": true, + "pedersen_ratio": 512, + "uses_range_check_builtin": true, + "range_check_ratio": 16, + "uses_ecdsa_builtin": true, + "ecdsa_ratio": 4096, + "uses_bitwise_builtin": true, + "bitwise_ratio": 32, + "uses_ec_op_builtin": true, + "ec_op_ratio": 2048, + "uses_keccak_builtin": true, + "keccak_ratio": 4096, + "uses_poseidon_builtin": true, + "poseidon_ratio": 512, + "uses_range_check96_builtin": true, + "range_check96_ratio": 16, + "range_check96_ratio_den": 1, + "uses_add_mod_builtin": true, + "add_mod_ratio": 256, + "add_mod_ratio_den": 1, + "uses_mul_mod_builtin": true, + "mul_mod_ratio": 512, + "mul_mod_ratio_den": 1 +} +EOF + +cat < "$TEMP_FOLDER/fractional_units_per_step.json" +{ + "rc_units": 4, + "log_diluted_units_per_step": -2, + "cpu_component_step": 8, + "memory_units_per_step": 8, + "uses_pedersen_builtin": false, + "pedersen_ratio": 0, + "uses_range_check_builtin": false, + "range_check_ratio": 0, + "uses_ecdsa_builtin": false, + "ecdsa_ratio": 0, + "uses_bitwise_builtin": false, + "bitwise_ratio": 0, + "uses_ec_op_builtin": false, + "ec_op_ratio": 0, + "uses_keccak_builtin": false, + "keccak_ratio": 0, + "uses_poseidon_builtin": false, + "poseidon_ratio": 0, + "uses_range_check96_builtin": false, + "range_check96_ratio": 0, + "range_check96_ratio_den": 1, + "uses_add_mod_builtin": false, + "add_mod_ratio": 0, + "add_mod_ratio_den": 1, + "uses_mul_mod_builtin": false, + "mul_mod_ratio": 0, + "mul_mod_ratio_den": 1 +} +EOF + +cat < "$TEMP_FOLDER/ratio_den.json" +{ + "rc_units": 4, + "log_diluted_units_per_step": 4, + "cpu_component_step": 8, + "memory_units_per_step": 512, + "uses_pedersen_builtin": false, + "pedersen_ratio": 0, + "uses_range_check_builtin": false, + "range_check_ratio": 0, + "uses_ecdsa_builtin": false, + "ecdsa_ratio": 0, + "uses_bitwise_builtin": false, + "bitwise_ratio": 0, + "uses_ec_op_builtin": false, + "ec_op_ratio": 0, + "uses_keccak_builtin": false, + "keccak_ratio": 0, + "uses_poseidon_builtin": false, + "poseidon_ratio": 0, + "uses_range_check96_builtin": true, + "range_check96_ratio": 1, + "range_check96_ratio_den": 2, + "uses_add_mod_builtin": true, + "add_mod_ratio": 1, + "add_mod_ratio_den": 2, + "uses_mul_mod_builtin": true, + "mul_mod_ratio": 1, + "mul_mod_ratio_den": 2 +} +EOF + +# Build cases to execute +CASES=( + "cairo_programs/proof_programs/factorial.json;all_cairo" + "cairo_programs/proof_programs/factorial.json;double_all_cairo" + "cairo_programs/proof_programs/fibonacci.json;all_cairo" + "cairo_programs/proof_programs/fibonacci.json;double_all_cairo" + "cairo_programs/proof_programs/bigint.json;all_cairo" + "cairo_programs/proof_programs/bigint.json;double_all_cairo" + "cairo_programs/proof_programs/dict.json;all_cairo" + "cairo_programs/proof_programs/dict.json;double_all_cairo" + "cairo_programs/proof_programs/sha256.json;all_cairo" + "cairo_programs/proof_programs/sha256.json;double_all_cairo" + "cairo_programs/proof_programs/keccak.json;all_cairo" + "cairo_programs/proof_programs/keccak.json;double_all_cairo" + # Mod builtin feature + "cairo_programs/mod_builtin_feature/proof/mod_builtin.json;all_cairo" + "cairo_programs/mod_builtin_feature/proof/mod_builtin_failure.json;all_cairo" + "cairo_programs/mod_builtin_feature/proof/apply_poly.json;all_cairo" + # Fractional units per step + "cairo_programs/proof_programs/factorial.json;fractional_units_per_step" + "cairo_programs/proof_programs/fibonacci.json;fractional_units_per_step" + # Ratio den + "cairo_programs/mod_builtin_feature/proof/mod_builtin.json;ratio_den" + "cairo_programs/mod_builtin_feature/proof/mod_builtin_failure.json;ratio_den" + "cairo_programs/mod_builtin_feature/proof/apply_poly.json;ratio_den" +) + +# Build pie cases to execute +PIE_CASES=( + "cairo_programs/fibonacci.rs.pie.zip;all_cairo" + "cairo_programs/fibonacci.rs.pie.zip;double_all_cairo" + "cairo_programs/factorial.rs.pie.zip;all_cairo" + "cairo_programs/factorial.rs.pie.zip;double_all_cairo" + "cairo_programs/bigint.rs.pie.zip;all_cairo" + "cairo_programs/bigint.rs.pie.zip;double_all_cairo" + "cairo_programs/dict.rs.pie.zip;all_cairo" + "cairo_programs/dict.rs.pie.zip;double_all_cairo" + "cairo_programs/sha256.rs.pie.zip;all_cairo" + "cairo_programs/sha256.rs.pie.zip;double_all_cairo" + "cairo_programs/keccak.rs.pie.zip;all_cairo" + "cairo_programs/keccak.rs.pie.zip;double_all_cairo" +) + +passed_tests=0 +failed_tests=0 +exit_code=0 + +for case in "${CASES[@]}"; do + IFS=";" read -r program layout <<< "$case" + + full_program="$program" + full_layout="$TEMP_FOLDER/$layout.json" + + # Run cairo-vm + echo "Running cairo-vm with case: $case" + cargo run -p cairo-vm-cli --features mod_builtin --release -- "$full_program" \ + --layout "dynamic" --cairo_layout_params_file "$full_layout" --proof_mode \ + --trace_file program_rs.trace --memory_file program_rs.memory --air_public_input program_rs.air_public_input --air_private_input program_rs.air_private_input + + # Run cairo-lang + echo "Running cairo-lang with case: $case" + cairo-run --program "$full_program" \ + --layout "dynamic" --cairo_layout_params_file "$full_layout" --proof_mode \ + --trace_file program_py.trace --memory_file program_py.memory --air_public_input program_py.air_public_input --air_private_input program_py.air_private_input + + # Compare trace + echo "Running trace comparison for case: $case" + if ! diff -q program_rs.trace program_py.trace; then + echo "Trace differs for case: $case" + exit_code=1 + failed_tests=$((failed_tests + 1)) + else + passed_tests=$((passed_tests + 1)) + fi + + # Compare memory + echo "Running memory comparison for case: $case" + if ! ./vm/src/tests/memory_comparator.py program_rs.memory program_py.memory; then + echo "Memory differs for case: $case" + 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 case: $case" + if ! ./vm/src/tests/air_public_input_comparator.py program_rs.air_public_input program_py.air_public_input; then + echo "Air public input differs for case: $case" + 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 case: $case" + if ! ./vm/src/tests/air_private_input_comparator.py program_rs.air_private_input program_py.air_private_input; then + echo "Air private input differs for case: $case" + 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 program_rs.* + rm program_py.* +done + + +for case in "${PIE_CASES[@]}"; do + IFS=";" read -r program layout <<< "$case" + + full_program="$program" + full_layout="$TEMP_FOLDER/$layout.json" + + # Run cairo-vm + echo "Running cairo-vm with case: $case" + cargo run -p cairo-vm-cli --features mod_builtin --release -- "$full_program" \ + --layout "dynamic" --cairo_layout_params_file "$full_layout" --run_from_cairo_pie \ + --trace_file program_rs.trace --memory_file program_rs.memory + + # Run cairo-lang + echo "Running cairo-lang with case: $case" + cairo-run --run_from_cairo_pie "$full_program" \ + --layout "dynamic" --cairo_layout_params_file "$full_layout" \ + --trace_file program_py.trace --memory_file program_py.memory + + # Compare trace + echo "Running trace comparison for case: $case" + if ! diff -q program_rs.trace program_py.trace; then + echo "Trace differs for case: $case" + exit_code=1 + failed_tests=$((failed_tests + 1)) + else + passed_tests=$((passed_tests + 1)) + fi + + # Compare memory + echo "Running memory comparison for case: $case" + if ! ./vm/src/tests/memory_comparator.py program_rs.memory program_py.memory; then + echo "Memory differs for case: $case" + 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 program_rs.* + rm program_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 6df6e743fe..9141c3a0f9 100644 --- a/vm/src/tests/mod.rs +++ b/vm/src/tests/mod.rs @@ -113,6 +113,7 @@ fn run_cairo_1_entrypoint( let mut runner = CairoRunner::new( &(contract_class.clone().try_into().unwrap()), LayoutName::all_cairo, + None, false, false, ) @@ -217,6 +218,7 @@ fn run_cairo_1_entrypoint_with_run_resources( let mut runner = CairoRunner::new( &(contract_class.clone().try_into().unwrap()), LayoutName::all_cairo, + None, false, false, ) diff --git a/vm/src/types/errors/program_errors.rs b/vm/src/types/errors/program_errors.rs index e4a7c96d90..77f62585e0 100644 --- a/vm/src/types/errors/program_errors.rs +++ b/vm/src/types/errors/program_errors.rs @@ -9,6 +9,8 @@ pub enum ProgramError { IO(#[from] std::io::Error), #[error(transparent)] Parse(#[from] serde_json::Error), + #[error("The \"{0}\" operation is not supported")] + OperationNotSupported(String), #[error("Entrypoint {0} not found")] EntrypointNotFound(String), #[error("Constant {0} has no value")] diff --git a/vm/src/types/instance_definitions/builtins_instance_def.rs b/vm/src/types/instance_definitions/builtins_instance_def.rs index ab13cf14fb..bc33233af2 100644 --- a/vm/src/types/instance_definitions/builtins_instance_def.rs +++ b/vm/src/types/instance_definitions/builtins_instance_def.rs @@ -1,4 +1,7 @@ +use crate::types::layout::CairoLayoutParams; + use super::mod_instance_def::ModInstanceDef; +use super::LowRatio; use super::{ bitwise_instance_def::BitwiseInstanceDef, ec_op_instance_def::EcOpInstanceDef, ecdsa_instance_def::EcdsaInstanceDef, keccak_instance_def::KeccakInstanceDef, @@ -192,25 +195,69 @@ impl BuiltinsInstanceDef { } } - pub(crate) fn dynamic() -> BuiltinsInstanceDef { + pub(crate) fn dynamic(params: CairoLayoutParams) -> BuiltinsInstanceDef { + let pedersen = Some(PedersenInstanceDef { + ratio: Some(params.pedersen_ratio), + }); + let range_check = Some(RangeCheckInstanceDef { + ratio: Some(LowRatio::new_int(params.range_check_ratio)), + }); + let ecdsa = Some(EcdsaInstanceDef { + ratio: Some(params.ecdsa_ratio), + }); + let bitwise = Some(BitwiseInstanceDef { + ratio: Some(params.bitwise_ratio), + }); + let ec_op = Some(EcOpInstanceDef { + ratio: Some(params.ec_op_ratio), + }); + let keccak = Some(KeccakInstanceDef { + ratio: Some(params.keccak_ratio), + }); + let poseidon = Some(PoseidonInstanceDef { + ratio: Some(params.poseidon_ratio), + }); + let range_check96 = Some(RangeCheckInstanceDef { + ratio: Some(LowRatio::new( + params.range_check96_ratio, + params.range_check96_ratio_den, + )), + }); + #[cfg(feature = "mod_builtin")] + let add_mod = Some(ModInstanceDef { + ratio: Some(LowRatio::new( + params.add_mod_ratio, + params.add_mod_ratio_den, + )), + word_bit_len: 96, + batch_size: 1, + }); + #[cfg(feature = "mod_builtin")] + let mul_mod = Some(ModInstanceDef { + ratio: Some(LowRatio::new( + params.mul_mod_ratio, + params.mul_mod_ratio_den, + )), + word_bit_len: 96, + batch_size: 1, + }); + #[cfg(not(feature = "mod_builtin"))] + let add_mod = None; + #[cfg(not(feature = "mod_builtin"))] + let mul_mod = None; + BuiltinsInstanceDef { output: true, - 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, + pedersen, + range_check, + ecdsa, + bitwise, + ec_op, + keccak, + poseidon, + range_check96, + add_mod, + mul_mod, } } } @@ -342,17 +389,4 @@ mod tests { assert!(builtins.keccak.is_none()); assert!(builtins.poseidon.is_none()); } - - #[test] - fn get_builtins_dynamic() { - let builtins = BuiltinsInstanceDef::dynamic(); - assert!(builtins.output); - assert!(builtins.pedersen.is_some()); - assert!(builtins.range_check.is_some()); - assert!(builtins.ecdsa.is_some()); - assert!(builtins.bitwise.is_some()); - assert!(builtins.ec_op.is_some()); - assert!(builtins.keccak.is_none()); - assert!(builtins.poseidon.is_none()); - } } diff --git a/vm/src/types/instance_definitions/diluted_pool_instance_def.rs b/vm/src/types/instance_definitions/diluted_pool_instance_def.rs index 9be73a835d..d3c109b27d 100644 --- a/vm/src/types/instance_definitions/diluted_pool_instance_def.rs +++ b/vm/src/types/instance_definitions/diluted_pool_instance_def.rs @@ -3,6 +3,7 @@ use serde::Serialize; #[derive(Serialize, Debug, PartialEq)] pub(crate) struct DilutedPoolInstanceDef { pub(crate) units_per_step: u32, // 2 ^ log_units_per_step (for cairo_lang comparison) + pub(crate) fractional_units_per_step: bool, // true when log_units_per_step is negative pub(crate) spacing: u32, pub(crate) n_bits: u32, } @@ -11,6 +12,7 @@ impl DilutedPoolInstanceDef { pub(crate) fn default() -> Self { DilutedPoolInstanceDef { units_per_step: 16, + fractional_units_per_step: false, spacing: 4, n_bits: 16, } @@ -21,6 +23,15 @@ impl DilutedPoolInstanceDef { units_per_step, spacing, n_bits, + ..Self::default() + } + } + + pub(crate) fn from_log_units_per_step(log_units_per_step: i32) -> Self { + DilutedPoolInstanceDef { + units_per_step: 2_u32.pow(log_units_per_step.unsigned_abs()), + fractional_units_per_step: log_units_per_step.is_negative(), + ..DilutedPoolInstanceDef::default() } } } diff --git a/vm/src/types/instance_definitions/mod.rs b/vm/src/types/instance_definitions/mod.rs index 8f1bba2198..7884222c07 100644 --- a/vm/src/types/instance_definitions/mod.rs +++ b/vm/src/types/instance_definitions/mod.rs @@ -1,3 +1,5 @@ +use serde::Serialize; + pub mod bitwise_instance_def; pub mod builtins_instance_def; pub mod diluted_pool_instance_def; @@ -9,3 +11,25 @@ pub mod mod_instance_def; pub mod pedersen_instance_def; pub mod poseidon_instance_def; pub mod range_check_instance_def; + +#[derive(Serialize, Debug, PartialEq, Copy, Clone)] +pub struct LowRatio { + pub numerator: u32, + pub denominator: u32, +} + +impl LowRatio { + pub fn new(numerator: u32, denominator: u32) -> Self { + Self { + numerator, + denominator, + } + } + + pub fn new_int(numerator: u32) -> Self { + Self { + numerator, + denominator: 1, + } + } +} diff --git a/vm/src/types/instance_definitions/mod_instance_def.rs b/vm/src/types/instance_definitions/mod_instance_def.rs index 0b84077d33..72150b9c3b 100644 --- a/vm/src/types/instance_definitions/mod_instance_def.rs +++ b/vm/src/types/instance_definitions/mod_instance_def.rs @@ -1,12 +1,14 @@ use serde::Serialize; +use super::LowRatio; + 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) ratio: Option, pub(crate) word_bit_len: u32, pub(crate) batch_size: usize, } @@ -14,9 +16,28 @@ pub(crate) struct ModInstanceDef { impl ModInstanceDef { pub(crate) fn new(ratio: Option, batch_size: usize, word_bit_len: u32) -> Self { ModInstanceDef { - ratio, + ratio: ratio.map(LowRatio::new_int), word_bit_len, batch_size, } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::*; + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_new() { + let builtin_instance = ModInstanceDef { + ratio: Some(LowRatio::new_int(10)), + word_bit_len: 3, + batch_size: 3, + }; + assert_eq!(ModInstanceDef::new(Some(10), 3, 3), builtin_instance); + } +} 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 4524fca9bb..ed09d7cfb3 100644 --- a/vm/src/types/instance_definitions/range_check_instance_def.rs +++ b/vm/src/types/instance_definitions/range_check_instance_def.rs @@ -1,20 +1,26 @@ use serde::Serialize; + +use super::LowRatio; pub(crate) const CELLS_PER_RANGE_CHECK: u32 = 1; #[derive(Serialize, Debug, PartialEq)] pub(crate) struct RangeCheckInstanceDef { - pub(crate) ratio: Option, + pub(crate) ratio: Option, } impl Default for RangeCheckInstanceDef { fn default() -> Self { - RangeCheckInstanceDef { ratio: Some(8) } + RangeCheckInstanceDef { + ratio: Some(LowRatio::new(8, 1)), + } } } impl RangeCheckInstanceDef { pub(crate) fn new(ratio: Option) -> Self { - RangeCheckInstanceDef { ratio } + RangeCheckInstanceDef { + ratio: ratio.map(LowRatio::new_int), + } } } @@ -28,14 +34,18 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_new() { - let builtin_instance = RangeCheckInstanceDef { ratio: Some(10) }; + let builtin_instance = RangeCheckInstanceDef { + ratio: Some(LowRatio::new_int(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) }; + let builtin_instance = RangeCheckInstanceDef { + ratio: Some(LowRatio::new_int(8)), + }; assert_eq!(RangeCheckInstanceDef::default(), builtin_instance); } } diff --git a/vm/src/types/layout.rs b/vm/src/types/layout.rs index ee0fae3196..58ccd8ef34 100644 --- a/vm/src/types/layout.rs +++ b/vm/src/types/layout.rs @@ -1,17 +1,24 @@ -use crate::types::layout_name::LayoutName; +use crate::{types::layout_name::LayoutName, vm::errors::runner_errors::RunnerError}; -use super::instance_definitions::{ - builtins_instance_def::BuiltinsInstanceDef, diluted_pool_instance_def::DilutedPoolInstanceDef, +use super::{ + builtin_name::BuiltinName, + instance_definitions::{ + builtins_instance_def::BuiltinsInstanceDef, + diluted_pool_instance_def::DilutedPoolInstanceDef, + }, }; -pub(crate) const MEMORY_UNITS_PER_STEP: u32 = 8; +pub(crate) const DEFAULT_MEMORY_UNITS_PER_STEP: u32 = 8; +pub(crate) const DEFAULT_CPU_COMPONENT_STEP: u32 = 1; -use serde::Serialize; +use serde::{Deserialize, Deserializer, Serialize}; #[derive(Serialize, Debug)] pub struct CairoLayout { pub(crate) name: LayoutName, + pub(crate) cpu_component_step: u32, pub(crate) rc_units: u32, + pub(crate) memory_units_per_step: u32, pub(crate) builtins: BuiltinsInstanceDef, pub(crate) public_memory_fraction: u32, pub(crate) diluted_pool_instance_def: Option, @@ -22,6 +29,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::plain, rc_units: 16, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::plain(), public_memory_fraction: 4, diluted_pool_instance_def: None, @@ -32,6 +41,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::small, rc_units: 16, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::small(), public_memory_fraction: 4, diluted_pool_instance_def: None, @@ -42,6 +53,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::dex, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::dex(), public_memory_fraction: 4, diluted_pool_instance_def: None, @@ -52,6 +65,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::recursive, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::recursive(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), @@ -62,6 +77,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::starknet, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::starknet(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::new(2, 4, 16)), @@ -72,6 +89,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::starknet_with_keccak, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::starknet_with_keccak(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), @@ -82,6 +101,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::recursive_large_output, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::recursive_large_output(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), @@ -91,6 +112,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::recursive_with_poseidon, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::recursive_with_poseidon(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::new(8, 4, 16)), @@ -101,6 +124,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::all_cairo, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::all_cairo(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), @@ -111,26 +136,216 @@ impl CairoLayout { CairoLayout { name: LayoutName::all_solidity, rc_units: 8, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::all_solidity(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), } } - pub(crate) fn dynamic_instance() -> CairoLayout { + pub(crate) fn dynamic_instance(params: CairoLayoutParams) -> CairoLayout { CairoLayout { name: LayoutName::dynamic, - rc_units: 16, - builtins: BuiltinsInstanceDef::dynamic(), + rc_units: params.rc_units, + cpu_component_step: params.cpu_component_step, + memory_units_per_step: params.memory_units_per_step, public_memory_fraction: 8, - diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), + diluted_pool_instance_def: Some(DilutedPoolInstanceDef::from_log_units_per_step( + params.log_diluted_units_per_step, + )), + builtins: BuiltinsInstanceDef::dynamic(params), + } + } +} + +#[cfg(feature = "test_utils")] +use arbitrary::{self, Arbitrary}; + +#[cfg_attr(feature = "test_utils", derive(Arbitrary))] +#[derive(Deserialize, Debug, Clone, Default)] +#[serde(try_from = "RawCairoLayoutParams")] +pub struct CairoLayoutParams { + pub rc_units: u32, + pub cpu_component_step: u32, + pub memory_units_per_step: u32, + pub log_diluted_units_per_step: i32, + pub pedersen_ratio: u32, + pub range_check_ratio: u32, + pub ecdsa_ratio: u32, + pub bitwise_ratio: u32, + pub ec_op_ratio: u32, + pub keccak_ratio: u32, + pub poseidon_ratio: u32, + pub range_check96_ratio: u32, + pub range_check96_ratio_den: u32, + pub add_mod_ratio: u32, + pub add_mod_ratio_den: u32, + pub mul_mod_ratio: u32, + pub mul_mod_ratio_den: u32, +} + +impl CairoLayoutParams { + #[cfg(feature = "std")] + pub fn from_file(params_path: &std::path::Path) -> std::io::Result { + let params_file = std::fs::File::open(params_path)?; + let params = serde_json::from_reader(params_file)?; + Ok(params) + } +} + +// The CairoLayoutParams contains aditional constraints that can't be validated by serde alone. +// To work around this. we use an aditional structure `RawCairoLayoutParams` that gets deserialized by serde +// and then its tranformed into `CairoLayoutParams`. + +#[derive(Deserialize, Debug, Default, Clone)] +pub struct RawCairoLayoutParams { + pub rc_units: u32, + pub cpu_component_step: u32, + pub memory_units_per_step: u32, + pub log_diluted_units_per_step: i32, + #[serde(deserialize_with = "bool_from_int_or_bool")] + pub uses_pedersen_builtin: bool, + pub pedersen_ratio: u32, + #[serde(deserialize_with = "bool_from_int_or_bool")] + pub uses_range_check_builtin: bool, + pub range_check_ratio: u32, + #[serde(deserialize_with = "bool_from_int_or_bool")] + pub uses_ecdsa_builtin: bool, + pub ecdsa_ratio: u32, + #[serde(deserialize_with = "bool_from_int_or_bool")] + pub uses_bitwise_builtin: bool, + pub bitwise_ratio: u32, + #[serde(deserialize_with = "bool_from_int_or_bool")] + pub uses_ec_op_builtin: bool, + pub ec_op_ratio: u32, + #[serde(deserialize_with = "bool_from_int_or_bool")] + pub uses_keccak_builtin: bool, + pub keccak_ratio: u32, + #[serde(deserialize_with = "bool_from_int_or_bool")] + pub uses_poseidon_builtin: bool, + pub poseidon_ratio: u32, + #[serde(deserialize_with = "bool_from_int_or_bool")] + pub uses_range_check96_builtin: bool, + pub range_check96_ratio: u32, + pub range_check96_ratio_den: u32, + #[serde(deserialize_with = "bool_from_int_or_bool")] + pub uses_add_mod_builtin: bool, + pub add_mod_ratio: u32, + pub add_mod_ratio_den: u32, + #[serde(deserialize_with = "bool_from_int_or_bool")] + pub uses_mul_mod_builtin: bool, + pub mul_mod_ratio: u32, + pub mul_mod_ratio_den: u32, +} + +impl TryFrom for CairoLayoutParams { + type Error = RunnerError; + + fn try_from(value: RawCairoLayoutParams) -> Result { + if !value.uses_pedersen_builtin && value.pedersen_ratio != 0 { + return Err(RunnerError::BadDynamicLayoutBuiltinRatio( + BuiltinName::pedersen, + )); } + if !value.uses_range_check_builtin && value.range_check_ratio != 0 { + return Err(RunnerError::BadDynamicLayoutBuiltinRatio( + BuiltinName::range_check, + )); + } + if !value.uses_ecdsa_builtin && value.ecdsa_ratio != 0 { + return Err(RunnerError::BadDynamicLayoutBuiltinRatio( + BuiltinName::ecdsa, + )); + } + if !value.uses_bitwise_builtin && value.bitwise_ratio != 0 { + return Err(RunnerError::BadDynamicLayoutBuiltinRatio( + BuiltinName::bitwise, + )); + } + if !value.uses_ec_op_builtin && value.ec_op_ratio != 0 { + return Err(RunnerError::BadDynamicLayoutBuiltinRatio( + BuiltinName::ec_op, + )); + } + if !value.uses_keccak_builtin && value.keccak_ratio != 0 { + return Err(RunnerError::BadDynamicLayoutBuiltinRatio( + BuiltinName::keccak, + )); + } + if !value.uses_poseidon_builtin && value.poseidon_ratio != 0 { + return Err(RunnerError::BadDynamicLayoutBuiltinRatio( + BuiltinName::poseidon, + )); + } + if !value.uses_range_check96_builtin && value.range_check96_ratio != 0 { + return Err(RunnerError::BadDynamicLayoutBuiltinRatio( + BuiltinName::range_check96, + )); + } + if !value.uses_add_mod_builtin && value.add_mod_ratio != 0 { + return Err(RunnerError::BadDynamicLayoutBuiltinRatio( + BuiltinName::add_mod, + )); + } + if !value.uses_mul_mod_builtin && value.mul_mod_ratio != 0 { + return Err(RunnerError::BadDynamicLayoutBuiltinRatio( + BuiltinName::mul_mod, + )); + } + + Ok(CairoLayoutParams { + rc_units: value.rc_units, + log_diluted_units_per_step: value.log_diluted_units_per_step, + cpu_component_step: value.cpu_component_step, + memory_units_per_step: value.memory_units_per_step, + range_check96_ratio_den: value.range_check96_ratio_den, + mul_mod_ratio_den: value.mul_mod_ratio_den, + add_mod_ratio_den: value.add_mod_ratio_den, + pedersen_ratio: value.pedersen_ratio, + range_check_ratio: value.range_check_ratio, + ecdsa_ratio: value.ecdsa_ratio, + bitwise_ratio: value.bitwise_ratio, + ec_op_ratio: value.ec_op_ratio, + keccak_ratio: value.keccak_ratio, + poseidon_ratio: value.poseidon_ratio, + range_check96_ratio: value.range_check96_ratio, + add_mod_ratio: value.add_mod_ratio, + mul_mod_ratio: value.mul_mod_ratio, + }) + } +} + +fn bool_from_int_or_bool<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum IntOrBool { + Int(i64), + Boolean(bool), + } + + match IntOrBool::deserialize(deserializer)? { + IntOrBool::Int(0) => Ok(false), + IntOrBool::Int(_) => Ok(true), + IntOrBool::Boolean(v) => Ok(v), } } #[cfg(test)] mod tests { use super::*; + #[cfg(feature = "mod_builtin")] + use crate::types::instance_definitions::mod_instance_def::ModInstanceDef; + + use crate::types::instance_definitions::{ + 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, LowRatio, + }; #[cfg(target_arch = "wasm32")] use wasm_bindgen_test::*; @@ -257,15 +472,144 @@ mod tests { #[test] fn get_dynamic_instance() { - let layout = CairoLayout::dynamic_instance(); - let builtins = BuiltinsInstanceDef::dynamic(); + // dummy cairo layout params + let params = CairoLayoutParams { + rc_units: 32, + cpu_component_step: 8, + memory_units_per_step: 16, + log_diluted_units_per_step: 5, + pedersen_ratio: 32, + range_check_ratio: 32, + ecdsa_ratio: 32, + bitwise_ratio: 32, + ec_op_ratio: 32, + keccak_ratio: 32, + poseidon_ratio: 0, + range_check96_ratio: 8, + range_check96_ratio_den: 16, + add_mod_ratio: 8, + add_mod_ratio_den: 16, + mul_mod_ratio: 32, + mul_mod_ratio_den: 16, + }; + + let layout = CairoLayout::dynamic_instance(params); + assert_eq!(layout.name, LayoutName::dynamic); - assert_eq!(layout.rc_units, 16); - assert_eq!(layout.builtins, builtins); - assert_eq!(layout.public_memory_fraction, 8); + assert_eq!(layout.rc_units, 32); + assert_eq!(layout.cpu_component_step, 8); + assert_eq!(layout.memory_units_per_step, 16); + assert_eq!(layout.public_memory_fraction, 8); // hardcoded assert_eq!( layout.diluted_pool_instance_def, - Some(DilutedPoolInstanceDef::default()) + Some(DilutedPoolInstanceDef { + units_per_step: 32, + ..DilutedPoolInstanceDef::default() // hardcoded + }) + ); + + assert!(layout.builtins.output); + assert_eq!( + layout.builtins.pedersen, + Some(PedersenInstanceDef { ratio: Some(32) }) + ); + assert_eq!( + layout.builtins.range_check, + Some(RangeCheckInstanceDef { + ratio: Some(LowRatio::new_int(32)) + }) + ); + assert_eq!( + layout.builtins.ecdsa, + Some(EcdsaInstanceDef { ratio: Some(32) }) + ); + assert_eq!( + layout.builtins.bitwise, + Some(BitwiseInstanceDef { ratio: Some(32) }) + ); + assert_eq!( + layout.builtins.ec_op, + Some(EcOpInstanceDef { ratio: Some(32) }) + ); + assert_eq!( + layout.builtins.keccak, + Some(KeccakInstanceDef { ratio: Some(32) }) + ); + assert_eq!( + layout.builtins.poseidon, + Some(PoseidonInstanceDef { ratio: Some(0) }), ); + assert_eq!( + layout.builtins.range_check96, + Some(RangeCheckInstanceDef { + ratio: Some(LowRatio::new(8, 16)) + }) + ); + #[cfg(feature = "mod_builtin")] + { + assert_eq!( + layout.builtins.mul_mod, + Some(ModInstanceDef { + ratio: Some(LowRatio { + numerator: 32, + denominator: 16 + }), + word_bit_len: 96, // hardcoded + batch_size: 1 // hardcoded + }), + ); + assert_eq!( + layout.builtins.add_mod, + Some(ModInstanceDef { + ratio: Some(LowRatio { + numerator: 8, + denominator: 16 + }), + word_bit_len: 96, // hardcoded + batch_size: 1 // hardcoded + }) + ); + } + #[cfg(not(feature = "mod_builtin"))] + { + assert_eq!(layout.builtins.mul_mod, None,); + assert_eq!(layout.builtins.add_mod, None,); + } + } + + #[test] + fn parse_dynamic_instance() { + let cairo_layout_params_json = "{\n\ + \"rc_units\": 4,\n\ + \"log_diluted_units_per_step\": 4,\n\ + \"cpu_component_step\": 8,\n\ + \"memory_units_per_step\": 8,\n\ + \"uses_pedersen_builtin\": true,\n\ + \"pedersen_ratio\": 256,\n\ + \"uses_range_check_builtin\": true,\n\ + \"range_check_ratio\": 8,\n\ + \"uses_ecdsa_builtin\": true,\n\ + \"ecdsa_ratio\": 2048,\n\ + \"uses_bitwise_builtin\": true,\n\ + \"bitwise_ratio\": 16,\n\ + \"uses_ec_op_builtin\": true,\n\ + \"ec_op_ratio\": 1024,\n\ + \"uses_keccak_builtin\": true,\n\ + \"keccak_ratio\": 2048,\n\ + \"uses_poseidon_builtin\": true,\n\ + \"poseidon_ratio\": 256,\n\ + \"uses_range_check96_builtin\": true,\n\ + \"range_check96_ratio\": 8,\n\ + \"range_check96_ratio_den\": 1,\n\ + \"uses_add_mod_builtin\": true,\n\ + \"add_mod_ratio\": 128,\n\ + \"add_mod_ratio_den\": 1,\n\ + \"uses_mul_mod_builtin\": true,\n\ + \"mul_mod_ratio\": 256,\n\ + \"mul_mod_ratio_den\": 1\n\ + }\n\ + "; + + serde_json::from_str::(cairo_layout_params_json).unwrap(); } } diff --git a/vm/src/types/layout_name.rs b/vm/src/types/layout_name.rs index 8cfd792dc0..4082743f42 100644 --- a/vm/src/types/layout_name.rs +++ b/vm/src/types/layout_name.rs @@ -36,7 +36,7 @@ impl LayoutName { LayoutName::recursive_with_poseidon => "recursive_with_poseidon", LayoutName::all_solidity => "all_solidity", LayoutName::all_cairo => "all_cairo", - LayoutName::dynamic => "all_cairo", + LayoutName::dynamic => "dynamic", } } } diff --git a/vm/src/types/program.rs b/vm/src/types/program.rs index 0fc8c6dc83..fcc2cb1a3e 100644 --- a/vm/src/types/program.rs +++ b/vm/src/types/program.rs @@ -60,9 +60,9 @@ use arbitrary::{Arbitrary, Unstructured}; // failures. // Fields in `Program` (other than `SharedProgramData` itself) are used by the main logic. #[derive(Clone, Default, Debug, PartialEq, Eq)] -pub(crate) struct SharedProgramData { +pub struct SharedProgramData { pub(crate) data: Vec, - pub(crate) hints_collection: HintsCollection, + pub hints_collection: HintsCollection, pub(crate) main: Option, //start and end labels will only be used in proof-mode pub(crate) start: Option, @@ -70,7 +70,7 @@ pub(crate) struct SharedProgramData { pub(crate) error_message_attributes: Vec, pub(crate) instruction_locations: Option>, pub(crate) identifiers: HashMap, - pub(crate) reference_manager: Vec, + pub reference_manager: Vec, } #[cfg(feature = "test_utils")] @@ -107,13 +107,13 @@ impl<'a> Arbitrary<'a> for SharedProgramData { } #[derive(Clone, Default, Debug, PartialEq, Eq)] -pub(crate) struct HintsCollection { - hints: Vec, +pub struct HintsCollection { + pub hints: Vec, /// This maps a PC to the range of hints in `hints` that correspond to it. #[cfg(not(feature = "extensive_hints"))] pub(crate) hints_ranges: Vec, #[cfg(feature = "extensive_hints")] - pub(crate) hints_ranges: HashMap, + pub hints_ranges: HashMap, } impl HintsCollection { @@ -200,8 +200,8 @@ pub type HintRange = (usize, NonZeroUsize); #[cfg_attr(feature = "test_utils", derive(Arbitrary))] #[derive(Clone, Debug, PartialEq, Eq)] pub struct Program { - pub(crate) shared_program_data: Arc, - pub(crate) constants: HashMap, + pub shared_program_data: Arc, + pub constants: HashMap, pub(crate) builtins: Vec, } @@ -344,8 +344,8 @@ impl Program { inner_dereference: r.value_address.inner_dereference, // only store `ap` tracking data if the reference is referred to it ap_tracking_data: match (&r.value_address.offset1, &r.value_address.offset2) { - (OffsetValue::Reference(Register::AP, _, _), _) - | (_, OffsetValue::Reference(Register::AP, _, _)) => { + (OffsetValue::Reference(Register::AP, _, _, _), _) + | (_, OffsetValue::Reference(Register::AP, _, _, _)) => { Some(r.ap_tracking_data.clone()) } _ => None, @@ -720,6 +720,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -732,6 +733,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -773,6 +775,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -785,6 +788,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -934,6 +938,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -946,6 +951,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -1059,6 +1065,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -1071,6 +1078,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -1123,6 +1131,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -1135,6 +1144,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -1182,6 +1192,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); identifiers.insert( @@ -1193,6 +1204,7 @@ mod tests { full_name: Some("__main__.main.Args".to_string()), members: Some(HashMap::new()), cairo_type: None, + size: Some(0), }, ); identifiers.insert( @@ -1204,6 +1216,7 @@ mod tests { full_name: Some("__main__.main.ImplicitArgs".to_string()), members: Some(HashMap::new()), cairo_type: None, + size: Some(0), }, ); identifiers.insert( @@ -1215,6 +1228,7 @@ mod tests { full_name: Some("__main__.main.Return".to_string()), members: Some(HashMap::new()), cairo_type: None, + size: Some(0), }, ); identifiers.insert( @@ -1226,6 +1240,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); @@ -1281,6 +1296,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); identifiers.insert( @@ -1292,6 +1308,7 @@ mod tests { full_name: Some("__main__.main.Args".to_string()), members: Some(HashMap::new()), cairo_type: None, + size: Some(0), }, ); identifiers.insert( @@ -1303,6 +1320,7 @@ mod tests { full_name: Some("__main__.main.ImplicitArgs".to_string()), members: Some(HashMap::new()), cairo_type: None, + size: Some(0), }, ); identifiers.insert( @@ -1314,6 +1332,7 @@ mod tests { full_name: Some("__main__.main.Return".to_string()), members: Some(HashMap::new()), cairo_type: None, + size: Some(0), }, ); identifiers.insert( @@ -1325,6 +1344,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ); diff --git a/vm/src/utils.rs b/vm/src/utils.rs index 070efcaf6d..760c5201ad 100644 --- a/vm/src/utils.rs +++ b/vm/src/utils.rs @@ -252,19 +252,23 @@ pub mod test_utils { crate::vm::runners::cairo_runner::CairoRunner::new( &$program, crate::types::layout_name::LayoutName::all_cairo, + None, false, false, ) .unwrap() }; ($program:expr, $layout:expr) => { - crate::vm::runners::cairo_runner::CairoRunner::new(&$program, $layout, false, false) - .unwrap() + crate::vm::runners::cairo_runner::CairoRunner::new( + &$program, $layout, None, false, false, + ) + .unwrap() }; ($program:expr, $layout:expr, $proof_mode:expr) => { crate::vm::runners::cairo_runner::CairoRunner::new( &$program, $layout, + None, $proof_mode, false, ) @@ -274,6 +278,7 @@ pub mod test_utils { crate::vm::runners::cairo_runner::CairoRunner::new( &$program, $layout, + None, $proof_mode, $trace_enabled, ) diff --git a/vm/src/vm/errors/hint_errors.rs b/vm/src/vm/errors/hint_errors.rs index 0594a2f518..4851980837 100644 --- a/vm/src/vm/errors/hint_errors.rs +++ b/vm/src/vm/errors/hint_errors.rs @@ -154,6 +154,8 @@ pub enum HintError { BigintToU32Fail, #[error("BigInt to BigUint failed, BigInt is negative")] BigIntToBigUintFail, + #[error("BigUint to BigInt failed")] + BigUintToBigIntFail, #[error("Assertion failed, 0 <= ids.a % PRIME < range_check_builtin.bound \n a = {0} is out of range")] ValueOutOfRange(Box), #[error("Assertion failed, 0 <= ids.a % PRIME < range_check_builtin.bound \n a = {0} is out of range")] @@ -190,6 +192,10 @@ pub enum HintError { ExcessBalanceKeyError(Box), #[error("excess_balance_func: Failed to calculate {0}")] ExcessBalanceCalculationFailed(Box), + #[error("generate_nibbles fail: tried to read from an empty list of nibbles")] + EmptyNibbles, + #[error("circuit evalution: {0}")] + CircuitEvaluationFailed(Box), } #[cfg(test)] diff --git a/vm/src/vm/errors/runner_errors.rs b/vm/src/vm/errors/runner_errors.rs index 561f89997e..98c0787810 100644 --- a/vm/src/vm/errors/runner_errors.rs +++ b/vm/src/vm/errors/runner_errors.rs @@ -132,6 +132,10 @@ pub enum RunnerError { CairoPieProofMode, #[error("{0}: Invalid additional data")] InvalidAdditionalData(BuiltinName), + #[error("dynamic layout params is missing")] + MissingDynamicLayoutParams, + #[error("dynamic layout {0} ratio should be 0 when disabled")] + BadDynamicLayoutBuiltinRatio(BuiltinName), } #[cfg(test)] diff --git a/vm/src/vm/errors/vm_exception.rs b/vm/src/vm/errors/vm_exception.rs index 5a31d6669c..005160d95b 100644 --- a/vm/src/vm/errors/vm_exception.rs +++ b/vm/src/vm/errors/vm_exception.rs @@ -177,7 +177,7 @@ fn get_value_from_simple_reference( .get(ref_id)?; // Filter ap-based references match reference.offset1 { - OffsetValue::Reference(Register::AP, _, _) => None, + OffsetValue::Reference(Register::AP, _, _, _) => None, _ => { // Filer complex types (only felt/felt pointers) match reference.cairo_type { diff --git a/vm/src/vm/runners/builtin_runner/mod.rs b/vm/src/vm/runners/builtin_runner/mod.rs index 26d4a9026c..2374f43f3d 100644 --- a/vm/src/vm/runners/builtin_runner/mod.rs +++ b/vm/src/vm/runners/builtin_runner/mod.rs @@ -47,7 +47,7 @@ pub use bitwise::BitwiseBuiltinRunner; pub use ec_op::EcOpBuiltinRunner; pub use hash::HashBuiltinRunner; pub use modulo::ModBuiltinRunner; -use num_integer::div_floor; +use num_integer::{div_ceil, div_floor}; pub use output::{OutputBuiltinRunner, OutputBuiltinState}; pub use poseidon::PoseidonBuiltinRunner; pub use range_check::RangeCheckBuiltinRunner; @@ -170,6 +170,14 @@ impl BuiltinRunner { pub fn get_allocated_memory_units( &self, vm: &VirtualMachine, + ) -> Result { + Ok(self.get_allocated_instances(vm)? * self.cells_per_instance() as usize) + } + + ///Returns the builtin's allocated instances + pub fn get_allocated_instances( + &self, + vm: &VirtualMachine, ) -> Result { match *self { BuiltinRunner::Output(_) | BuiltinRunner::SegmentArena(_) => Ok(0), @@ -179,23 +187,40 @@ impl BuiltinRunner { // Dynamic layout has the exact number of instances it needs (up to a power of 2). let instances: usize = self.get_used_cells(&vm.segments)? / self.cells_per_instance() as usize; - let components = (instances / self.instances_per_component() as usize) - .next_power_of_two(); - Ok(self.cells_per_instance() as usize - * self.instances_per_component() as usize - * components) + let needed_components = instances / self.instances_per_component() as usize; + + let components = if needed_components > 0 { + needed_components.next_power_of_two() + } else { + 0 + }; + Ok(self.instances_per_component() as usize * components) } + // Dynamic layout allows for builtins with ratio 0 + Some(0) => Ok(0), Some(ratio) => { - let min_step = (ratio * self.instances_per_component()) as usize; + let min_step_num = (ratio * self.instances_per_component()) as usize; + let min_step = if let Some(ratio_den) = self.ratio_den() { + div_ceil(min_step_num, ratio_den as usize) + } else { + min_step_num + }; + if vm.current_step < min_step { return Err(InsufficientAllocatedCellsError::MinStepNotReached( Box::new((min_step, self.name())), ) .into()); }; - let value = safe_div_usize(vm.current_step, ratio as usize) - .map_err(|_| MemoryError::ErrorCalculatingMemoryUnits)?; - Ok(self.cells_per_instance() as usize * value) + + let allocated_instances = if let Some(ratio_den) = self.ratio_den() { + safe_div_usize(vm.current_step * ratio_den as usize, ratio as usize) + .map_err(|_| MemoryError::ErrorCalculatingMemoryUnits)? + } else { + safe_div_usize(vm.current_step, ratio as usize) + .map_err(|_| MemoryError::ErrorCalculatingMemoryUnits)? + }; + Ok(allocated_instances) } } } @@ -252,6 +277,15 @@ impl BuiltinRunner { } } + pub fn ratio_den(&self) -> Option { + match self { + BuiltinRunner::RangeCheck(range_check) => range_check.ratio_den(), + BuiltinRunner::RangeCheck96(range_check) => range_check.ratio_den(), + BuiltinRunner::Mod(modulo) => modulo.ratio_den(), + _ => None, + } + } + pub fn add_validation_rule(&self, memory: &mut Memory) { match *self { BuiltinRunner::RangeCheck(ref range_check) => range_check.add_validation_rule(memory), @@ -662,6 +696,8 @@ mod tests { use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; use crate::relocatable; use crate::types::builtin_name::BuiltinName; + use crate::types::instance_definitions::mod_instance_def::ModInstanceDef; + use crate::types::instance_definitions::LowRatio; use crate::types::program::Program; use crate::utils::test_utils::*; use crate::vm::errors::memory_errors::InsufficientAllocatedCellsError; @@ -1053,6 +1089,26 @@ mod tests { assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(256)); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_allocated_memory_units_zero_ratio() { + let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new(Some(0), true)); + let vm = vm!(); + assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(0)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_allocated_memory_units_none_ratio() { + let mut builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new(None, true)); + let mut vm = vm!(); + + builtin.initialize_segments(&mut vm.segments); + vm.compute_segments_effective_sizes(); + + assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(0)); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_range_check_usage_range_check() { @@ -1543,6 +1599,30 @@ mod tests { assert_eq!(keccak_builtin.ratio(), (Some(2048)),); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_ratio_den_tests() { + let rangecheck_builtin: BuiltinRunner = + RangeCheckBuiltinRunner::::new_with_low_ratio( + Some(LowRatio::new(1, 2)), + true, + ) + .into(); + assert_eq!(rangecheck_builtin.ratio_den(), (Some(2)),); + + let rangecheck96_builtin: BuiltinRunner = + RangeCheckBuiltinRunner::::new_with_low_ratio( + Some(LowRatio::new(1, 4)), + true, + ) + .into(); + assert_eq!(rangecheck96_builtin.ratio_den(), (Some(4)),); + + let mod_builtin: BuiltinRunner = + ModBuiltinRunner::new_add_mod(&ModInstanceDef::new(Some(5), 3, 3), true).into(); + assert_eq!(mod_builtin.ratio_den(), (Some(1)),); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn bitwise_get_used_instances_test() { diff --git a/vm/src/vm/runners/builtin_runner/modulo.rs b/vm/src/vm/runners/builtin_runner/modulo.rs index b4aa0b7fa6..085e3549b6 100644 --- a/vm/src/vm/runners/builtin_runner/modulo.rs +++ b/vm/src/vm/runners/builtin_runner/modulo.rs @@ -21,7 +21,7 @@ use crate::{ }, Felt252, }; -use core::{fmt::Display, ops::Shl}; +use core::ops::Shl; use num_bigint::BigUint; use num_integer::div_ceil; use num_integer::Integer; @@ -47,6 +47,7 @@ pub struct ModBuiltinRunner { // 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], + k_bound: BigUint, } #[derive(Debug, Clone)] @@ -55,21 +56,11 @@ pub enum ModBuiltinType { 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 { +impl ModBuiltinType { + pub(crate) fn operation_string(&self) -> &'static str { match self { - Operation::Mul => "*".fmt(f), - Operation::Add => "+".fmt(f), - Operation::Sub => "-".fmt(f), - Operation::DivMod(_) => "/".fmt(f), + ModBuiltinType::Mul => "*", + ModBuiltinType::Add => "+", } } } @@ -85,17 +76,28 @@ struct Inputs { impl ModBuiltinRunner { pub(crate) fn new_add_mod(instance_def: &ModInstanceDef, included: bool) -> Self { - Self::new(instance_def.clone(), included, ModBuiltinType::Add) + Self::new( + instance_def.clone(), + included, + ModBuiltinType::Add, + Some(2u32.into()), + ) } pub(crate) fn new_mul_mod(instance_def: &ModInstanceDef, included: bool) -> Self { - Self::new(instance_def.clone(), included, ModBuiltinType::Mul) + Self::new(instance_def.clone(), included, ModBuiltinType::Mul, None) } - fn new(instance_def: ModInstanceDef, included: bool, builtin_type: ModBuiltinType) -> Self { + fn new( + instance_def: ModInstanceDef, + included: bool, + builtin_type: ModBuiltinType, + k_bound: Option, + ) -> 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); + let int_lim = BigUint::from(2_u32).pow(N_WORDS as u32 * instance_def.word_bit_len); Self { builtin_type, base: 0, @@ -106,6 +108,7 @@ impl ModBuiltinRunner { zero_segment_size, shift, shift_powers, + k_bound: k_bound.unwrap_or(int_lim), } } @@ -137,7 +140,11 @@ impl ModBuiltinRunner { } pub fn ratio(&self) -> Option { - self.instance_def.ratio + self.instance_def.ratio.map(|ratio| ratio.numerator) + } + + pub fn ratio_den(&self) -> Option { + self.instance_def.ratio.map(|ratio| ratio.denominator) } pub fn batch_size(&self) -> usize { @@ -256,6 +263,8 @@ impl ModBuiltinRunner { }); } + instances.sort_by_key(|input| input.index); + vec![PrivateInput::Mod(ModInput { instances, zero_value_address: relocation_table @@ -435,13 +444,13 @@ impl ModBuiltinRunner { // Fills a value in the values table, if exactly one value is missing. // Returns true on success or if all values are already known. + // + // The builtin type (add or mul) determines which operation to perform 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(); @@ -458,19 +467,19 @@ impl ModBuiltinRunner { 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); + let value = self.apply_operation(a, b, &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); + let value = self.deduce_operand(a, c, &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); + let value = self.deduce_operand(b, c, &inputs.p)?; self.write_n_words_value(memory, addresses[0], value)?; Ok(true) } @@ -539,44 +548,33 @@ impl ModBuiltinRunner { 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, - )); + if add_mod_index < add_mod_n { + if let Some((_, add_mod_runner, _)) = add_mod { + if add_mod_runner.fill_value(memory, &add_mod_inputs, add_mod_index)? { + add_mod_index += 1; + continue; + } + } } + + if mul_mod_index < mul_mod_n { + if let Some((_, mul_mod_runner, _)) = mul_mod { + if mul_mod_runner.fill_value(memory, &mul_mod_inputs, mul_mod_index)? { + mul_mod_index += 1; + } + continue; + } + } + + return Err(RunnerError::FillMemoryCoudNotFillTable( + add_mod_index, + mul_mod_index, + )); } Ok(()) } @@ -625,14 +623,11 @@ impl ModBuiltinRunner { 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) { + let a_op_b = self.apply_operation(&a, &b, &inputs.p)?; + if a_op_b.mod_floor(&inputs.p) != c.mod_floor(&inputs.p) { // Build error string let p = inputs.p; + let op = self.builtin_type.operation_string(); 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(), @@ -667,23 +662,145 @@ impl ModBuiltinRunner { 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)?, - }) + // Calculates the result of `lhs OP rhs` + // + // The builtin type (add or mul) determines the OP + pub(crate) fn apply_operation( + &self, + lhs: &BigUint, + rhs: &BigUint, + prime: &BigUint, + ) -> Result { + let full_value = match self.builtin_type { + ModBuiltinType::Mul => lhs * rhs, + ModBuiltinType::Add => lhs + rhs, + }; + + let value = if full_value < &self.k_bound * prime { + full_value.mod_floor(prime) + } else { + full_value - (&self.k_bound - 1u32) * prime + }; + + Ok(value) + } + + // Given `known OP unknown = result (mod p)`, it deduces `unknown` + // + // The builtin type (add or mul) determines the OP + pub(crate) fn deduce_operand( + &self, + known: &BigUint, + result: &BigUint, + prime: &BigUint, + ) -> Result { + let value = match self.builtin_type { + ModBuiltinType::Add => { + if known <= result { + result - known + } else { + result + prime - known + } + } + ModBuiltinType::Mul => div_mod_unsigned(result, known, prime)?, + }; + Ok(value) + } } #[cfg(test)] mod tests { + use super::*; + + #[test] + fn apply_operation_add() { + let builtin = ModBuiltinRunner::new_add_mod(&ModInstanceDef::new(Some(8), 8, 8), true); + + assert_eq!( + builtin + .apply_operation( + &BigUint::from(2u32), + &BigUint::from(3u32), + &BigUint::from(7u32) + ) + .unwrap(), + BigUint::from(5u32) + ); + + assert_eq!( + builtin + .apply_operation( + &BigUint::from(5u32), + &BigUint::from(5u32), + &BigUint::from(5u32) + ) + .unwrap(), + BigUint::from(5u32) + ); + } + + #[test] + fn apply_operation_mul() { + let builtin = ModBuiltinRunner::new_mul_mod(&ModInstanceDef::new(Some(8), 8, 8), true); + + assert_eq!( + builtin + .apply_operation( + &BigUint::from(2u32), + &BigUint::from(3u32), + &BigUint::from(7u32) + ) + .unwrap(), + BigUint::from(6u32) + ); + } + + #[test] + fn deduce_operand_add() { + let builtin = ModBuiltinRunner::new_add_mod(&ModInstanceDef::new(Some(8), 8, 8), true); + + assert_eq!( + builtin + .deduce_operand( + &BigUint::from(2u32), + &BigUint::from(5u32), + &BigUint::from(7u32) + ) + .unwrap(), + BigUint::from(3u32) + ); + assert_eq!( + builtin + .deduce_operand( + &BigUint::from(5u32), + &BigUint::from(2u32), + &BigUint::from(7u32) + ) + .unwrap(), + BigUint::from(4u32) + ); + } + + #[test] + fn deduce_operand_mul() { + let builtin = ModBuiltinRunner::new_mul_mod(&ModInstanceDef::new(Some(8), 8, 8), true); + + assert_eq!( + builtin + .deduce_operand( + &BigUint::from(2u32), + &BigUint::from(1u32), + &BigUint::from(7u32) + ) + .unwrap(), + BigUint::from(4u32) + ); + } + #[test] #[cfg(feature = "mod_builtin")] fn test_air_private_input_all_cairo() { - use super::*; use crate::{ air_private_input::{ModInput, ModInputInstance, ModInputMemoryVars, PrivateInput}, hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor, @@ -699,7 +816,8 @@ mod tests { let mut hint_processor = BuiltinHintProcessor::new_empty(); let program = Program::from_bytes(program_data, Some("main")).unwrap(); - let mut runner = CairoRunner::new(&program, LayoutName::all_cairo, true, false).unwrap(); + let mut runner = + CairoRunner::new(&program, LayoutName::all_cairo, None, true, false).unwrap(); let end = runner.initialize(false).unwrap(); // Modify add_mod & mul_mod params @@ -734,8 +852,8 @@ mod tests { a2: Felt252::ZERO, a3: Felt252::ZERO, b_offset: 12, - b0: Felt252::ZERO, - b1: Felt252::ZERO, + b0: Felt252::ONE, + b1: Felt252::ONE, b2: Felt252::ZERO, b3: Felt252::ZERO, c_offset: 4, @@ -797,8 +915,8 @@ mod tests { 0, ModInputMemoryVars { a_offset: 12, - a0: Felt252::ZERO, - a1: Felt252::ZERO, + a0: Felt252::ONE, + a1: Felt252::ONE, a2: Felt252::ZERO, a3: Felt252::ZERO, b_offset: 8, diff --git a/vm/src/vm/runners/builtin_runner/range_check.rs b/vm/src/vm/runners/builtin_runner/range_check.rs index 9c19de3309..a4800bcf78 100644 --- a/vm/src/vm/runners/builtin_runner/range_check.rs +++ b/vm/src/vm/runners/builtin_runner/range_check.rs @@ -4,7 +4,7 @@ use crate::{ cmp::{max, min}, prelude::*, }, - types::builtin_name::BuiltinName, + types::{builtin_name::BuiltinName, instance_definitions::LowRatio}, }; use crate::Felt252; @@ -35,7 +35,7 @@ lazy_static! { #[derive(Debug, Clone)] pub struct RangeCheckBuiltinRunner { - ratio: Option, + ratio: Option, base: usize, pub(crate) stop_ptr: Option, pub(crate) included: bool, @@ -43,6 +43,18 @@ pub struct RangeCheckBuiltinRunner { impl RangeCheckBuiltinRunner { pub fn new(ratio: Option, included: bool) -> RangeCheckBuiltinRunner { + RangeCheckBuiltinRunner { + ratio: ratio.map(LowRatio::new_int), + base: 0, + stop_ptr: None, + included, + } + } + + pub fn new_with_low_ratio( + ratio: Option, + included: bool, + ) -> RangeCheckBuiltinRunner { RangeCheckBuiltinRunner { ratio, base: 0, @@ -68,7 +80,11 @@ impl RangeCheckBuiltinRunner { } pub fn ratio(&self) -> Option { - self.ratio + self.ratio.map(|ratio| ratio.numerator) + } + + pub fn ratio_den(&self) -> Option { + self.ratio.map(|ratio| ratio.denominator) } pub fn name(&self) -> BuiltinName { diff --git a/vm/src/vm/runners/builtin_runner/signature.rs b/vm/src/vm/runners/builtin_runner/signature.rs index 6e94206ef3..481d867b95 100644 --- a/vm/src/vm/runners/builtin_runner/signature.rs +++ b/vm/src/vm/runners/builtin_runner/signature.rs @@ -3,7 +3,6 @@ use crate::math_utils::div_mod; use crate::stdlib::{cell::RefCell, collections::HashMap, prelude::*, rc::Rc}; use crate::types::builtin_name::BuiltinName; -use crate::types::errors::math_errors::MathError; use crate::types::instance_definitions::ecdsa_instance_def::CELLS_PER_SIGNATURE; use crate::vm::errors::runner_errors::RunnerError; use crate::vm::runners::cairo_pie::BuiltinAdditionalData; @@ -22,7 +21,7 @@ use lazy_static::lazy_static; use num_bigint::{BigInt, Sign}; use num_integer::div_ceil; use num_traits::{Num, One}; -use starknet_crypto::{verify, FieldElement, Signature}; +use starknet_crypto::{verify, Signature}; lazy_static! { static ref EC_ORDER: BigInt = BigInt::from_str_radix( @@ -60,8 +59,8 @@ impl SignatureBuiltinRunner { let r_be_bytes = r.to_bytes_be(); let s_be_bytes = s.to_bytes_be(); let (r_felt, s_felt) = ( - FieldElement::from_bytes_be(&r_be_bytes).map_err(|_| MathError::ByteConversionError)?, - FieldElement::from_bytes_be(&s_be_bytes).map_err(|_| MathError::ByteConversionError)?, + Felt252::from_bytes_be(&r_be_bytes), + Felt252::from_bytes_be(&s_be_bytes), ); let signature = Signature { @@ -127,11 +126,9 @@ impl SignatureBuiltinRunner { .get(&pubkey_addr) .ok_or_else(|| MemoryError::SignatureNotFound(Box::new(pubkey_addr)))?; - let public_key = FieldElement::from_bytes_be(&pubkey.to_bytes_be()) - .map_err(|_| MathError::ByteConversionError)?; + let public_key = Felt252::from_bytes_be(&pubkey.to_bytes_be()); let (r, s) = (signature.r, signature.s); - let message = FieldElement::from_bytes_be(&msg.to_bytes_be()) - .map_err(|_| MathError::ByteConversionError)?; + let message = Felt252::from_bytes_be(&msg.to_bytes_be()); match verify(&public_key, &message, &r, &s) { Ok(true) => Ok(vec![]), _ => Err(MemoryError::InvalidSignature(Box::new(( @@ -198,10 +195,8 @@ impl SignatureBuiltinRunner { self.signatures.borrow_mut().insert( *addr, Signature { - r: FieldElement::from_bytes_be(&r.to_bytes_be()) - .map_err(|_| MathError::ByteConversionError)?, - s: FieldElement::from_bytes_be(&s.to_bytes_be()) - .map_err(|_| MathError::ByteConversionError)?, + r: Felt252::from_bytes_be(&r.to_bytes_be()), + s: Felt252::from_bytes_be(&s.to_bytes_be()), }, ); } @@ -210,7 +205,17 @@ impl SignatureBuiltinRunner { pub fn air_private_input(&self, memory: &Memory) -> Vec { let mut private_inputs = vec![]; - for (addr, signature) in self.signatures.borrow().iter() { + + // Collect and sort the signatures by their index before the loop + let binding = self.signatures.borrow(); + let mut sorted_signatures: Vec<_> = binding.iter().collect(); + sorted_signatures.sort_by_key(|(addr, _)| { + addr.offset + .checked_div(CELLS_PER_SIGNATURE as usize) + .unwrap_or_default() + }); + + for (addr, signature) in sorted_signatures { if let (Ok(pubkey), Some(msg)) = ( memory.get_integer(*addr), (*addr + 1_usize) @@ -513,8 +518,8 @@ mod tests { let signatures = HashMap::from([( Relocatable::from((4, 0)), Signature { - r: FieldElement::from_dec_str("45678").unwrap(), - s: FieldElement::from_dec_str("1239").unwrap(), + r: Felt252::from_dec_str("45678").unwrap(), + s: Felt252::from_dec_str("1239").unwrap(), }, )]); builtin.signatures = Rc::new(RefCell::new(signatures)); @@ -534,8 +539,8 @@ mod tests { let signatures = HashMap::from([( Relocatable::from((0, 0)), Signature { - r: FieldElement::from_dec_str("45678").unwrap(), - s: FieldElement::from_dec_str("1239").unwrap(), + r: Felt252::from_dec_str("45678").unwrap(), + s: Felt252::from_dec_str("1239").unwrap(), }, )]); builtin_a.signatures = Rc::new(RefCell::new(signatures)); @@ -554,4 +559,67 @@ mod tests { assert_eq!(signature_a.s, signature_b.s); } } + #[test] + fn get_air_private_input() { + let mut builtin = SignatureBuiltinRunner::new(Some(512), true); + + builtin.base = 0; + + let signature1_r = Felt252::from(1234); + let signature1_s = Felt252::from(5678); + let signature2_r = Felt252::from(8765); + let signature2_s = Felt252::from(4321); + + let sig1_addr = Relocatable::from((builtin.base as isize, 0)); + let sig2_addr = Relocatable::from((builtin.base as isize, CELLS_PER_SIGNATURE as usize)); + + builtin + .add_signature(sig1_addr, &(signature1_r, signature1_s)) + .unwrap(); + builtin + .add_signature(sig2_addr, &(signature2_r, signature2_s)) + .unwrap(); + + let pubkey1 = Felt252::from(1111); + let msg1 = Felt252::from(2222); + let pubkey2 = Felt252::from(3333); + let msg2 = Felt252::from(4444); + + let segments = segments![ + ((0, 0), 1111), + ((0, 1), 2222), + ((0, 2), 3333), + ((0, 3), 4444) + ]; + let w1 = + Felt252::from(&div_mod(&BigInt::one(), &signature1_s.to_bigint(), &EC_ORDER).unwrap()); + + let w2 = + Felt252::from(&div_mod(&BigInt::one(), &signature2_s.to_bigint(), &EC_ORDER).unwrap()); + + let expected_private_inputs = vec![ + PrivateInput::Signature(PrivateInputSignature { + index: 0, + pubkey: pubkey1, + msg: msg1, + signature_input: SignatureInput { + r: signature1_r, + w: w1, + }, + }), + PrivateInput::Signature(PrivateInputSignature { + index: 1, + pubkey: pubkey2, + msg: msg2, + signature_input: SignatureInput { + r: signature2_r, + w: w2, + }, + }), + ]; + + let private_inputs = builtin.air_private_input(&segments.memory); + + assert_eq!(private_inputs, expected_private_inputs); + } } diff --git a/vm/src/vm/runners/cairo_pie.rs b/vm/src/vm/runners/cairo_pie.rs index d86b1840c8..4383671971 100644 --- a/vm/src/vm/runners/cairo_pie.rs +++ b/vm/src/vm/runners/cairo_pie.rs @@ -289,6 +289,10 @@ impl CairoPie { return Err(CairoPieValidationError::DiffAdditionalData); } for (name, data) in self.additional_data.0.iter() { + // As documented above, we skip the pedersen field when comparing. + if *name == BuiltinName::pedersen { + continue; + } if !pie.additional_data.0.get(name).is_some_and(|d| d == data) { return Err(CairoPieValidationError::DiffAdditionalDataForBuiltin(*name)); } @@ -759,6 +763,9 @@ pub(super) mod serde_impl { BuiltinName::ec_op, BuiltinName::keccak, BuiltinName::poseidon, + BuiltinName::range_check96, + BuiltinName::add_mod, + BuiltinName::mul_mod, ]; for name in BUILTIN_ORDERED_LIST { diff --git a/vm/src/vm/runners/cairo_runner.rs b/vm/src/vm/runners/cairo_runner.rs index ecf47892b0..65209c35de 100644 --- a/vm/src/vm/runners/cairo_runner.rs +++ b/vm/src/vm/runners/cairo_runner.rs @@ -1,13 +1,14 @@ use crate::{ air_private_input::AirPrivateInput, air_public_input::{PublicInput, PublicInputError}, + math_utils::safe_div_usize, stdlib::{ any::Any, collections::{HashMap, HashSet}, ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}, prelude::*, }, - types::{builtin_name::BuiltinName, layout::MEMORY_UNITS_PER_STEP, layout_name::LayoutName}, + types::{builtin_name::BuiltinName, layout::CairoLayoutParams, layout_name::LayoutName}, vm::{ runners::builtin_runner::SegmentArenaBuiltinRunner, trace::trace_entry::{relocate_trace_register, RelocatedTraceEntry}, @@ -17,7 +18,6 @@ use crate::{ use crate::{ hint_processor::hint_processor_definition::{HintProcessor, HintReference}, - math_utils::safe_div_usize, types::{ errors::{math_errors::MathError, program_errors::ProgramError}, exec_scope::ExecutionScopes, @@ -168,9 +168,12 @@ pub enum RunnerMode { } impl CairoRunner { + /// The `dynamic_layout_params` argument should only be used with dynamic layout. + /// It is ignored otherwise. pub fn new_v2( program: &Program, layout: LayoutName, + dynamic_layout_params: Option, mode: RunnerMode, trace_enabled: bool, ) -> Result { @@ -185,7 +188,12 @@ impl CairoRunner { LayoutName::recursive_with_poseidon => CairoLayout::recursive_with_poseidon(), LayoutName::all_cairo => CairoLayout::all_cairo_instance(), LayoutName::all_solidity => CairoLayout::all_solidity_instance(), - LayoutName::dynamic => CairoLayout::dynamic_instance(), + LayoutName::dynamic => { + let params = + dynamic_layout_params.ok_or(RunnerError::MissingDynamicLayoutParams)?; + + CairoLayout::dynamic_instance(params) + } }; Ok(CairoRunner { program: program.clone(), @@ -215,6 +223,7 @@ impl CairoRunner { pub fn new( program: &Program, layout: LayoutName, + dynamic_layout_params: Option, proof_mode: bool, trace_enabled: bool, ) -> Result { @@ -222,11 +231,18 @@ impl CairoRunner { Self::new_v2( program, layout, + dynamic_layout_params, RunnerMode::ProofModeCanonical, trace_enabled, ) } else { - Self::new_v2(program, layout, RunnerMode::ExecutionMode, trace_enabled) + Self::new_v2( + program, + layout, + dynamic_layout_params, + RunnerMode::ExecutionMode, + trace_enabled, + ) } } @@ -288,7 +304,7 @@ impl CairoRunner { let included = program_builtins.remove(&BuiltinName::range_check); if included || self.is_proof_mode() { self.vm.builtin_runners.push( - RangeCheckBuiltinRunner::::new( + RangeCheckBuiltinRunner::::new_with_low_ratio( instance_def.ratio, included, ) @@ -346,8 +362,11 @@ impl CairoRunner { let included = program_builtins.remove(&BuiltinName::range_check96); if included || self.is_proof_mode() { self.vm.builtin_runners.push( - RangeCheckBuiltinRunner::::new(instance_def.ratio, included) - .into(), + RangeCheckBuiltinRunner::::new_with_low_ratio( + instance_def.ratio, + included, + ) + .into(), ); } } @@ -822,14 +841,20 @@ impl CairoRunner { diluted_pool_instance.n_bits, ); - let multiplier = safe_div_usize( - self.vm.current_step, - builtin_runner.ratio().unwrap_or(1) as usize, - )?; + let multiplier = builtin_runner.get_allocated_instances(&self.vm)?; + used_units_by_builtins += used_units * multiplier; } - let diluted_units = diluted_pool_instance.units_per_step as usize * self.vm.current_step; + let diluted_units = if !diluted_pool_instance.fractional_units_per_step { + diluted_pool_instance.units_per_step as usize * self.vm.current_step + } else { + safe_div_usize( + self.vm.current_step, + diluted_pool_instance.units_per_step as usize, + )? + }; + let unused_diluted_units = diluted_units.saturating_sub(used_units_by_builtins); let diluted_usage_upper_bound = 1usize << diluted_pool_instance.n_bits; @@ -1157,7 +1182,7 @@ 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 = MEMORY_UNITS_PER_STEP * vm_current_step_u32; + let total_memory_units = instance.memory_units_per_step * vm_current_step_u32; let (public_memory_units, rem) = div_rem(total_memory_units, instance.public_memory_fraction); if rem != 0 { @@ -4464,6 +4489,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, )] .into_iter() @@ -4492,6 +4518,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ), ( @@ -4503,6 +4530,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, ), ] @@ -4532,6 +4560,7 @@ mod tests { full_name: None, members: None, cairo_type: None, + size: None, }, )] .into_iter() diff --git a/vm/src/vm/vm_core.rs b/vm/src/vm/vm_core.rs index a65014a7b3..5862aee093 100644 --- a/vm/src/vm/vm_core.rs +++ b/vm/src/vm/vm_core.rs @@ -1000,6 +1000,10 @@ impl VirtualMachine { /// Add a new relocation rule. /// + /// When using feature "extensive_hints" the destination is allowed to be an Integer (via + /// MaybeRelocatable). Relocating memory to anything other than a `Relocatable` is generally + /// not useful, but it does make the implementation consistent with the pythonic version. + /// /// Will return an error if any of the following conditions are not met: /// - Source address's segment must be negative (temporary). /// - Source address's offset must be zero. @@ -1007,7 +1011,8 @@ impl VirtualMachine { pub fn add_relocation_rule( &mut self, src_ptr: Relocatable, - dst_ptr: Relocatable, + #[cfg(not(feature = "extensive_hints"))] dst_ptr: Relocatable, + #[cfg(feature = "extensive_hints")] dst_ptr: MaybeRelocatable, ) -> Result<(), MemoryError> { self.segments.memory.add_relocation_rule(src_ptr, dst_ptr) } diff --git a/vm/src/vm/vm_memory/memory.rs b/vm/src/vm/vm_memory/memory.rs index 9dbfc9a790..affd0a06d4 100644 --- a/vm/src/vm/vm_memory/memory.rs +++ b/vm/src/vm/vm_memory/memory.rs @@ -161,7 +161,10 @@ pub struct Memory { pub(crate) temp_data: Vec>, // relocation_rules's keys map to temp_data's indices and therefore begin at // zero; that is, segment_index = -1 maps to key 0, -2 to key 1... + #[cfg(not(feature = "extensive_hints"))] pub(crate) relocation_rules: HashMap, + #[cfg(feature = "extensive_hints")] + pub(crate) relocation_rules: HashMap, pub validated_addresses: AddressSet, validation_rules: Vec>, } @@ -247,6 +250,7 @@ impl Memory { } // Version of Memory.relocate_value() that doesn't require a self reference + #[cfg(not(feature = "extensive_hints"))] fn relocate_address( addr: Relocatable, relocation_rules: &HashMap, @@ -260,6 +264,23 @@ impl Memory { } Ok(addr.into()) } + #[cfg(feature = "extensive_hints")] + fn relocate_address( + addr: Relocatable, + relocation_rules: &HashMap, + ) -> Result { + if addr.segment_index < 0 { + // Adjust the segment index to begin at zero, as per the struct field's + // comment. + if let Some(x) = relocation_rules.get(&(-(addr.segment_index + 1) as usize)) { + return Ok(match x { + MaybeRelocatable::RelocatableValue(r) => (*r + addr.offset)?.into(), + MaybeRelocatable::Int(i) => i.into(), + }); + } + } + Ok(addr.into()) + } /// Relocates the memory according to the relocation rules and clears `self.relocaction_rules`. pub fn relocate_memory(&mut self) -> Result<(), MemoryError> { @@ -289,6 +310,15 @@ impl Memory { for index in (0..self.temp_data.len()).rev() { if let Some(base_addr) = self.relocation_rules.get(&index) { let data_segment = self.temp_data.remove(index); + + #[cfg(feature = "extensive_hints")] + let base_addr = match base_addr { + MaybeRelocatable::RelocatableValue(addr) => addr, + MaybeRelocatable::Int(_) => { + continue; + } + }; + // Insert the to-be relocated segment into the real memory let mut addr = *base_addr; if let Some(s) = self.data.get_mut(addr.segment_index as usize) { @@ -310,13 +340,17 @@ impl Memory { self.relocation_rules.clear(); Ok(()) } - /// Add a new relocation rule. /// + /// When using feature "extensive_hints" the destination is allowed to be an Integer (via + /// MaybeRelocatable). Relocating memory to anything other than a `Relocatable` is generally + /// not useful, but it does make the implementation consistent with the pythonic version. + /// /// Will return an error if any of the following conditions are not met: /// - Source address's segment must be negative (temporary). /// - Source address's offset must be zero. /// - There shouldn't already be relocation at the source segment. + #[cfg(not(feature = "extensive_hints"))] pub(crate) fn add_relocation_rule( &mut self, src_ptr: Relocatable, @@ -341,6 +375,31 @@ impl Memory { self.relocation_rules.insert(segment_index, dst_ptr); Ok(()) } + #[cfg(feature = "extensive_hints")] + pub(crate) fn add_relocation_rule( + &mut self, + src_ptr: Relocatable, + dst: MaybeRelocatable, + ) -> Result<(), MemoryError> { + if src_ptr.segment_index >= 0 { + return Err(MemoryError::AddressNotInTemporarySegment( + src_ptr.segment_index, + )); + } + if src_ptr.offset != 0 { + return Err(MemoryError::NonZeroOffset(src_ptr.offset)); + } + + // Adjust the segment index to begin at zero, as per the struct field's + // comment. + let segment_index = -(src_ptr.segment_index + 1) as usize; + if self.relocation_rules.contains_key(&segment_index) { + return Err(MemoryError::DuplicatedRelocation(src_ptr.segment_index)); + } + + self.relocation_rules.insert(segment_index, dst); + Ok(()) + } /// Gets the value from memory address as a Felt252 value. /// Returns an Error if the value at the memory address is missing or not a Felt252. @@ -646,6 +705,7 @@ pub(crate) trait RelocateValue<'a, Input: 'a, Output: 'a> { fn relocate_value(&self, value: Input) -> Result; } +#[cfg(not(feature = "extensive_hints"))] impl RelocateValue<'_, Relocatable, Relocatable> for Memory { fn relocate_value(&self, addr: Relocatable) -> Result { if addr.segment_index < 0 { @@ -661,6 +721,27 @@ impl RelocateValue<'_, Relocatable, Relocatable> for Memory { Ok(addr) } } +#[cfg(feature = "extensive_hints")] +impl RelocateValue<'_, Relocatable, MaybeRelocatable> for Memory { + fn relocate_value(&self, addr: Relocatable) -> Result { + if addr.segment_index < 0 { + // Adjust the segment index to begin at zero, as per the struct field's + // comment. + if let Some(x) = self + .relocation_rules + .get(&(-(addr.segment_index + 1) as usize)) + { + return Ok(match x { + MaybeRelocatable::RelocatableValue(r) => { + (*r + addr.offset).map_err(MemoryError::Math)?.into() + } + MaybeRelocatable::Int(i) => i.into(), + }); + } + } + Ok(addr.into()) + } +} impl<'a> RelocateValue<'a, &'a Felt252, &'a Felt252> for Memory { fn relocate_value(&self, value: &'a Felt252) -> Result<&'a Felt252, MemoryError> { @@ -676,7 +757,12 @@ impl<'a> RelocateValue<'a, &'a MaybeRelocatable, Cow<'a, MaybeRelocatable>> for Ok(match value { MaybeRelocatable::Int(_) => Cow::Borrowed(value), MaybeRelocatable::RelocatableValue(addr) => { - Cow::Owned(self.relocate_value(*addr)?.into()) + #[cfg(not(feature = "extensive_hints"))] + let v = self.relocate_value(*addr)?.into(); + #[cfg(feature = "extensive_hints")] + let v = self.relocate_value(*addr)?; + + Cow::Owned(v) } }) } @@ -1617,6 +1703,64 @@ mod memory_tests { ); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[cfg(feature = "extensive_hints")] + fn relocate_address_to_integer() { + let mut memory = Memory::new(); + memory + .add_relocation_rule((-1, 0).into(), 0.into()) + .unwrap(); + memory + .add_relocation_rule((-2, 0).into(), 42.into()) + .unwrap(); + + assert_eq!( + Memory::relocate_address((-1, 0).into(), &memory.relocation_rules).unwrap(), + MaybeRelocatable::Int(0.into()), + ); + assert_eq!( + Memory::relocate_address((-2, 0).into(), &memory.relocation_rules).unwrap(), + MaybeRelocatable::Int(42.into()), + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[cfg(feature = "extensive_hints")] + fn relocate_address_integer_no_duplicates() { + let mut memory = Memory::new(); + memory + .add_relocation_rule((-1, 0).into(), 1.into()) + .unwrap(); + assert_eq!( + memory.add_relocation_rule((-1, 0).into(), 42.into()), + Err(MemoryError::DuplicatedRelocation(-1)) + ); + assert_eq!( + memory.add_relocation_rule((-1, 0).into(), (2, 0).into()), + Err(MemoryError::DuplicatedRelocation(-1)) + ); + + assert_eq!( + Memory::relocate_address((-1, 0).into(), &memory.relocation_rules).unwrap(), + MaybeRelocatable::Int(1.into()), + ); + + memory + .add_relocation_rule((-2, 0).into(), (3, 0).into()) + .unwrap(); + assert_eq!( + memory.add_relocation_rule((-2, 0).into(), 1.into()), + Err(MemoryError::DuplicatedRelocation(-2)) + ); + + assert_eq!( + Memory::relocate_address((-2, 0).into(), &memory.relocation_rules).unwrap(), + MaybeRelocatable::RelocatableValue((3, 0).into()), + ); + } + #[test] fn mark_address_as_accessed() { let mut memory = memory![((0, 0), 0)];