diff --git a/.github/workflows/hyperfine.yml b/.github/workflows/hyperfine.yml new file mode 100644 index 0000000000..11ee7c788b --- /dev/null +++ b/.github/workflows/hyperfine.yml @@ -0,0 +1,199 @@ +name: Hyperfine Benchmark + +on: + pull_request: + branches: [ '*' ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + +jobs: + build-programs: + strategy: + matrix: + branch: [ base, head ] + name: Build Cairo programs for ${{ matrix.branch }} + runs-on: ubuntu-22.04 + outputs: + benchmark-hashes-base: ${{ steps.export-hashes.outputs.benchmark-hashes-base }} + benchmark-hashes-head: ${{ steps.export-hashes.outputs.benchmark-hashes-head }} + steps: + - name: Checkout base commit + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request[matrix.branch].sha }} + + - name: Fetch from cache + uses: actions/cache@v3 + id: cache + with: + path: base_programs/*.json + key: benchmarks-${{ hashFiles( 'cairo_programs/benchmarks/*.cairo' ) }} + restore-keys: benchmarks- + + - name: Install Python + if: ${{ steps.cache.outputs.cache-hit != 'true' }} + uses: actions/setup-python@v4 + with: + python-version: '3.9' + + - name: Install Cairo compiler + if: ${{ steps.cache.outputs.cache-hit != 'true' }} + run: pip install -r requirements.txt + + - name: Build programs + if: ${{ steps.cache.outputs.cache-hit != 'true' }} + run: | + make -j cairo_bench_programs + mkdir -p ${{ matrix.branch }}_programs + cp cairo_programs/benchmarks/*.json ${{ matrix.branch }}_programs + + - name: Export benchmark hashes + id: export-hashes + run: + echo "benchmark-hashes-${{ matrix.branch }}=${{ hashFiles( 'cairo_programs/benchmarks/*.cairo' ) }}" >> "$GITHUB_OUTPUT" + + + build-binaries: + strategy: + matrix: + branch: [ base, head ] + name: Build cairo-vm-cli for ${{ matrix.branch }} + runs-on: ubuntu-22.04 + steps: + - name: Populate cache + uses: actions/cache@v3 + 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@stable + with: + toolchain: 1.66.1 + + - name: Checkout + if: ${{ steps.cache.outputs.cache-hit != 'true' }} + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request[matrix.branch].sha }} + + - name: Fetch Rust cache + if: ${{ steps.cache.outputs.cache-hit != 'true' }} + uses: Swatinem/rust-cache@v2 + + - name: Build binary + if: ${{ steps.cache.outputs.cache-hit != 'true' }} + run: | + cargo b --release -p cairo-vm-cli + mkdir bin + cp target/release/cairo-vm-cli bin/cairo-vm-cli-${{ matrix.branch }} + + + run-hyperfine: + strategy: + matrix: + program_state: [ modified, unmodified ] + name: Run benchmarks for ${{ matrix.program_state }} programs + needs: [ build-programs, build-binaries ] + runs-on: ubuntu-22.04 + steps: + - name: Install Hyperfine + uses: taiki-e/install-action@v2 + with: + tool: hyperfine@1.16 + + - name: Fetch base binary + uses: actions/cache/restore@v3 + with: + path: bin/cairo-vm-cli-base + key: binary-${{ github.event.pull_request.base.sha }} + + - name: Fetch HEAD binary + uses: actions/cache/restore@v3 + with: + path: bin/cairo-vm-cli-head + key: binary-${{ github.event.pull_request.head.sha }} + + - name: Fetch base programs + uses: actions/cache/restore@v3 + with: + path: base_programs/*.json + key: benchmarks-${{ needs.build-programs.outputs.benchmark-hashes-base }} + + - name: Fetch head programs + uses: actions/cache/restore@v3 + with: + path: head_programs/*.json + key: benchmarks-${{ needs.build-programs.outputs.benchmark-hashes-head }} + + - name: Benchmark ${{ matrix.program_state }} programs + id: run-benchmarks + run: | + mkdir target_programs + if [ 'modified' = ${{ matrix.program_state }} ]; then + BINS=head + for f in head_programs/*.json; do + # Only run new or modified benchmarks + if !cmp -s ${f/head/base} $f; then + cp $f target_programs/ + fi + done + else + BINS="base,head" + for f in base_programs/*.json; do + # Only run unmodified benchmarks + if cmp -s ${f/base/head} $f; then + cp $f target_programs/ + fi + done + fi + find target_programs -name '*.json' -exec basename -s .json '{}' '+' | \ + sort | xargs -P 2 -I '{program}' \ + hyperfine -N -r 10 --export-markdown "target_programs/{program}.md" \ + -L bin "$BINS" -n "{bin} {program}" \ + -s "cat ./bin/cairo-vm-cli-{bin} target_programs/{program}.json" \ + "./bin/cairo-vm-cli-{bin} --layout starknet_with_keccak \ + --memory_file /dev/null --trace_file /dev/null target_programs/{program}.json" + echo "benchmark_count=$(ls target_programs/*.md | wc -l)" >> $GITHUB_OUTPUT + + - name: Print tables + if: steps.run-benchmarks.outputs.benchmark_count != 0 + run: | + { + echo "Benchmark Results for ${{ matrix.program_state }} programs :rocket:" + for f in target_programs/*.md; do + echo + cat $f + done + } | tee -a comment_body.md + + - name: Find comment + if: ${{ steps.run-benchmarks.outputs.benchmark_count != 0 }} + uses: peter-evans/find-comment@v2 + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: Benchmark Results for ${{ matrix.program_state }} programs + + - name: Create comment + if: steps.fc.outputs.comment-id == '' && steps.run-benchmarks.outputs.benchmark_count != 0 + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ github.event.pull_request.number }} + body-path: comment_body.md + + - name: Update comment + if: steps.fc.outputs.comment-id != '' && steps.run-benchmarks.outputs.benchmark_count != 0 + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + body-path: comment_body.md + edit-mode: replace diff --git a/.github/workflows/iai_pr.yml b/.github/workflows/iai_pr.yml index 727480956b..c2d6c3dd64 100644 --- a/.github/workflows/iai_pr.yml +++ b/.github/workflows/iai_pr.yml @@ -1,9 +1,5 @@ name: benchmark_pr -on: - pull_request: - branches: [ '*' ] - jobs: fetch-iai-results: runs-on: ubuntu-22.04 diff --git a/.gitignore b/.gitignore index 6d6c3eaba7..6ce6b063ab 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,6 @@ **/*.memory **/*.swp bench/results +.python-version +cairo-rs-env/* +cairo-rs-pypy-env/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 1eb6131fd7..5b23d95862 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ ## Cairo-VM Changelog #### Upcoming Changes + +* Add missing `\n` character in traceback string [#997](https://github.com/lambdaclass/cairo-rs/pull/997) + * BugFix: Add missing `\n` character after traceback lines when the filename is missing ("Unknown Location") + * 0.11 Support * Layouts update [#874](https://github.com/lambdaclass/cairo-rs/pull/874) * Keccak builtin updated [#873](https://github.com/lambdaclass/cairo-rs/pull/873), [#883](https://github.com/lambdaclass/cairo-rs/pull/883) @@ -12,55 +16,50 @@ * Added dynamic layout [#879](https://github.com/lambdaclass/cairo-rs/pull/879) * `get_segment_size` was exposed [#934](https://github.com/lambdaclass/cairo-rs/pull/934) -#### [0.3.0-rc1] - 2023-04-13 -* Derive Deserialize for ExecutionResources [#922](https://github.com/lambdaclass/cairo-rs/pull/922) -* Remove builtin names from VirtualMachine.builtin_runners [#921](https://github.com/lambdaclass/cairo-rs/pull/921) -* Implemented hints on common/ec.cairo [#888](https://github.com/lambdaclass/cairo-rs/pull/888) -* Changed `Memory.insert` argument types [#902](https://github.com/lambdaclass/cairo-rs/pull/902) -* feat: implemented `Deserialize` on Program by changing builtins field type to enum [#896](https://github.com/lambdaclass/cairo-rs/pull/896) -* Effective size computation from the VM exposed [#887](https://github.com/lambdaclass/cairo-rs/pull/887) -* Wasm32 Support! [#828](https://github.com/lambdaclass/cairo-rs/pull/828), [#893](https://github.com/lambdaclass/cairo-rs/pull/893) -* `MathError` added for math operation [#855](https://github.com/lambdaclass/cairo-rs/pull/855) -* Check for overflows in relocatable operations [#859](https://github.com/lambdaclass/cairo-rs/pull/859) -* Use `Relocatable` instead of `&MaybeRelocatable` in `load_data` and `get_range`[#860](https://github.com/lambdaclass/cairo-rs/pull/860) [#867](https://github.com/lambdaclass/cairo-rs/pull/867) -* Memory-related errors moved to `MemoryError` [#854](https://github.com/lambdaclass/cairo-rs/pull/854) - * Removed unused error variants - * Moved memory-related error variants to `MemoryError` - * Changed memory getters to return `MemoryError` instead of `VirtualMachineError` - * Changed all memory-related errors in hint from `HintError::Internal(VmError::...` to `HintError::Memory(MemoryError::...` -* feat: Builder pattern for `VirtualMachine` [#820](https://github.com/lambdaclass/cairo-rs/pull/820) -* Simplified `Memory::get` return type to `Option` [#852](https://github.com/lambdaclass/cairo-rs/pull/852) -* Improved idenitifier variable error handling [#851](https://github.com/lambdaclass/cairo-rs/pull/851) -* `CairoRunner::write_output` now prints missing and relocatable values [#853](https://github.com/lambdaclass/cairo-rs/pull/853) -* `VirtualMachineError::FailedToComputeOperands` error message expanded [#848](https://github.com/lambdaclass/cairo-rs/pull/848) -* Builtin names made public [#849](https://github.com/lambdaclass/cairo-rs/pull/849) -* `secure_run` flag moved to `CairoRunConfig` struct [#832](https://github.com/lambdaclass/cairo-rs/pull/832) -* `vm_core` error types revised and iimplemented `AddAssign` for `Relocatable` [#837](https://github.com/lambdaclass/cairo-rs/pull/837) -* `to_bigint` and `to_biguint` deprecated [#757](https://github.com/lambdaclass/cairo-rs/pull/757) -* `Memory` moved into `MemorySegmentManager` [#830](https://github.com/lambdaclass/cairo-rs/pull/830) - * To reduce the complexity of the VM's memory and enforce proper usage (as the memory and its segment manager are now a "unified" entity) - * Removed `memory` field from `VirtualMachine` - * Added `memory` field to `MemorySegmentManager` - * Removed `Memory` argument from methods where `MemorySegmentManager` is also an argument - * Added test macro `segments` (an extension of the `memory` macro) -* `Display` trait added to Memory struct [#812](https://github.com/lambdaclass/cairo-rs/pull/812) -* feat: Extensible VirtualMachineError and removed PartialEq trait [#783](https://github.com/lambdaclass/cairo-rs/pull/783) - * `VirtualMachineError::Other(anyhow::Error)` was added to allow to returning custom errors when using `cairo-rs` - * The `PartialEq` trait was removed from the `VirtualMachineError` enum -* VM hooks added as a conditional feature [#761](https://github.com/lambdaclass/cairo-rs/pull/761) - * Cairo-rs based testing tools such as cairo-foundry or those built by FuzzingLabs need access to the state of the VM at specific points during the execution. - * This PR adds the possibility for users of the cairo-rs lib to execute their custom additional code during the program execution. - * The Rust "feature" mechanism was used in order to guarantee that this ability is only available when the lib user needs it, and is not compiled when it's not required. - * Three hooks were created: - * before the first step - * before each step - * after each step -* ExecutionResource operations: add and substract [#774](https://github.com/lambdaclass/cairo-rs/pull/774), multiplication [#908](https://github.com/lambdaclass/cairo-rs/pull/908) , and `AddAssign` [#914](https://github.com/lambdaclass/cairo-rs/pull/914) +* Add missing hint on cairo_secp lib [#990](https://github.com/lambdaclass/cairo-rs/pull/990): + + `BuiltinHintProcessor` now supports the following hint: + + ```python + from starkware.cairo.common.cairo_secp.secp_utils import pack + + slope = pack(ids.slope, PRIME) + x = pack(ids.point.x, PRIME) + y = pack(ids.point.y, PRIME) -* Add missing hint on cairo_secp lib [#984]: + value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P + ``` + +* Add missing hint on cairo_secp lib [#989](https://github.com/lambdaclass/cairo-rs/pull/989): `BuiltinHintProcessor` now supports the following hint: + + ```python + from starkware.cairo.common.cairo_secp.secp_utils import SECP_P + q, r = divmod(pack(ids.val, PRIME), SECP_P) + assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." + ids.q = q % PRIME + ``` + +* Add missing hint on cairo_secp lib [#986](https://github.com/lambdaclass/cairo-rs/pull/986): + + `BuiltinHintProcessor` now supports the following hint: + + ```python + from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack + from starkware.python.math_utils import div_mod + + # Compute the slope. + x = pack(ids.pt.x, PRIME) + y = pack(ids.pt.y, PRIME) + value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P) + ``` + +* Add missing hint on cairo_secp lib [#984](https://github.com/lambdaclass/cairo-rs/pull/984): + + `BuiltinHintProcessor` now supports the following hint: + ```python from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack from starkware.python.math_utils import div_mod @@ -95,6 +94,8 @@ segments.write_arg(ids.keccak_ptr, output_values) ``` +* Make hints code `src/hint_processor/builtin_hint_processor/hint_code.rs` public [#988](https://github.com/lambdaclass/cairo-rs/pull/988) + * Implement hints on uint384 lib (Part 1) [#960](https://github.com/lambdaclass/cairo-rs/pull/960) `BuiltinHintProcessor` now supports the following hints: @@ -198,6 +199,10 @@ ids.root.d2 = root_split[2] ``` +* Re-export the `cairo-felt` crate as `cairo_vm::felt` [#981](https://github.com/lambdaclass/cairo-rs/pull/981) + * Removes the need of explicitly importing `cairo-felt` in downstream projects + and helps ensure there is no version mismatch caused by that + * Implement hint on `uint256_mul_div_mod`[#957](https://github.com/lambdaclass/cairo-rs/pull/957) `BuiltinHintProcessor` now supports the following hint: @@ -218,68 +223,50 @@ Used by the common library function `uint256_mul_div_mod` -* Add missing hint on cairo_secp lib [#986]: - - `BuiltinHintProcessor` now supports the following hint: - ```python - from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack - from starkware.python.math_utils import div_mod - - # Compute the slope. - x = pack(ids.pt.x, PRIME) - y = pack(ids.pt.y, PRIME) - value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P) - ``` - -* Add missing hint on cairo_secp lib [#984]: - `BuiltinHintProcessor` now supports the following hint: - ```python - from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack - from starkware.python.math_utils import div_mod - - # Compute the slope. - x0 = pack(ids.pt0.x, PRIME) - y0 = pack(ids.pt0.y, PRIME) - x1 = pack(ids.pt1.x, PRIME) - y1 = pack(ids.pt1.y, PRIME) - value = slope = div_mod(y0 - y1, x0 - x1, SECP_P) - ``` - -* Add missing hint on cairo_secp lib [#989]: - - `BuiltinHintProcessor` now supports the following hint: - ```python - from starkware.cairo.common.cairo_secp.secp_utils import SECP_P - q, r = divmod(pack(ids.val, PRIME), SECP_P) - assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." - ids.q = q % PRIME - ``` - -* Add missing hint on cairo_secp lib [#986]: - `BuiltinHintProcessor` now supports the following hint: - ```python - from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack - from starkware.python.math_utils import div_mod - - # Compute the slope. - x = pack(ids.pt.x, PRIME) - y = pack(ids.pt.y, PRIME) - value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P) - ``` - -* Add missing hint on cairo_secp lib [#984]: - `BuiltinHintProcessor` now supports the following hint: - ```python - from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack - from starkware.python.math_utils import div_mod - - # Compute the slope. - x0 = pack(ids.pt0.x, PRIME) - y0 = pack(ids.pt0.y, PRIME) - x1 = pack(ids.pt1.x, PRIME) - y1 = pack(ids.pt1.y, PRIME) - value = slope = div_mod(y0 - y1, x0 - x1, SECP_P) - ``` +#### [0.3.0-rc1] - 2023-04-13 +* Derive Deserialize for ExecutionResources [#922](https://github.com/lambdaclass/cairo-rs/pull/922) +* Remove builtin names from VirtualMachine.builtin_runners [#921](https://github.com/lambdaclass/cairo-rs/pull/921) +* Implemented hints on common/ec.cairo [#888](https://github.com/lambdaclass/cairo-rs/pull/888) +* Changed `Memory.insert` argument types [#902](https://github.com/lambdaclass/cairo-rs/pull/902) +* feat: implemented `Deserialize` on Program by changing builtins field type to enum [#896](https://github.com/lambdaclass/cairo-rs/pull/896) +* Effective size computation from the VM exposed [#887](https://github.com/lambdaclass/cairo-rs/pull/887) +* Wasm32 Support! [#828](https://github.com/lambdaclass/cairo-rs/pull/828), [#893](https://github.com/lambdaclass/cairo-rs/pull/893) +* `MathError` added for math operation [#855](https://github.com/lambdaclass/cairo-rs/pull/855) +* Check for overflows in relocatable operations [#859](https://github.com/lambdaclass/cairo-rs/pull/859) +* Use `Relocatable` instead of `&MaybeRelocatable` in `load_data` and `get_range`[#860](https://github.com/lambdaclass/cairo-rs/pull/860) [#867](https://github.com/lambdaclass/cairo-rs/pull/867) +* Memory-related errors moved to `MemoryError` [#854](https://github.com/lambdaclass/cairo-rs/pull/854) + * Removed unused error variants + * Moved memory-related error variants to `MemoryError` + * Changed memory getters to return `MemoryError` instead of `VirtualMachineError` + * Changed all memory-related errors in hint from `HintError::Internal(VmError::...` to `HintError::Memory(MemoryError::...` +* feat: Builder pattern for `VirtualMachine` [#820](https://github.com/lambdaclass/cairo-rs/pull/820) +* Simplified `Memory::get` return type to `Option` [#852](https://github.com/lambdaclass/cairo-rs/pull/852) +* Improved idenitifier variable error handling [#851](https://github.com/lambdaclass/cairo-rs/pull/851) +* `CairoRunner::write_output` now prints missing and relocatable values [#853](https://github.com/lambdaclass/cairo-rs/pull/853) +* `VirtualMachineError::FailedToComputeOperands` error message expanded [#848](https://github.com/lambdaclass/cairo-rs/pull/848) +* Builtin names made public [#849](https://github.com/lambdaclass/cairo-rs/pull/849) +* `secure_run` flag moved to `CairoRunConfig` struct [#832](https://github.com/lambdaclass/cairo-rs/pull/832) +* `vm_core` error types revised and iimplemented `AddAssign` for `Relocatable` [#837](https://github.com/lambdaclass/cairo-rs/pull/837) +* `to_bigint` and `to_biguint` deprecated [#757](https://github.com/lambdaclass/cairo-rs/pull/757) +* `Memory` moved into `MemorySegmentManager` [#830](https://github.com/lambdaclass/cairo-rs/pull/830) + * To reduce the complexity of the VM's memory and enforce proper usage (as the memory and its segment manager are now a "unified" entity) + * Removed `memory` field from `VirtualMachine` + * Added `memory` field to `MemorySegmentManager` + * Removed `Memory` argument from methods where `MemorySegmentManager` is also an argument + * Added test macro `segments` (an extension of the `memory` macro) +* `Display` trait added to Memory struct [#812](https://github.com/lambdaclass/cairo-rs/pull/812) +* feat: Extensible VirtualMachineError and removed PartialEq trait [#783](https://github.com/lambdaclass/cairo-rs/pull/783) + * `VirtualMachineError::Other(anyhow::Error)` was added to allow to returning custom errors when using `cairo-rs` + * The `PartialEq` trait was removed from the `VirtualMachineError` enum +* VM hooks added as a conditional feature [#761](https://github.com/lambdaclass/cairo-rs/pull/761) + * Cairo-rs based testing tools such as cairo-foundry or those built by FuzzingLabs need access to the state of the VM at specific points during the execution. + * This PR adds the possibility for users of the cairo-rs lib to execute their custom additional code during the program execution. + * The Rust "feature" mechanism was used in order to guarantee that this ability is only available when the lib user needs it, and is not compiled when it's not required. + * Three hooks were created: + * before the first step + * before each step + * after each step +* ExecutionResource operations: add and substract [#774](https://github.com/lambdaclass/cairo-rs/pull/774), multiplication [#908](https://github.com/lambdaclass/cairo-rs/pull/908) , and `AddAssign` [#914](https://github.com/lambdaclass/cairo-rs/pull/914) * Move `Memory` into `MemorySegmentManager` [#830](https://github.com/lambdaclass/cairo-rs/pull/830) * Structural changes: diff --git a/Makefile b/Makefile index 9494b6feff..3ffec8df5c 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ RELBIN:=target/release/cairo-rs-run DBGBIN:=target/debug/cairo-rs-run -.PHONY: deps build run check test clippy coverage benchmark flamegraph \ +.PHONY: deps deps-macos cargo-deps build run check test clippy coverage benchmark flamegraph \ compare_benchmarks_deps compare_benchmarks docs clean \ compare_vm_output compare_trace_memory compare_trace compare_memory \ compare_trace_memory_proof compare_trace_proof compare_memory_proof \ @@ -88,18 +88,36 @@ COMPILED_BAD_TESTS:=$(patsubst $(BAD_TEST_DIR)/%.cairo, $(BAD_TEST_DIR)/%.json, $(BAD_TEST_DIR)/%.json: $(BAD_TEST_DIR)/%.cairo cairo-compile $< --output $@ -deps: +cargo-deps: cargo install --version 1.1.0 cargo-criterion cargo install --version 0.6.1 flamegraph cargo install --version 1.14.0 hyperfine cargo install --version 0.9.49 cargo-nextest cargo install --version 0.5.9 cargo-llvm-cov - pyenv install pypy3.7-7.3.9 - pyenv global pypy3.7-7.3.9 - pip install -r requirements.txt - pyenv install 3.7.12 - pyenv global 3.7.12 - pip install -r requirements.txt + cargo install --version 0.11.0 wasm-pack + +deps: + $(MAKE) cargo-deps + pyenv install -s pypy3.9-7.3.9 + PYENV_VERSION=pypy3.9-7.3.9 python -m venv cairo-rs-pypy-env + . cairo-rs-pypy-env/bin/activate ; \ + pip install -r requirements.txt ; \ + pyenv install -s 3.9.15 + PYENV_VERSION=3.9.15 python -m venv cairo-rs-env + . cairo-rs-env/bin/activate ; \ + pip install -r requirements.txt ; \ + +deps-macos: + $(MAKE) cargo-deps + brew install gmp + arch -x86_64 pyenv install -s pypy3.9-7.3.9 + PYENV_VERSION=pypy3.9-7.3.9 python -m venv cairo-rs-pypy-env + . cairo-rs-pypy-env/bin/activate ; \ + CFLAGS=-I/opt/homebrew/opt/gmp/include LDFLAGS=-L/opt/homebrew/opt/gmp/lib pip install -r requirements.txt ; \ + pyenv install -s 3.9.15 + PYENV_VERSION=3.9.15 python -m venv cairo-rs-env + . cairo-rs-env/bin/activate ; \ + CFLAGS=-I/opt/homebrew/opt/gmp/include LDFLAGS=-L/opt/homebrew/opt/gmp/lib pip install -r requirements.txt ; \ $(RELBIN): cargo build --release @@ -182,4 +200,5 @@ clean: rm -f $(TEST_PROOF_DIR)/*.json rm -f $(TEST_PROOF_DIR)/*.memory rm -f $(TEST_PROOF_DIR)/*.trace - + rm -rf cairo-rs-env + rm -rf cairo-rs-pypy-env diff --git a/README.md b/README.md index f8d5a78031..ca6263249e 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ It's Turing-complete and it was created by [Starkware](https://starkware.co/) as **Optional** -These dependencies are only necessary in order to run the original VM and compile Cairo programs. +These dependencies are only necessary in order to run the original VM, compile Cairo programs, and run tests. - PyEnv with Python 3.9 - cairo-lang diff --git a/bench/run_benchmarks.sh b/bench/run_benchmarks.sh index d44130d21f..230092b4f0 100755 --- a/bench/run_benchmarks.sh +++ b/bench/run_benchmarks.sh @@ -9,7 +9,7 @@ for file in $(ls $tests_path | grep .cairo | sed -E 's/\.cairo//'); do export PATH="$(pyenv root)/shims:$PATH" hyperfine \ - -n "Cairo VM (CPython)" "PYENV_VERSION=3.7.12 cairo-run --layout all --program $tests_path/$file.json" \ - -n "Cairo VM (PyPy)" "PYENV_VERSION=pypy3.7-7.3.9 cairo-run --layout all --program $tests_path/$file.json" \ - -n "cairo-rs (Rust)" "../target/release/cairo-vm-cli $tests_path/$file.json --layout all" + -n "Cairo VM (CPython)" "PYENV_VERSION=3.9.15 cairo-run --layout starknet_with_keccak --program $tests_path/$file.json" \ + -n "Cairo VM (PyPy)" "PYENV_VERSION=pypy3.9-7.3.9 cairo-run --layout starknet_with_keccak --program $tests_path/$file.json" \ + -n "cairo-rs (Rust)" "../target/release/cairo-vm-cli $tests_path/$file.json --layout starknet_with_keccak" done diff --git a/cairo_programs/efficient_secp256r1_ec.cairo b/cairo_programs/efficient_secp256r1_ec.cairo new file mode 100644 index 0000000000..dcfc588f14 --- /dev/null +++ b/cairo_programs/efficient_secp256r1_ec.cairo @@ -0,0 +1,83 @@ +%builtins range_check + +// Source: https://github.com/myBraavos/efficient-secp256r1/blob/73cca4d53730cb8b2dcf34e36c7b8f34b96b3230/src/secp256r1/ec.cairo#L127 + +from starkware.cairo.common.cairo_secp.bigint import BigInt3, UnreducedBigInt3, nondet_bigint3 +from starkware.cairo.common.cairo_secp.ec import EcPoint, compute_doubling_slope +from starkware.cairo.common.cairo_secp.field import ( + is_zero, + unreduced_mul, + unreduced_sqr, + verify_zero, +) + +// Computes the addition of a given point to itself. +// +// Arguments: +// point - the point to operate on. +// +// Returns: +// res - a point representing point + point. +func ec_double{range_check_ptr}(point: EcPoint) -> (res: EcPoint) { + // The zero point. + if (point.x.d0 == 0) { + if (point.x.d1 == 0) { + if (point.x.d2 == 0) { + return (res=point); + } + } + } + + let (slope: BigInt3) = compute_doubling_slope(point); + let (slope_sqr: UnreducedBigInt3) = unreduced_sqr(slope); + + %{ + from starkware.cairo.common.cairo_secp.secp_utils import pack + + slope = pack(ids.slope, PRIME) + x = pack(ids.point.x, PRIME) + y = pack(ids.point.y, PRIME) + + value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P + %} + let (new_x: BigInt3) = nondet_bigint3(); + + %{ value = new_y = (slope * (x - new_x) - y) % SECP_P %} + let (new_y: BigInt3) = nondet_bigint3(); + verify_zero( + UnreducedBigInt3( + d0=slope_sqr.d0 - new_x.d0 - 2 * point.x.d0, + d1=slope_sqr.d1 - new_x.d1 - 2 * point.x.d1, + d2=slope_sqr.d2 - new_x.d2 - 2 * point.x.d2, + ), + ); + + let (x_diff_slope: UnreducedBigInt3) = unreduced_mul( + BigInt3(d0=point.x.d0 - new_x.d0, d1=point.x.d1 - new_x.d1, d2=point.x.d2 - new_x.d2), slope + ); + verify_zero( + UnreducedBigInt3( + d0=x_diff_slope.d0 - point.y.d0 - new_y.d0, + d1=x_diff_slope.d1 - point.y.d1 - new_y.d1, + d2=x_diff_slope.d2 - point.y.d2 - new_y.d2, + ), + ); + + return (res=EcPoint(new_x, new_y)); +} + +func main{range_check_ptr: felt}() { + let x = BigInt3(235, 522, 111); + let y = BigInt3(1323, 15124, 796759); + + let point = EcPoint(x, y); + + let (res) = ec_double(point); + + assert res = EcPoint( + BigInt3(64960503569511978748964127, 74077005698377320581054215, 17246103581201827820088765), + BigInt3(13476289913106792137931934, 29193128211607101710049068, 18079689234850912663169436), + ); + + return (); +} diff --git a/requirements.txt b/requirements.txt index 9eca8e3b19..6e0951b922 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ ecdsa==0.18.0 +bitarray==2.7.3 fastecdsa==2.2.3 sympy==1.11.1 typeguard==2.13.3 diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index 31aa8e2aa8..58fc12429d 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -390,7 +390,7 @@ impl HintProcessor for BuiltinHintProcessor { "pt0", "pt1", ), - hint_code::EC_DOUBLE_ASSIGN_NEW_X => { + hint_code::EC_DOUBLE_ASSIGN_NEW_X_V1 | hint_code::EC_DOUBLE_ASSIGN_NEW_X_V2 => { ec_double_assign_new_x(vm, exec_scopes, &hint_data.ids_data, &hint_data.ap_tracking) } hint_code::EC_DOUBLE_ASSIGN_NEW_Y => ec_double_assign_new_y(exec_scopes), diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 8bf667122e..1b6e765eb2 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -1,36 +1,35 @@ -pub(crate) const ADD_SEGMENT: &str = "memory[ap] = segments.add()"; +pub const ADD_SEGMENT: &str = "memory[ap] = segments.add()"; -pub(crate) const VM_ENTER_SCOPE: &str = "vm_enter_scope()"; -pub(crate) const VM_EXIT_SCOPE: &str = "vm_exit_scope()"; +pub const VM_ENTER_SCOPE: &str = "vm_enter_scope()"; +pub const VM_EXIT_SCOPE: &str = "vm_exit_scope()"; -pub(crate) const MEMCPY_ENTER_SCOPE: &str = "vm_enter_scope({'n': ids.len})"; -pub(crate) const MEMCPY_CONTINUE_COPYING: &str = r#"n -= 1 +pub const MEMCPY_ENTER_SCOPE: &str = "vm_enter_scope({'n': ids.len})"; +pub const MEMCPY_CONTINUE_COPYING: &str = r#"n -= 1 ids.continue_copying = 1 if n > 0 else 0"#; -pub(crate) const MEMSET_ENTER_SCOPE: &str = "vm_enter_scope({'n': ids.n})"; -pub(crate) const MEMSET_CONTINUE_LOOP: &str = r#"n -= 1 +pub const MEMSET_ENTER_SCOPE: &str = "vm_enter_scope({'n': ids.n})"; +pub const MEMSET_CONTINUE_LOOP: &str = r#"n -= 1 ids.continue_loop = 1 if n > 0 else 0"#; -pub(crate) const POW: &str = "ids.locs.bit = (ids.prev_locs.exp % PRIME) & 1"; +pub const POW: &str = "ids.locs.bit = (ids.prev_locs.exp % PRIME) & 1"; -pub(crate) const IS_NN: &str = - "memory[ap] = 0 if 0 <= (ids.a % PRIME) < range_check_builtin.bound else 1"; -pub(crate) const IS_NN_OUT_OF_RANGE: &str = +pub const IS_NN: &str = "memory[ap] = 0 if 0 <= (ids.a % PRIME) < range_check_builtin.bound else 1"; +pub const IS_NN_OUT_OF_RANGE: &str = "memory[ap] = 0 if 0 <= ((-ids.a - 1) % PRIME) < range_check_builtin.bound else 1"; -pub(crate) const IS_LE_FELT: &str = "memory[ap] = 0 if (ids.a % PRIME) <= (ids.b % PRIME) else 1"; -pub(crate) const IS_POSITIVE: &str = r#"from starkware.cairo.common.math_utils import is_positive +pub const IS_LE_FELT: &str = "memory[ap] = 0 if (ids.a % PRIME) <= (ids.b % PRIME) else 1"; +pub const IS_POSITIVE: &str = r#"from starkware.cairo.common.math_utils import is_positive ids.is_positive = 1 if is_positive( value=ids.value, prime=PRIME, rc_bound=range_check_builtin.bound) else 0"#; -pub(crate) const ASSERT_NN: &str = r#"from starkware.cairo.common.math_utils import assert_integer +pub const ASSERT_NN: &str = r#"from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.a) assert 0 <= ids.a % PRIME < range_check_builtin.bound, f'a = {ids.a} is out of range.'"#; -pub(crate) const ASSERT_NOT_ZERO: &str = r#"from starkware.cairo.common.math_utils import assert_integer +pub const ASSERT_NOT_ZERO: &str = r#"from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.value) assert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.'"#; -pub(crate) const ASSERT_NOT_EQUAL: &str = r#"from starkware.cairo.lang.vm.relocatable import RelocatableValue +pub const ASSERT_NOT_EQUAL: &str = r#"from starkware.cairo.lang.vm.relocatable import RelocatableValue both_ints = isinstance(ids.a, int) and isinstance(ids.b, int) both_relocatable = ( isinstance(ids.a, RelocatableValue) and isinstance(ids.b, RelocatableValue) and @@ -39,7 +38,7 @@ assert both_ints or both_relocatable, \ f'assert_not_equal failed: non-comparable values: {ids.a}, {ids.b}.' assert (ids.a - ids.b) % PRIME != 0, f'assert_not_equal failed: {ids.a} = {ids.b}.'"#; -pub(crate) const ASSERT_LE_FELT: &str = r#"import itertools +pub const ASSERT_LE_FELT: &str = r#"import itertools from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.a) @@ -59,20 +58,20 @@ memory[ids.range_check_ptr + 1], memory[ids.range_check_ptr + 0] = ( memory[ids.range_check_ptr + 3], memory[ids.range_check_ptr + 2] = ( divmod(lengths_and_indices[1][0], ids.PRIME_OVER_2_HIGH))"#; -pub(crate) const ASSERT_LE_FELT_EXCLUDED_0: &str = "memory[ap] = 1 if excluded != 0 else 0"; -pub(crate) const ASSERT_LE_FELT_EXCLUDED_1: &str = "memory[ap] = 1 if excluded != 1 else 0"; -pub(crate) const ASSERT_LE_FELT_EXCLUDED_2: &str = "assert excluded == 2"; +pub const ASSERT_LE_FELT_EXCLUDED_0: &str = "memory[ap] = 1 if excluded != 0 else 0"; +pub const ASSERT_LE_FELT_EXCLUDED_1: &str = "memory[ap] = 1 if excluded != 1 else 0"; +pub const ASSERT_LE_FELT_EXCLUDED_2: &str = "assert excluded == 2"; -pub(crate) const ASSERT_LT_FELT: &str = r#"from starkware.cairo.common.math_utils import assert_integer +pub const ASSERT_LT_FELT: &str = r#"from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.a) assert_integer(ids.b) assert (ids.a % PRIME) < (ids.b % PRIME), \ f'a = {ids.a % PRIME} is not less than b = {ids.b % PRIME}.'"#; -pub(crate) const SPLIT_INT_ASSERT_RANGE: &str = +pub const SPLIT_INT_ASSERT_RANGE: &str = "assert ids.value == 0, 'split_int(): value is out of range.'"; -pub(crate) const ASSERT_250_BITS: &str = r#"from starkware.cairo.common.math_utils import as_int +pub const ASSERT_250_BITS: &str = r#"from starkware.cairo.common.math_utils import as_int # Correctness check. value = as_int(ids.value, PRIME) % PRIME @@ -81,32 +80,32 @@ assert value < ids.UPPER_BOUND, f'{value} is outside of the range [0, 2**250).' # Calculation for the assertion. ids.high, ids.low = divmod(ids.value, ids.SHIFT)"#; -pub(crate) const SPLIT_INT: &str = r#"memory[ids.output] = res = (int(ids.value) % PRIME) % ids.base +pub const SPLIT_INT: &str = r#"memory[ids.output] = res = (int(ids.value) % PRIME) % ids.base assert res < ids.bound, f'split_int(): Limb {res} is out of range.'"#; -pub(crate) const SPLIT_64: &str = r#"ids.low = ids.a & ((1<<64) - 1) +pub const SPLIT_64: &str = r#"ids.low = ids.a & ((1<<64) - 1) ids.high = ids.a >> 64"#; -pub(crate) const SPLIT_FELT: &str = r#"from starkware.cairo.common.math_utils import assert_integer +pub const SPLIT_FELT: &str = r#"from starkware.cairo.common.math_utils import assert_integer assert ids.MAX_HIGH < 2**128 and ids.MAX_LOW < 2**128 assert PRIME - 1 == ids.MAX_HIGH * 2**128 + ids.MAX_LOW assert_integer(ids.value) ids.low = ids.value & ((1 << 128) - 1) ids.high = ids.value >> 128"#; -pub(crate) const SQRT: &str = r#"from starkware.python.math_utils import isqrt +pub const SQRT: &str = r#"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)"#; -pub(crate) const UNSIGNED_DIV_REM: &str = r#"from starkware.cairo.common.math_utils import assert_integer +pub const UNSIGNED_DIV_REM: &str = r#"from starkware.cairo.common.math_utils import assert_integer assert_integer(ids.div) assert 0 < ids.div <= PRIME // range_check_builtin.bound, \ f'div={hex(ids.div)} is out of the valid range.' ids.q, ids.r = divmod(ids.value, ids.div)"#; -pub(crate) const SIGNED_DIV_REM: &str = r#"from starkware.cairo.common.math_utils import as_int, assert_integer +pub const SIGNED_DIV_REM: &str = r#"from starkware.cairo.common.math_utils import as_int, assert_integer assert_integer(ids.div) assert 0 < ids.div <= PRIME // range_check_builtin.bound, \ @@ -124,7 +123,7 @@ assert -ids.bound <= q < ids.bound, \ ids.biased_q = q + ids.bound"#; -pub(crate) const IS_QUAD_RESIDUE: &str = r#"from starkware.crypto.signature.signature import FIELD_PRIME +pub const IS_QUAD_RESIDUE: &str = r#"from starkware.crypto.signature.signature import FIELD_PRIME from starkware.python.math_utils import div_mod, is_quad_residue, sqrt x = ids.x @@ -133,7 +132,7 @@ if is_quad_residue(x, FIELD_PRIME): else: ids.y = sqrt(div_mod(x, 3, FIELD_PRIME), FIELD_PRIME)"#; -pub(crate) const FIND_ELEMENT: &str = r#"array_ptr = ids.array_ptr +pub const FIND_ELEMENT: &str = r#"array_ptr = ids.array_ptr elm_size = ids.elm_size assert isinstance(elm_size, int) and elm_size > 0, \ f'Invalid value for elm_size. Got: {elm_size}.' @@ -163,7 +162,7 @@ else: else: raise ValueError(f'Key {key} was not found.')"#; -pub(crate) const SEARCH_SORTED_LOWER: &str = r#"array_ptr = ids.array_ptr +pub const SEARCH_SORTED_LOWER: &str = r#"array_ptr = ids.array_ptr elm_size = ids.elm_size assert isinstance(elm_size, int) and elm_size > 0, \ f'Invalid value for elm_size. Got: {elm_size}.' @@ -183,7 +182,7 @@ for i in range(n_elms): else: ids.index = n_elms"#; -pub(crate) const SET_ADD: &str = r#"assert ids.elm_size > 0 +pub const SET_ADD: &str = r#"assert ids.elm_size > 0 assert ids.set_ptr <= ids.set_end_ptr elm_list = memory.get_range(ids.elm_ptr, ids.elm_size) for i in range(0, ids.set_end_ptr - ids.set_ptr, ids.elm_size): @@ -194,29 +193,29 @@ for i in range(0, ids.set_end_ptr - ids.set_ptr, ids.elm_size): else: ids.is_elm_in_set = 0"#; -pub(crate) const DEFAULT_DICT_NEW: &str = r#"if '__dict_manager' not in globals(): +pub const DEFAULT_DICT_NEW: &str = r#"if '__dict_manager' not in globals(): from starkware.cairo.common.dict import DictManager __dict_manager = DictManager() memory[ap] = __dict_manager.new_default_dict(segments, ids.default_value)"#; -pub(crate) const DICT_NEW: &str = r#"if '__dict_manager' not in globals(): +pub const DICT_NEW: &str = r#"if '__dict_manager' not in globals(): from starkware.cairo.common.dict import DictManager __dict_manager = DictManager() memory[ap] = __dict_manager.new_dict(segments, initial_dict) del initial_dict"#; -pub(crate) const DICT_READ: &str = r#"dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) +pub const DICT_READ: &str = r#"dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) dict_tracker.current_ptr += ids.DictAccess.SIZE ids.value = dict_tracker.data[ids.key]"#; -pub(crate) const DICT_WRITE: &str = r#"dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) +pub const DICT_WRITE: &str = r#"dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) dict_tracker.current_ptr += ids.DictAccess.SIZE ids.dict_ptr.prev_value = dict_tracker.data[ids.key] dict_tracker.data[ids.key] = ids.new_value"#; -pub(crate) const DICT_UPDATE: &str = r#"# Verify dict pointer and prev value. +pub const DICT_UPDATE: &str = r#"# Verify dict pointer and prev value. dict_tracker = __dict_manager.get_tracker(ids.dict_ptr) current_value = dict_tracker.data[ids.key] assert current_value == ids.prev_value, \ @@ -226,7 +225,7 @@ assert current_value == ids.prev_value, \ dict_tracker.data[ids.key] = ids.new_value dict_tracker.current_ptr += ids.DictAccess.SIZE"#; -pub(crate) const SQUASH_DICT: &str = r#"dict_access_size = ids.DictAccess.SIZE +pub const SQUASH_DICT: &str = r#"dict_access_size = ids.DictAccess.SIZE address = ids.dict_accesses.address_ assert ids.ptr_diff % dict_access_size == 0, \ 'Accesses array size must be divisible by DictAccess.SIZE' @@ -246,26 +245,26 @@ keys = sorted(access_indices.keys(), reverse=True) ids.big_keys = 1 if keys[0] >= range_check_builtin.bound else 0 ids.first_key = key = keys.pop()"#; -pub(crate) const SQUASH_DICT_INNER_SKIP_LOOP: &str = +pub const SQUASH_DICT_INNER_SKIP_LOOP: &str = "ids.should_skip_loop = 0 if current_access_indices else 1"; -pub(crate) const SQUASH_DICT_INNER_FIRST_ITERATION: &str = r#"current_access_indices = sorted(access_indices[key])[::-1] +pub const SQUASH_DICT_INNER_FIRST_ITERATION: &str = r#"current_access_indices = sorted(access_indices[key])[::-1] current_access_index = current_access_indices.pop() memory[ids.range_check_ptr] = current_access_index"#; -pub(crate) const SQUASH_DICT_INNER_CHECK_ACCESS_INDEX: &str = r#"new_access_index = current_access_indices.pop() +pub const SQUASH_DICT_INNER_CHECK_ACCESS_INDEX: &str = r#"new_access_index = current_access_indices.pop() ids.loop_temps.index_delta_minus1 = new_access_index - current_access_index - 1 current_access_index = new_access_index"#; -pub(crate) const SQUASH_DICT_INNER_CONTINUE_LOOP: &str = +pub const SQUASH_DICT_INNER_CONTINUE_LOOP: &str = "ids.loop_temps.should_continue = 1 if current_access_indices else 0"; -pub(crate) const SQUASH_DICT_INNER_ASSERT_LEN_KEYS: &str = "assert len(keys) == 0"; -pub(crate) const SQUASH_DICT_INNER_LEN_ASSERT: &str = "assert len(current_access_indices) == 0"; -pub(crate) const SQUASH_DICT_INNER_USED_ACCESSES_ASSERT: &str = +pub const SQUASH_DICT_INNER_ASSERT_LEN_KEYS: &str = "assert len(keys) == 0"; +pub const SQUASH_DICT_INNER_LEN_ASSERT: &str = "assert len(current_access_indices) == 0"; +pub const SQUASH_DICT_INNER_USED_ACCESSES_ASSERT: &str = "assert ids.n_used_accesses == len(access_indices[key])"; -pub(crate) const SQUASH_DICT_INNER_NEXT_KEY: &str = r#"assert len(keys) > 0, 'No keys left but remaining_accesses > 0.' +pub const SQUASH_DICT_INNER_NEXT_KEY: &str = r#"assert len(keys) > 0, 'No keys left but remaining_accesses > 0.' ids.next_key = key = keys.pop()"#; -pub(crate) const DICT_SQUASH_COPY_DICT: &str = r#"# Prepare arguments for dict_new. In particular, the same dictionary values should be copied +pub const DICT_SQUASH_COPY_DICT: &str = r#"# Prepare arguments for dict_new. In particular, the same dictionary values should be copied # to the new (squashed) dictionary. vm_enter_scope({ # Make __dict_manager accessible. @@ -274,28 +273,26 @@ vm_enter_scope({ 'initial_dict': dict(__dict_manager.get_dict(ids.dict_accesses_end)), })"#; -pub(crate) const DICT_SQUASH_UPDATE_PTR: &str = r#"# Update the DictTracker's current_ptr to point to the end of the squashed dict. +pub const DICT_SQUASH_UPDATE_PTR: &str = r#"# Update the DictTracker's current_ptr to point to the end of the squashed dict. __dict_manager.get_tracker(ids.squashed_dict_start).current_ptr = \ ids.squashed_dict_end.address_"#; -pub(crate) const BIGINT_TO_UINT256: &str = - "ids.low = (ids.x.d0 + ids.x.d1 * ids.BASE) & ((1 << 128) - 1)"; -pub(crate) const UINT256_ADD: &str = r#"sum_low = ids.a.low + ids.b.low +pub const BIGINT_TO_UINT256: &str = "ids.low = (ids.x.d0 + ids.x.d1 * ids.BASE) & ((1 << 128) - 1)"; +pub const UINT256_ADD: &str = r#"sum_low = ids.a.low + ids.b.low ids.carry_low = 1 if sum_low >= ids.SHIFT else 0 sum_high = ids.a.high + ids.b.high + ids.carry_low ids.carry_high = 1 if sum_high >= ids.SHIFT else 0"#; -pub(crate) const UINT256_SQRT: &str = r#"from starkware.python.math_utils import isqrt +pub const UINT256_SQRT: &str = r#"from starkware.python.math_utils import isqrt n = (ids.n.high << 128) + ids.n.low root = isqrt(n) assert 0 <= root < 2 ** 128 ids.root.low = root ids.root.high = 0"#; -pub(crate) const UINT256_SIGNED_NN: &str = - "memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0"; +pub const UINT256_SIGNED_NN: &str = "memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0"; -pub(crate) const UINT256_UNSIGNED_DIV_REM: &str = r#"a = (ids.a.high << 128) + ids.a.low +pub const UINT256_UNSIGNED_DIV_REM: &str = r#"a = (ids.a.high << 128) + ids.a.low div = (ids.div.high << 128) + ids.div.low quotient, remainder = divmod(a, div) @@ -304,7 +301,7 @@ ids.quotient.high = quotient >> 128 ids.remainder.low = remainder & ((1 << 128) - 1) ids.remainder.high = remainder >> 128"#; -pub(crate) const UINT256_MUL_DIV_MOD: &str = r#"a = (ids.a.high << 128) + ids.a.low +pub const UINT256_MUL_DIV_MOD: &str = r#"a = (ids.a.high << 128) + ids.a.low b = (ids.b.high << 128) + ids.b.low div = (ids.div.high << 128) + ids.div.low quotient, remainder = divmod(a * b, div) @@ -316,9 +313,9 @@ ids.quotient_high.high = quotient >> 384 ids.remainder.low = remainder & ((1 << 128) - 1) ids.remainder.high = remainder >> 128"#; -pub(crate) const USORT_ENTER_SCOPE: &str = +pub const USORT_ENTER_SCOPE: &str = "vm_enter_scope(dict(__usort_max_size = globals().get('__usort_max_size')))"; -pub(crate) const USORT_BODY: &str = r#"from collections import defaultdict +pub const USORT_BODY: &str = r#"from collections import defaultdict input_ptr = ids.input input_len = int(ids.input_len) @@ -338,18 +335,18 @@ ids.output_len = len(output) ids.output = segments.gen_arg(output) ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])"#; -pub(crate) const USORT_VERIFY: &str = r#"last_pos = 0 +pub const USORT_VERIFY: &str = r#"last_pos = 0 positions = positions_dict[ids.value][::-1]"#; -pub(crate) const USORT_VERIFY_MULTIPLICITY_ASSERT: &str = "assert len(positions) == 0"; -pub(crate) const USORT_VERIFY_MULTIPLICITY_BODY: &str = r#"current_pos = positions.pop() +pub const USORT_VERIFY_MULTIPLICITY_ASSERT: &str = "assert len(positions) == 0"; +pub const USORT_VERIFY_MULTIPLICITY_BODY: &str = r#"current_pos = positions.pop() ids.next_item_index = current_pos - last_pos last_pos = current_pos + 1"#; -pub(crate) const BLAKE2S_COMPUTE: &str = r#"from starkware.cairo.common.cairo_blake2s.blake2s_utils import compute_blake2s_func +pub const BLAKE2S_COMPUTE: &str = r#"from starkware.cairo.common.cairo_blake2s.blake2s_utils import compute_blake2s_func compute_blake2s_func(segments=segments, output_ptr=ids.output)"#; -pub(crate) const BLAKE2S_FINALIZE: &str = r#"# Add dummy pairs of input and output. +pub const BLAKE2S_FINALIZE: &str = r#"# Add dummy pairs of input and output. from starkware.cairo.common.cairo_blake2s.blake2s_utils import IV, blake2s_compress _n_packed_instances = int(ids.N_PACKED_INSTANCES) @@ -370,36 +367,36 @@ output = blake2s_compress( padding = (modified_iv + message + [0, 0xffffffff] + output) * (_n_packed_instances - 1) segments.write_arg(ids.blake2s_ptr_end, padding)"#; -pub(crate) const BLAKE2S_ADD_UINT256: &str = r#"B = 32 +pub const BLAKE2S_ADD_UINT256: &str = r#"B = 32 MASK = 2 ** 32 - 1 segments.write_arg(ids.data, [(ids.low >> (B * i)) & MASK for i in range(4)]) segments.write_arg(ids.data + 4, [(ids.high >> (B * i)) & MASK for i in range(4)]"#; -pub(crate) const BLAKE2S_ADD_UINT256_BIGEND: &str = r#"B = 32 +pub const BLAKE2S_ADD_UINT256_BIGEND: &str = r#"B = 32 MASK = 2 ** 32 - 1 segments.write_arg(ids.data, [(ids.high >> (B * (3 - i))) & MASK for i in range(4)]) segments.write_arg(ids.data + 4, [(ids.low >> (B * (3 - i))) & MASK for i in range(4)])"#; -pub(crate) const NONDET_BIGINT3: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import split +pub const NONDET_BIGINT3: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import split segments.write_arg(ids.res.address_, split(value))"#; -pub(crate) const VERIFY_ZERO_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const VERIFY_ZERO_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack q, r = divmod(pack(ids.val, PRIME), SECP_P) assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." ids.q = q % PRIME"#; -pub(crate) const VERIFY_ZERO_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P +pub const VERIFY_ZERO_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P q, r = divmod(pack(ids.val, PRIME), SECP_P) assert r == 0, f"verify_zero: Invalid input {ids.val.d0, ids.val.d1, ids.val.d2}." ids.q = q % PRIME"#; -pub(crate) const REDUCE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const REDUCE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack value = pack(ids.x, PRIME) % SECP_P"#; -pub(crate) const UNSAFE_KECCAK: &str = r#"from eth_hash.auto import keccak +pub const UNSAFE_KECCAK: &str = r#"from eth_hash.auto import keccak data, length = ids.data, ids.length @@ -419,7 +416,7 @@ hashed = keccak(keccak_input) ids.high = int.from_bytes(hashed[:16], 'big') ids.low = int.from_bytes(hashed[16:32], 'big')"#; -pub(crate) const UNSAFE_KECCAK_FINALIZE: &str = r#"from eth_hash.auto import keccak +pub const UNSAFE_KECCAK_FINALIZE: &str = r#"from eth_hash.auto import keccak keccak_input = bytearray() n_elms = ids.keccak_state.end_ptr - ids.keccak_state.start_ptr for word in memory.get_range(ids.keccak_state.start_ptr, n_elms): @@ -428,25 +425,25 @@ hashed = keccak(keccak_input) ids.high = int.from_bytes(hashed[:16], 'big') ids.low = int.from_bytes(hashed[16:32], 'big')"#; -pub(crate) const IS_ZERO_NONDET: &str = "memory[ap] = to_felt_or_relocatable(x == 0)"; -pub(crate) const IS_ZERO_PACK: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const IS_ZERO_NONDET: &str = "memory[ap] = to_felt_or_relocatable(x == 0)"; +pub const IS_ZERO_PACK: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack x = pack(ids.x, PRIME) % SECP_P"#; -pub(crate) const IS_ZERO_ASSIGN_SCOPE_VARS: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P +pub const IS_ZERO_ASSIGN_SCOPE_VARS: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P from starkware.python.math_utils import div_mod value = x_inv = div_mod(1, x, SECP_P)"#; -pub(crate) const DIV_MOD_N_PACKED_DIVMOD: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import N, pack +pub const DIV_MOD_N_PACKED_DIVMOD: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import N, pack from starkware.python.math_utils import div_mod, safe_div a = pack(ids.a, PRIME) b = pack(ids.b, PRIME) value = res = div_mod(a, b, N)"#; -pub(crate) const DIV_MOD_N_SAFE_DIV: &str = r#"value = k = safe_div(res * b - a, N)"#; +pub const DIV_MOD_N_SAFE_DIV: &str = r#"value = k = safe_div(res * b - a, N)"#; -pub(crate) const GET_POINT_FROM_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const GET_POINT_FROM_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack x_cube_int = pack(ids.x_cube, PRIME) % SECP_P y_square_int = (x_cube_int + ids.BETA) % SECP_P @@ -458,13 +455,13 @@ if ids.v % 2 == y % 2: else: value = (-y) % SECP_P"#; -pub(crate) const EC_NEGATE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const EC_NEGATE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack y = pack(ids.point.y, PRIME) % SECP_P # The modulo operation in python always returns a nonnegative number. value = (-y) % SECP_P"#; -pub(crate) const EC_DOUBLE_SCOPE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const EC_DOUBLE_SCOPE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack from starkware.python.math_utils import ec_double_slope # Compute the slope. @@ -472,7 +469,7 @@ x = pack(ids.point.x, PRIME) y = pack(ids.point.y, PRIME) value = slope = ec_double_slope(point=(x, y), alpha=0, p=SECP_P)"#; -pub(crate) const EC_DOUBLE_SCOPE_WHITELIST: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const EC_DOUBLE_SCOPE_WHITELIST: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack from starkware.python.math_utils import div_mod # Compute the slope. @@ -480,7 +477,7 @@ x = pack(ids.pt.x, PRIME) y = pack(ids.pt.y, PRIME) value = slope = div_mod(3 * x ** 2, 2 * y, SECP_P)"#; -pub(crate) const COMPUTE_SLOPE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const COMPUTE_SLOPE: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack from starkware.python.math_utils import line_slope # Compute the slope. @@ -490,7 +487,7 @@ x1 = pack(ids.point1.x, PRIME) y1 = pack(ids.point1.y, PRIME) value = slope = line_slope(point1=(x0, y0), point2=(x1, y1), p=SECP_P)"#; -pub(crate) const COMPUTE_SLOPE_WHITELIST: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const COMPUTE_SLOPE_WHITELIST: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack from starkware.python.math_utils import div_mod # Compute the slope. @@ -500,7 +497,7 @@ x1 = pack(ids.pt1.x, PRIME) y1 = pack(ids.pt1.y, PRIME) value = slope = div_mod(y0 - y1, x0 - x1, SECP_P)"#; -pub(crate) const EC_DOUBLE_ASSIGN_NEW_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const EC_DOUBLE_ASSIGN_NEW_X_V1: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack slope = pack(ids.slope, PRIME) x = pack(ids.point.x, PRIME) @@ -508,12 +505,19 @@ y = pack(ids.point.y, PRIME) value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"#; -pub(crate) const EC_DOUBLE_ASSIGN_NEW_Y: &str = - r#"value = new_y = (slope * (x - new_x) - y) % SECP_P"#; +pub const EC_DOUBLE_ASSIGN_NEW_X_V2: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import pack -pub(crate) const SHA256_INPUT: &str = r#"ids.full_word = int(ids.n_bytes >= 4)"#; +slope = pack(ids.slope, PRIME) +x = pack(ids.point.x, PRIME) +y = pack(ids.point.y, PRIME) + +value = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"#; + +pub const EC_DOUBLE_ASSIGN_NEW_Y: &str = r#"value = new_y = (slope * (x - new_x) - y) % SECP_P"#; -pub(crate) const SHA256_MAIN: &str = r#"from starkware.cairo.common.cairo_sha256.sha256_utils import ( +pub const SHA256_INPUT: &str = r#"ids.full_word = int(ids.n_bytes >= 4)"#; + +pub const SHA256_MAIN: &str = r#"from starkware.cairo.common.cairo_sha256.sha256_utils import ( IV, compute_message_schedule, sha2_compress_function) _sha256_input_chunk_size_felts = int(ids.SHA256_INPUT_CHUNK_SIZE_FELTS) @@ -524,7 +528,7 @@ w = compute_message_schedule(memory.get_range( new_state = sha2_compress_function(IV, w) segments.write_arg(ids.output, new_state)"#; -pub(crate) const SHA256_FINALIZE: &str = r#"# Add dummy pairs of input and output. +pub const SHA256_FINALIZE: &str = r#"# Add dummy pairs of input and output. from starkware.cairo.common.cairo_sha256.sha256_utils import ( IV, compute_message_schedule, sha2_compress_function) @@ -539,16 +543,16 @@ output = sha2_compress_function(IV, w) padding = (message + IV + output) * (_block_size - 1) segments.write_arg(ids.sha256_ptr_end, padding)"#; -pub(crate) const KECCAK_WRITE_ARGS: &str = r#"segments.write_arg(ids.inputs, [ids.low % 2 ** 64, ids.low // 2 ** 64]) +pub const KECCAK_WRITE_ARGS: &str = r#"segments.write_arg(ids.inputs, [ids.low % 2 ** 64, ids.low // 2 ** 64]) segments.write_arg(ids.inputs + 2, [ids.high % 2 ** 64, ids.high // 2 ** 64])"#; -pub(crate) const COMPARE_BYTES_IN_WORD_NONDET: &str = +pub const COMPARE_BYTES_IN_WORD_NONDET: &str = r#"memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)"#; -pub(crate) const COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET: &str = +pub const COMPARE_KECCAK_FULL_RATE_IN_BYTES_NONDET: &str = r#"memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)"#; -pub(crate) const BLOCK_PERMUTATION: &str = r#"from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func +pub const BLOCK_PERMUTATION: &str = r#"from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) assert 0 <= _keccak_state_size_felts < 100 @@ -558,7 +562,7 @@ segments.write_arg(ids.keccak_ptr, output_values)"#; // The 0.10.3 whitelist uses this variant (instead of the one used by the common library), but both hints have the same behaviour // We should check for future refactors that may discard one of the variants -pub(crate) const BLOCK_PERMUTATION_WHITELIST: &str = r#"from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func +pub const BLOCK_PERMUTATION_WHITELIST: &str = r#"from starkware.cairo.common.cairo_keccak.keccak_utils import keccak_func _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) assert 0 <= _keccak_state_size_felts < 100 @@ -566,7 +570,7 @@ output_values = keccak_func(memory.get_range( ids.keccak_ptr - _keccak_state_size_felts, _keccak_state_size_felts)) segments.write_arg(ids.keccak_ptr, output_values)"#; -pub(crate) const CAIRO_KECCAK_FINALIZE: &str = r#"# Add dummy pairs of input and output. +pub const CAIRO_KECCAK_FINALIZE: &str = r#"# Add dummy pairs of input and output. _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) _block_size = int(ids.BLOCK_SIZE) assert 0 <= _keccak_state_size_felts < 100 @@ -575,7 +579,7 @@ inp = [0] * _keccak_state_size_felts padding = (inp + keccak_func(inp)) * _block_size segments.write_arg(ids.keccak_ptr_end, padding)"#; -pub(crate) const FAST_EC_ADD_ASSIGN_NEW_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack +pub const FAST_EC_ADD_ASSIGN_NEW_X: &str = r#"from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack slope = pack(ids.slope, PRIME) x0 = pack(ids.point0.x, PRIME) @@ -584,43 +588,39 @@ y0 = pack(ids.point0.y, PRIME) value = new_x = (pow(slope, 2, SECP_P) - x0 - x1) % SECP_P"#; -pub(crate) const FAST_EC_ADD_ASSIGN_NEW_Y: &str = +pub const FAST_EC_ADD_ASSIGN_NEW_Y: &str = r#"value = new_y = (slope * (x0 - new_x) - y0) % SECP_P"#; -pub(crate) const EC_MUL_INNER: &str = r#"memory[ap] = (ids.scalar % PRIME) % 2"#; +pub const EC_MUL_INNER: &str = r#"memory[ap] = (ids.scalar % PRIME) % 2"#; -pub(crate) const RELOCATE_SEGMENT: &str = +pub const RELOCATE_SEGMENT: &str = r#"memory.add_relocation_rule(src_ptr=ids.src_ptr, dest_ptr=ids.dest_ptr)"#; -pub(crate) const TEMPORARY_ARRAY: &str = r#"ids.temporary_array = segments.add_temp_segment()"#; -pub(crate) const VERIFY_ECDSA_SIGNATURE: &str = +pub const TEMPORARY_ARRAY: &str = r#"ids.temporary_array = segments.add_temp_segment()"#; +pub const VERIFY_ECDSA_SIGNATURE: &str = r#"ecdsa_builtin.add_signature(ids.ecdsa_ptr.address_, (ids.signature_r, ids.signature_s))"#; -pub(crate) const SPLIT_OUTPUT_0: &str = "ids.output0_low = ids.output0 & ((1 << 128) - 1) +pub const SPLIT_OUTPUT_0: &str = "ids.output0_low = ids.output0 & ((1 << 128) - 1) ids.output0_high = ids.output0 >> 128"; -pub(crate) const SPLIT_OUTPUT_1: &str = "ids.output1_low = ids.output1 & ((1 << 128) - 1) +pub const SPLIT_OUTPUT_1: &str = "ids.output1_low = ids.output1 & ((1 << 128) - 1) ids.output1_high = ids.output1 >> 128"; -pub(crate) const SPLIT_INPUT_3: &str = "ids.high3, ids.low3 = divmod(memory[ids.inputs + 3], 256)"; -pub(crate) const SPLIT_INPUT_6: &str = - "ids.high6, ids.low6 = divmod(memory[ids.inputs + 6], 256 ** 2)"; -pub(crate) const SPLIT_INPUT_9: &str = - "ids.high9, ids.low9 = divmod(memory[ids.inputs + 9], 256 ** 3)"; -pub(crate) const SPLIT_INPUT_12: &str = +pub const SPLIT_INPUT_3: &str = "ids.high3, ids.low3 = divmod(memory[ids.inputs + 3], 256)"; +pub const SPLIT_INPUT_6: &str = "ids.high6, ids.low6 = divmod(memory[ids.inputs + 6], 256 ** 2)"; +pub const SPLIT_INPUT_9: &str = "ids.high9, ids.low9 = divmod(memory[ids.inputs + 9], 256 ** 3)"; +pub const SPLIT_INPUT_12: &str = "ids.high12, ids.low12 = divmod(memory[ids.inputs + 12], 256 ** 4)"; -pub(crate) const SPLIT_INPUT_15: &str = +pub const SPLIT_INPUT_15: &str = "ids.high15, ids.low15 = divmod(memory[ids.inputs + 15], 256 ** 5)"; -pub(crate) const SPLIT_N_BYTES: &str = +pub const SPLIT_N_BYTES: &str = "ids.n_words_to_copy, ids.n_bytes_left = divmod(ids.n_bytes, ids.BYTES_IN_WORD)"; -pub(crate) const SPLIT_OUTPUT_MID_LOW_HIGH: &str = - "tmp, ids.output1_low = divmod(ids.output1, 256 ** 7) +pub const SPLIT_OUTPUT_MID_LOW_HIGH: &str = "tmp, ids.output1_low = divmod(ids.output1, 256 ** 7) ids.output1_high, ids.output1_mid = divmod(tmp, 2 ** 128)"; -pub(crate) const NONDET_N_GREATER_THAN_10: &str = - "memory[ap] = to_felt_or_relocatable(ids.n >= 10)"; -pub(crate) const NONDET_N_GREATER_THAN_2: &str = "memory[ap] = to_felt_or_relocatable(ids.n >= 2)"; -pub(crate) const RANDOM_EC_POINT: &str = r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME +pub const NONDET_N_GREATER_THAN_10: &str = "memory[ap] = to_felt_or_relocatable(ids.n >= 10)"; +pub const NONDET_N_GREATER_THAN_2: &str = "memory[ap] = to_felt_or_relocatable(ids.n >= 2)"; +pub const RANDOM_EC_POINT: &str = r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME from starkware.python.math_utils import random_ec_point from starkware.python.utils import to_bytes @@ -629,7 +629,7 @@ from starkware.python.utils import to_bytes # (2) It's hard to choose inputs for which the builtin will fail. seed = b"".join(map(to_bytes, [ids.p.x, ids.p.y, ids.m, ids.q.x, ids.q.y])) ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#; -pub(crate) const CHAINED_EC_OP_RANDOM_EC_POINT: &str = r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME +pub const CHAINED_EC_OP_RANDOM_EC_POINT: &str = r#"from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME from starkware.python.math_utils import random_ec_point from starkware.python.utils import to_bytes @@ -656,7 +656,7 @@ seed = b"".join( ) ) ids.s.x, ids.s.y = random_ec_point(FIELD_PRIME, ALPHA, BETA, seed)"#; -pub(crate) const RECOVER_Y: &str = +pub const RECOVER_Y: &str = "from starkware.crypto.signature.signature import ALPHA, BETA, FIELD_PRIME from starkware.python.math_utils import recover_y ids.p.x = ids.x @@ -664,8 +664,7 @@ ids.p.x = ids.x ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME)"; // The following hints support the lib https://github.com/NethermindEth/research-basic-Cairo-operations-big-integers/blob/main/lib/uint384.cairo -pub(crate) const UINT384_UNSIGNED_DIV_REM: &str = - "def split(num: int, num_bits_shift: int, length: int): +pub const UINT384_UNSIGNED_DIV_REM: &str = "def split(num: int, num_bits_shift: int, length: int): a = [] for _ in range(length): a.append( num & ((1 << num_bits_shift) - 1) ) @@ -691,15 +690,15 @@ remainder_split = split(remainder, num_bits_shift=128, length=3) ids.remainder.d0 = remainder_split[0] ids.remainder.d1 = remainder_split[1] ids.remainder.d2 = remainder_split[2]"; -pub(crate) const UINT384_SPLIT_128: &str = "ids.low = ids.a & ((1<<128) - 1) +pub const UINT384_SPLIT_128: &str = "ids.low = ids.a & ((1<<128) - 1) ids.high = ids.a >> 128"; -pub(crate) const ADD_NO_UINT384_CHECK: &str = "sum_d0 = ids.a.d0 + ids.b.d0 +pub const ADD_NO_UINT384_CHECK: &str = "sum_d0 = ids.a.d0 + ids.b.d0 ids.carry_d0 = 1 if sum_d0 >= ids.SHIFT else 0 sum_d1 = ids.a.d1 + ids.b.d1 + ids.carry_d0 ids.carry_d1 = 1 if sum_d1 >= ids.SHIFT else 0 sum_d2 = ids.a.d2 + ids.b.d2 + ids.carry_d1 ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0"; -pub(crate) const UINT384_UNSIGNED_DIV_REM_EXPANDED: &str = +pub const UINT384_UNSIGNED_DIV_REM_EXPANDED: &str = "def split(num: int, num_bits_shift: int, length: int): a = [] for _ in range(length): @@ -730,7 +729,7 @@ remainder_split = split(remainder, num_bits_shift=128, length=3) ids.remainder.d0 = remainder_split[0] ids.remainder.d1 = remainder_split[1] ids.remainder.d2 = remainder_split[2]"; -pub(crate) const UINT384_SQRT: &str = "from starkware.python.math_utils import isqrt +pub const UINT384_SQRT: &str = "from starkware.python.math_utils import isqrt def split(num: int, num_bits_shift: int, length: int): a = [] @@ -750,8 +749,7 @@ root_split = split(root, num_bits_shift=128, length=3) ids.root.d0 = root_split[0] ids.root.d1 = root_split[1] ids.root.d2 = root_split[2]"; -pub(crate) const UINT384_SIGNED_NN: &str = - "memory[ap] = 1 if 0 <= (ids.a.d2 % PRIME) < 2 ** 127 else 0"; +pub const UINT384_SIGNED_NN: &str = "memory[ap] = 1 if 0 <= (ids.a.d2 % PRIME) < 2 ** 127 else 0"; #[cfg(feature = "skip_next_instruction_hint")] -pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; +pub const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; diff --git a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs index 9c7dfe74f8..049c721d54 100644 --- a/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs +++ b/src/hint_processor/builtin_hint_processor/secp/ec_utils.rs @@ -504,64 +504,67 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn run_ec_double_assign_new_x_ok() { - let hint_code = "from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack\n\nslope = pack(ids.slope, PRIME)\nx = pack(ids.point.x, PRIME)\ny = pack(ids.point.y, PRIME)\n\nvalue = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"; - let mut vm = vm_with_range_check!(); - - //Insert ids.point and ids.slope into memory - vm.segments = segments![ - ((1, 0), 134), - ((1, 1), 5123), - ((1, 2), 140), - ((1, 3), 1232), - ((1, 4), 4652), - ((1, 5), 720), - ((1, 6), 44186171158942157784255469_i128), - ((1, 7), 54173758974262696047492534_i128), - ((1, 8), 8106299688661572814170174_i128) - ]; - - //Initialize fp - vm.run_context.fp = 10; - let ids_data = HashMap::from([ - ("point".to_string(), HintReference::new_simple(-10)), - ("slope".to_string(), HintReference::new_simple(-4)), - ]); - let mut exec_scopes = ExecutionScopes::new(); - - //Execute the hint - assert_matches!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(())); - - check_scope!( - &exec_scopes, - [ - ( - "slope", - bigint_str!( - "48526828616392201132917323266456307435009781900148206102108934970258721901549" - ) - ), - ( - "x", - bigint_str!("838083498911032969414721426845751663479194726707495046") - ), - ( - "y", - bigint_str!("4310143708685312414132851373791311001152018708061750480") - ), - ( - "value", - bigint_str!( - "59479631769792988345961122678598249997181612138456851058217178025444564264149" - ) - ), - ( - "new_x", - bigint_str!( - "59479631769792988345961122678598249997181612138456851058217178025444564264149" - ) - ) - ] - ); + let hint_codes = vec!["from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack\n\nslope = pack(ids.slope, PRIME)\nx = pack(ids.point.x, PRIME)\ny = pack(ids.point.y, PRIME)\n\nvalue = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P", "from starkware.cairo.common.cairo_secp.secp_utils import pack\n\nslope = pack(ids.slope, PRIME)\nx = pack(ids.point.x, PRIME)\ny = pack(ids.point.y, PRIME)\n\nvalue = new_x = (pow(slope, 2, SECP_P) - 2 * x) % SECP_P"]; + + for hint_code in hint_codes { + let mut vm = vm_with_range_check!(); + + //Insert ids.point and ids.slope into memory + vm.segments = segments![ + ((1, 0), 134), + ((1, 1), 5123), + ((1, 2), 140), + ((1, 3), 1232), + ((1, 4), 4652), + ((1, 5), 720), + ((1, 6), 44186171158942157784255469_i128), + ((1, 7), 54173758974262696047492534_i128), + ((1, 8), 8106299688661572814170174_i128) + ]; + + //Initialize fp + vm.run_context.fp = 10; + let ids_data = HashMap::from([ + ("point".to_string(), HintReference::new_simple(-10)), + ("slope".to_string(), HintReference::new_simple(-4)), + ]); + let mut exec_scopes = ExecutionScopes::new(); + + //Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code, &mut exec_scopes), Ok(())); + + check_scope!( + &exec_scopes, + [ + ( + "slope", + bigint_str!( + "48526828616392201132917323266456307435009781900148206102108934970258721901549" + ) + ), + ( + "x", + bigint_str!("838083498911032969414721426845751663479194726707495046") + ), + ( + "y", + bigint_str!("4310143708685312414132851373791311001152018708061750480") + ), + ( + "value", + bigint_str!( + "59479631769792988345961122678598249997181612138456851058217178025444564264149" + ) + ), + ( + "new_x", + bigint_str!( + "59479631769792988345961122678598249997181612138456851058217178025444564264149" + ) + ) + ] + ); + } } #[test] diff --git a/src/lib.rs b/src/lib.rs index 02b9c850f6..c6387994c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,6 +48,7 @@ mod stdlib { pub use crate::without_std::*; } +pub extern crate felt; pub mod cairo_run; pub mod hint_processor; pub mod math_utils; diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index 54ddd004b7..ef2da718e2 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -1308,3 +1308,10 @@ fn cairo_run_ed25519_ec() { let program_data = include_bytes!("../../cairo_programs/ed25519_ec.json"); run_program_simple(program_data.as_slice()); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn cairo_run_efficient_secp256r1_ec() { + let program_data = include_bytes!("../../cairo_programs/efficient_secp256r1_ec.json"); + run_program_simple(program_data.as_slice()); +} diff --git a/src/vm/errors/vm_exception.rs b/src/vm/errors/vm_exception.rs index d1fc508c2b..1949d19460 100644 --- a/src/vm/errors/vm_exception.rs +++ b/src/vm/errors/vm_exception.rs @@ -106,7 +106,10 @@ pub fn get_traceback(vm: &VirtualMachine, runner: &CairoRunner) -> Option traceback.push_str(&format!("Unknown location (pc=0:{})", traceback_pc.offset)), + None => traceback.push_str(&format!( + "Unknown location (pc=0:{})\n", + traceback_pc.offset + )), } } (!traceback.is_empty())