From 65ab4faf5b4d7b7123d4ca8a87673310c949f49b Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Fri, 11 Apr 2025 18:44:06 -0300 Subject: [PATCH 01/64] Add sierra-emu as a debug utility --- .github/workflows/ci.yml | 15 + Cargo.lock | 209 +++ Cargo.toml | 3 + debug_utils/sierra-emu/.gitignore | 24 + debug_utils/sierra-emu/Cargo.toml | 46 + debug_utils/sierra-emu/Makefile | 75 + debug_utils/sierra-emu/README.md | 102 ++ .../sierra-emu/programs/hello_starknet.cairo | 18 + .../sierra-emu/programs/syscalls.cairo | 118 ++ .../scripts/check-corelib-version.sh | 10 + debug_utils/sierra-emu/src/args.rs | 32 + debug_utils/sierra-emu/src/debug.rs | 582 ++++++++ debug_utils/sierra-emu/src/dump.rs | 127 ++ debug_utils/sierra-emu/src/gas.rs | 242 ++++ debug_utils/sierra-emu/src/lib.rs | 60 + debug_utils/sierra-emu/src/main.rs | 206 +++ debug_utils/sierra-emu/src/starknet.rs | 851 +++++++++++ .../sierra-emu/src/starknet/block_info.rs | 20 + .../sierra-emu/src/starknet/execution_info.rs | 25 + .../src/starknet/execution_info_v2.rs | 30 + .../src/starknet/resource_bounds.rs | 20 + .../src/starknet/secp256k1_point.rs | 27 + .../src/starknet/secp256r1_point.rs | 27 + .../sierra-emu/src/starknet/tx_info.rs | 31 + .../sierra-emu/src/starknet/tx_v2_info.rs | 66 + debug_utils/sierra-emu/src/starknet/u256.rs | 22 + debug_utils/sierra-emu/src/test_utils.rs | 84 ++ debug_utils/sierra-emu/src/value.rs | 241 ++++ debug_utils/sierra-emu/src/vm.rs | 520 +++++++ debug_utils/sierra-emu/src/vm/ap_tracking.rs | 53 + debug_utils/sierra-emu/src/vm/array.rs | 240 ++++ debug_utils/sierra-emu/src/vm/bool.rs | 159 +++ debug_utils/sierra-emu/src/vm/bounded_int.rs | 398 ++++++ debug_utils/sierra-emu/src/vm/box.rs | 53 + debug_utils/sierra-emu/src/vm/branch_align.rs | 20 + debug_utils/sierra-emu/src/vm/bytes31.rs | 65 + debug_utils/sierra-emu/src/vm/cast.rs | 107 ++ debug_utils/sierra-emu/src/vm/circuit.rs | 352 +++++ debug_utils/sierra-emu/src/vm/const.rs | 165 +++ debug_utils/sierra-emu/src/vm/drop.rs | 20 + debug_utils/sierra-emu/src/vm/dup.rs | 20 + debug_utils/sierra-emu/src/vm/ec.rs | 260 ++++ debug_utils/sierra-emu/src/vm/enum.rs | 127 ++ debug_utils/sierra-emu/src/vm/felt252.rs | 75 + debug_utils/sierra-emu/src/vm/felt252_dict.rs | 74 + .../sierra-emu/src/vm/felt252_dict_entry.rs | 59 + .../sierra-emu/src/vm/function_call.rs | 23 + debug_utils/sierra-emu/src/vm/gas.rs | 184 +++ debug_utils/sierra-emu/src/vm/int128.rs | 193 +++ debug_utils/sierra-emu/src/vm/jump.rs | 20 + debug_utils/sierra-emu/src/vm/mem.rs | 83 ++ debug_utils/sierra-emu/src/vm/pedersen.rs | 39 + debug_utils/sierra-emu/src/vm/poseidon.rs | 51 + .../sierra-emu/src/vm/snapshot_take.rs | 20 + debug_utils/sierra-emu/src/vm/starknet.rs | 1265 +++++++++++++++++ debug_utils/sierra-emu/src/vm/struct.rs | 99 ++ debug_utils/sierra-emu/src/vm/uint128.rs | 257 ++++ debug_utils/sierra-emu/src/vm/uint16.rs | 176 +++ debug_utils/sierra-emu/src/vm/uint252.rs | 174 +++ debug_utils/sierra-emu/src/vm/uint32.rs | 176 +++ debug_utils/sierra-emu/src/vm/uint512.rs | 68 + debug_utils/sierra-emu/src/vm/uint64.rs | 176 +++ debug_utils/sierra-emu/src/vm/uint8.rs | 176 +++ debug_utils/sierra-emu/tests/libfuncs.rs | 157 ++ debug_utils/sierra-emu/tests/syscalls.rs | 58 + .../sierra-emu/tests/tests/circuits.cairo | 32 + .../tests/tests/circuits_failure.cairo | 20 + .../sierra-emu/tests/tests/test_u32.cairo | 3 + 68 files changed, 9530 insertions(+) create mode 100644 debug_utils/sierra-emu/.gitignore create mode 100644 debug_utils/sierra-emu/Cargo.toml create mode 100644 debug_utils/sierra-emu/Makefile create mode 100644 debug_utils/sierra-emu/README.md create mode 100644 debug_utils/sierra-emu/programs/hello_starknet.cairo create mode 100644 debug_utils/sierra-emu/programs/syscalls.cairo create mode 100755 debug_utils/sierra-emu/scripts/check-corelib-version.sh create mode 100644 debug_utils/sierra-emu/src/args.rs create mode 100644 debug_utils/sierra-emu/src/debug.rs create mode 100644 debug_utils/sierra-emu/src/dump.rs create mode 100644 debug_utils/sierra-emu/src/gas.rs create mode 100644 debug_utils/sierra-emu/src/lib.rs create mode 100644 debug_utils/sierra-emu/src/main.rs create mode 100644 debug_utils/sierra-emu/src/starknet.rs create mode 100644 debug_utils/sierra-emu/src/starknet/block_info.rs create mode 100644 debug_utils/sierra-emu/src/starknet/execution_info.rs create mode 100644 debug_utils/sierra-emu/src/starknet/execution_info_v2.rs create mode 100644 debug_utils/sierra-emu/src/starknet/resource_bounds.rs create mode 100644 debug_utils/sierra-emu/src/starknet/secp256k1_point.rs create mode 100644 debug_utils/sierra-emu/src/starknet/secp256r1_point.rs create mode 100644 debug_utils/sierra-emu/src/starknet/tx_info.rs create mode 100644 debug_utils/sierra-emu/src/starknet/tx_v2_info.rs create mode 100644 debug_utils/sierra-emu/src/starknet/u256.rs create mode 100644 debug_utils/sierra-emu/src/test_utils.rs create mode 100644 debug_utils/sierra-emu/src/value.rs create mode 100644 debug_utils/sierra-emu/src/vm.rs create mode 100644 debug_utils/sierra-emu/src/vm/ap_tracking.rs create mode 100644 debug_utils/sierra-emu/src/vm/array.rs create mode 100644 debug_utils/sierra-emu/src/vm/bool.rs create mode 100644 debug_utils/sierra-emu/src/vm/bounded_int.rs create mode 100644 debug_utils/sierra-emu/src/vm/box.rs create mode 100644 debug_utils/sierra-emu/src/vm/branch_align.rs create mode 100644 debug_utils/sierra-emu/src/vm/bytes31.rs create mode 100644 debug_utils/sierra-emu/src/vm/cast.rs create mode 100644 debug_utils/sierra-emu/src/vm/circuit.rs create mode 100644 debug_utils/sierra-emu/src/vm/const.rs create mode 100644 debug_utils/sierra-emu/src/vm/drop.rs create mode 100644 debug_utils/sierra-emu/src/vm/dup.rs create mode 100644 debug_utils/sierra-emu/src/vm/ec.rs create mode 100644 debug_utils/sierra-emu/src/vm/enum.rs create mode 100644 debug_utils/sierra-emu/src/vm/felt252.rs create mode 100644 debug_utils/sierra-emu/src/vm/felt252_dict.rs create mode 100644 debug_utils/sierra-emu/src/vm/felt252_dict_entry.rs create mode 100644 debug_utils/sierra-emu/src/vm/function_call.rs create mode 100644 debug_utils/sierra-emu/src/vm/gas.rs create mode 100644 debug_utils/sierra-emu/src/vm/int128.rs create mode 100644 debug_utils/sierra-emu/src/vm/jump.rs create mode 100644 debug_utils/sierra-emu/src/vm/mem.rs create mode 100644 debug_utils/sierra-emu/src/vm/pedersen.rs create mode 100644 debug_utils/sierra-emu/src/vm/poseidon.rs create mode 100644 debug_utils/sierra-emu/src/vm/snapshot_take.rs create mode 100644 debug_utils/sierra-emu/src/vm/starknet.rs create mode 100644 debug_utils/sierra-emu/src/vm/struct.rs create mode 100644 debug_utils/sierra-emu/src/vm/uint128.rs create mode 100644 debug_utils/sierra-emu/src/vm/uint16.rs create mode 100644 debug_utils/sierra-emu/src/vm/uint252.rs create mode 100644 debug_utils/sierra-emu/src/vm/uint32.rs create mode 100644 debug_utils/sierra-emu/src/vm/uint512.rs create mode 100644 debug_utils/sierra-emu/src/vm/uint64.rs create mode 100644 debug_utils/sierra-emu/src/vm/uint8.rs create mode 100644 debug_utils/sierra-emu/tests/libfuncs.rs create mode 100644 debug_utils/sierra-emu/tests/syscalls.rs create mode 100644 debug_utils/sierra-emu/tests/tests/circuits.cairo create mode 100644 debug_utils/sierra-emu/tests/tests/circuits_failure.cairo create mode 100644 debug_utils/sierra-emu/tests/tests/test_u32.cairo diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0224840e2b..82d17b316c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -341,3 +341,18 @@ jobs: df -h - name: build image run: docker build . + + build-sierra-emu: + name: Build sierra-emu + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Retreive cached dependecies + uses: Swatinem/rust-cache@v2 + - name: Deps + run: make deps + - name: Build + run: cargo build --all-features --verbose + - name: Run tests + run: make test diff --git a/Cargo.lock b/Cargo.lock index 9d105cd040..52aaa91b7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -337,6 +337,18 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" + [[package]] name = "bincode" version = "1.3.3" @@ -1324,6 +1336,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32b13ea120a812beba79e34316b3942a857c86ec1593cb34f27bb28272ce2cca" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "const_format" version = "0.2.34" @@ -1454,6 +1472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" dependencies = [ "generic-array", + "rand_core 0.6.4", "subtle", "zeroize", ] @@ -1480,6 +1499,17 @@ dependencies = [ "ordered-float", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "derivative" version = "2.2.0" @@ -1519,6 +1549,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] @@ -1529,6 +1560,20 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c7a8fb8a9fbf66c1f703fe16184d10ca0ee9d23be5b4436400408ba54a95005" +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + [[package]] name = "educe" version = "0.5.11" @@ -1559,6 +1604,26 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "ena" version = "0.14.3" @@ -1622,6 +1687,16 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "fixedbitset" version = "0.5.7" @@ -1729,6 +1804,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -1783,6 +1859,17 @@ dependencies = [ "microlp", ] +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "half" version = "2.5.0" @@ -2020,6 +2107,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + [[package]] name = "keccak" version = "0.1.5" @@ -2398,6 +2499,18 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "parity-scale-codec" version = "3.7.4" @@ -2461,6 +2574,15 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "petgraph" version = "0.7.1" @@ -2498,6 +2620,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "plotters" version = "0.3.7" @@ -2586,6 +2718,15 @@ dependencies = [ "syn 2.0.100", ] +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-crate" version = "3.3.0" @@ -3039,6 +3180,20 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "1.0.26" @@ -3136,6 +3291,50 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "sierra-emu" +version = "0.1.0" +dependencies = [ + "cairo-lang-compiler", + "cairo-lang-filesystem", + "cairo-lang-runner", + "cairo-lang-sierra", + "cairo-lang-sierra-ap-change", + "cairo-lang-sierra-gas", + "cairo-lang-starknet", + "cairo-lang-starknet-classes", + "cairo-lang-utils", + "clap", + "k256", + "keccak", + "num-bigint", + "num-traits", + "p256", + "rand 0.8.5", + "sec1", + "serde", + "serde_json", + "sha2", + "smallvec", + "starknet-crypto", + "starknet-curve", + "starknet-types-core", + "tempfile", + "thiserror 2.0.12", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "siphasher" version = "1.0.1" @@ -3183,6 +3382,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "sprs" version = "0.11.3" diff --git a/Cargo.toml b/Cargo.toml index a2c9479a46..80c5abbae8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -174,3 +174,6 @@ harness = false [[bench]] name = "libfuncs" harness = false + +[workspace] +members = ["debug_utils/sierra-emu"] diff --git a/debug_utils/sierra-emu/.gitignore b/debug_utils/sierra-emu/.gitignore new file mode 100644 index 0000000000..17dccf957d --- /dev/null +++ b/debug_utils/sierra-emu/.gitignore @@ -0,0 +1,24 @@ +# Created by https://www.toptal.com/developers/gitignore/api/rust +# Edit at https://www.toptal.com/developers/gitignore?templates=rust + +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + +# End of https://www.toptal.com/developers/gitignore/api/rust + +cairo2/ + +corelib + +*.tar + +*.json diff --git a/debug_utils/sierra-emu/Cargo.toml b/debug_utils/sierra-emu/Cargo.toml new file mode 100644 index 0000000000..1a69e7fbe7 --- /dev/null +++ b/debug_utils/sierra-emu/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "sierra-emu" +version = "0.1.0" +edition = "2021" +license = "Apache-2.0" +description = "A Cairo (Sierra) Virtual Machine." +repository = "https://github.com/lambdaclass/sierra-emu" +readme = "README.md" +keywords = ["starknet", "cairo", "compiler", "mlir"] + +[dependencies] +cairo-lang-compiler = "=2.12.0-dev.0" +cairo-lang-filesystem = "=2.12.0-dev.0" +cairo-lang-runner = "=2.12.0-dev.0" +cairo-lang-sierra = "=2.12.0-dev.0" +cairo-lang-sierra-ap-change = "=2.12.0-dev.0" +cairo-lang-sierra-gas = "=2.12.0-dev.0" +cairo-lang-starknet-classes = "=2.12.0-dev.0" +cairo-lang-utils = "=2.12.0-dev.0" +clap = { version = "4.5.26", features = ["derive"] } +k256 = "0.13.4" +keccak = "0.1.5" +num-bigint = "0.4.6" +num-traits = "0.2.19" +p256 = "0.13.2" +rand = "0.8.5" +sec1 = { version = "0.7.3", features = ["std"] } +serde = { version = "1.0.215", features = ["derive"] } +serde_json = "1.0.133" +sha2 = { version = "0.10.8", features = ["compress"] } +smallvec = "1.13.2" +starknet-crypto = "0.7.3" +starknet-curve = "0.5.1" +starknet-types-core = "0.1.7" +tempfile = "3.14.0" +thiserror = "2.0.3" +tracing = "0.1.41" +tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } + +[dev-dependencies] +cairo-lang-compiler = "=2.12.0-dev.0" +cairo-lang-starknet = "=2.12.0-dev.0" + +# On dev optimize dependencies a bit so it's not as slow. +[profile.dev.package."*"] +opt-level = 1 diff --git a/debug_utils/sierra-emu/Makefile b/debug_utils/sierra-emu/Makefile new file mode 100644 index 0000000000..06470973cd --- /dev/null +++ b/debug_utils/sierra-emu/Makefile @@ -0,0 +1,75 @@ +.PHONY: usage deps build check needs-cairo2 deps-macos build-cairo-2-compiler-macos decompress-cairo install-scarb clean + +UNAME := $(shell uname) + +CAIRO_2_VERSION= 2.12.0-dev.0 +SCARB_VERSION = 2.11.2 + +needs-cairo2: +ifeq ($(wildcard ./cairo2/.),) + $(error You are missing the Starknet Cairo 1 compiler, please run 'make deps' to install the necessary dependencies.) +endif + ./scripts/check-corelib-version.sh $(CAIRO_2_VERSION) + +usage: + @echo "Usage:" + @echo " deps: Installs the necesarry dependencies." + @echo " build: Builds the cairo-native library and binaries." + @echo " check: Checks format and lints." + @echo " test: Runs all tests." + @echo " clean: Cleans the built artifacts." + +build: + cargo build --release --all-features + +check: + cargo fmt --all -- --check + cargo clippy --all-targets --all-features -- -D warnings + +test: needs-cairo2 + cargo test --all-features + +clean: + cargo clean + +deps: +ifeq ($(UNAME), Linux) +deps: build-cairo-2-compiler install-scarb +endif +ifeq ($(UNAME), Darwin) +deps: deps-macos +endif + -rm -rf corelib + -ln -s cairo2/corelib corelib + +deps-macos: build-cairo-2-compiler-macos install-scarb-macos + +cairo-repo-2-dir = cairo2 +cairo-repo-2-dir-macos = cairo2-macos + +build-cairo-2-compiler-macos: | $(cairo-repo-2-dir-macos) + +$(cairo-repo-2-dir-macos): cairo-${CAIRO_2_VERSION}-macos.tar + $(MAKE) decompress-cairo SOURCE=$< TARGET=cairo2/ + +build-cairo-2-compiler: | $(cairo-repo-2-dir) + +$(cairo-repo-2-dir): cairo-${CAIRO_2_VERSION}.tar + $(MAKE) decompress-cairo SOURCE=$< TARGET=cairo2/ + +decompress-cairo: + rm -rf $(TARGET) \ + && tar -xzvf $(SOURCE) \ + && mv cairo/ $(TARGET) + +cairo-%-macos.tar: + curl -L -o "$@" "https://github.com/starkware-libs/cairo/releases/download/v$*/release-aarch64-apple-darwin.tar" + +cairo-%.tar: + curl -L -o "$@" "https://github.com/starkware-libs/cairo/releases/download/v$*/release-x86_64-unknown-linux-musl.tar.gz" + +install-scarb: + curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh| sh -s -- --no-modify-path --version $(SCARB_VERSION) + +install-scarb-macos: + curl --proto '=https' --tlsv1.2 -sSf https://docs.swmansion.com/scarb/install.sh| sh -s -- --version $(SCARB_VERSION) diff --git a/debug_utils/sierra-emu/README.md b/debug_utils/sierra-emu/README.md new file mode 100644 index 0000000000..36c00411d7 --- /dev/null +++ b/debug_utils/sierra-emu/README.md @@ -0,0 +1,102 @@ +
+ +### ⚡ Cairo Sierra Emulator ⚡ + +An Cairo emulator directly using the Cairo's intermediate representation "Sierra" instead of CASM.
+An useful usecase is to aid in debugging other Cairo related VMs. + +[Report Bug](https://github.com/lambdaclass/sierra-emu/issues/new) · [Request Feature](https://github.com/lambdaclass/sierra-emu/issues/new) + +[![Telegram Chat][tg-badge]][tg-url] +[![rust](https://github.com/lambdaclass/sierra-emu/actions/workflows/ci.yml/badge.svg)](https://github.com/lambdaclass/sierra-emu/actions/workflows/ci.yml) +[![codecov](https://img.shields.io/codecov/c/github/lambdaclass/sierra-emu)](https://codecov.io/gh/lambdaclass/sierra-emu) +[![license](https://img.shields.io/github/license/lambdaclass/sierra-emu)](/LICENSE) +[![pr-welcome]](#-contributing) + + +[tg-badge]: https://img.shields.io/endpoint?url=https%3A%2F%2Ftg.sumanjay.workers.dev%2FLambdaStarkNet%2F&logo=telegram&label=chat&color=neon +[tg-url]: https://t.me/LambdaStarkNet +[pr-welcome]: https://img.shields.io/static/v1?color=orange&label=PRs&style=flat&message=welcome + +
+ + + +## Running the Program +`cargo run ` + +## Using the API + +With a contract: + +```rust +use std::path::Path; + +use cairo_lang_compiler::CompilerConfig; +use cairo_lang_starknet::compile::compile_path; +use sierra_emu::{ + starknet::StubSyscallHandler, ContractExecutionResult, + VirtualMachine, +}; +let path = Path::new("programs/hello_starknet.cairo"); + +let contract = compile_path( + path, + None, + CompilerConfig { + replace_ids: true, + ..Default::default() + }, +) +.unwrap(); + +let sierra_program = contract.extract_sierra_program().unwrap(); + +let entry_point = contract.entry_points_by_type.external.first().unwrap(); + +let mut vm = VirtualMachine::new_starknet( + sierra_program.clone().into(), + &contract.entry_points_by_type, +); + +let calldata = [2.into()]; +let initial_gas = 1000000; + +// Change the StubSyscallHandler with your own implementation. +let syscall_handler = &mut StubSyscallHandler::default(); + +vm.call_contract(entry_point.selector.clone().into(), initial_gas, calldata); + +let _race = vm.run_with_trace(syscall_handler); +let trace_str = serde_json::to_string_pretty(&trace).unwrap(); +std::fs::write("contract_trace.json", trace_str).unwrap(); +``` + +With a program: + +```rust +let path = Path::new(path); + +let sierra_program = Arc::new( + compile_cairo_project_at_path( + path, + CompilerConfig { + replace_ids: true, + ..Default::default() + }, + ) + .unwrap(), +); + +let function = find_entry_point_by_name(&sierra_program, func_name).unwrap(); + +let mut vm = VirtualMachine::new(sierra_program.clone()); + +let args = args.iter().cloned(); +let initial_gas = 1000000; + +vm.call_program(function, initial_gas, args); + +let syscall_handler = &mut StubSyscallHandler::default(); +let trace = vm.run_with_trace(syscall_handler); +``` diff --git a/debug_utils/sierra-emu/programs/hello_starknet.cairo b/debug_utils/sierra-emu/programs/hello_starknet.cairo new file mode 100644 index 0000000000..aab0edc5e6 --- /dev/null +++ b/debug_utils/sierra-emu/programs/hello_starknet.cairo @@ -0,0 +1,18 @@ +#[starknet::contract] +mod Echo { + #[storage] + struct Storage { + balance: felt252, + } + + #[constructor] + fn constructor(ref self: ContractState, initial_balance: felt252) { + //panic_with_felt252('panic'); + self.balance.write(initial_balance); + } + + #[external(v0)] + fn echo(ref self: ContractState, value: felt252) -> felt252 { + value + } +} diff --git a/debug_utils/sierra-emu/programs/syscalls.cairo b/debug_utils/sierra-emu/programs/syscalls.cairo new file mode 100644 index 0000000000..e8e23cbcfe --- /dev/null +++ b/debug_utils/sierra-emu/programs/syscalls.cairo @@ -0,0 +1,118 @@ +use starknet::ClassHash; +use starknet::{ + call_contract_syscall, ContractAddress, + deploy_syscall, emit_event_syscall, ExecutionInfo, get_block_hash_syscall, + keccak_syscall, + library_call_syscall, replace_class_syscall, send_message_to_l1_syscall, + storage_address_try_from_felt252, storage_read_syscall, storage_write_syscall, SyscallResult, + testing::cheatcode, +}; +use starknet::syscalls::get_execution_info_syscall; +use starknet::syscalls::get_execution_info_v2_syscall; + +fn get_block_hash() -> SyscallResult { + get_block_hash_syscall(0) +} + +fn get_execution_info() -> SyscallResult> { + get_execution_info_syscall() +} + +fn get_execution_info_v2() -> SyscallResult> { + get_execution_info_v2_syscall() +} + +const ZERO_CLASS_HASH: ClassHash = 0x0.try_into().unwrap(); +const ZERO_CONTRACT_ADDRESS: ContractAddress = 0x0.try_into().unwrap(); + +fn deploy() -> SyscallResult<(ContractAddress, Span)> { + deploy_syscall(ZERO_CLASS_HASH, 0, array![].span(), false) +} + +fn replace_class() -> SyscallResult<()> { + replace_class_syscall(ZERO_CLASS_HASH) +} + +fn library_call() -> SyscallResult> { + library_call_syscall(ZERO_CLASS_HASH, 0, array![].span()) +} + +fn call_contract() -> SyscallResult> { + call_contract_syscall(ZERO_CONTRACT_ADDRESS, 0, array![].span()) +} + +fn storage_read() -> felt252 { + storage_read_syscall(0, storage_address_try_from_felt252(0).unwrap()).unwrap() +} + +fn storage_write() { + storage_write_syscall(0, storage_address_try_from_felt252(0).unwrap(), 0).unwrap() +} + +fn emit_event() -> SyscallResult<()> { + emit_event_syscall(array![].span(), array![].span()) +} + +fn send_message_to_l1() -> SyscallResult<()> { + send_message_to_l1_syscall(3, array![2].span()) +} + +fn keccak() -> SyscallResult { + keccak_syscall(array![].span()) +} + +fn set_sequencer_address(address: felt252) -> Span { + return cheatcode::<'set_sequencer_address'>(array![address].span()); +} + +fn set_account_contract_address(address: felt252) -> Span { + return cheatcode::<'set_account_contract_address'>(array![address].span()); +} + +fn set_block_number(number: felt252) -> Span { + return cheatcode::<'set_block_number'>(array![number].span()); +} + +fn set_block_timestamp(timestamp: felt252) -> Span { + return cheatcode::<'set_block_timestamp'>(array![timestamp].span()); +} + +fn set_caller_address(address: felt252) -> Span { + return cheatcode::<'set_caller_address'>(array![address].span()); +} + +fn set_chain_id(id: felt252) -> Span { + return cheatcode::<'set_chain_id'>(array![id].span()); +} + +fn set_contract_address(address: felt252) -> Span { + return cheatcode::<'set_contract_address'>(array![address].span()); +} + +fn set_max_fee(fee: felt252) -> Span { + return cheatcode::<'set_max_fee'>(array![fee].span()); +} + +fn set_nonce(nonce: felt252) -> Span { + return cheatcode::<'set_nonce'>(array![nonce].span()); +} + +fn set_signature(signature: Array) -> Span { + return cheatcode::<'set_signature'>(signature.span()); +} + +fn set_transaction_hash(hash: felt252) -> Span { + return cheatcode::<'set_transaction_hash'>(array![hash].span()); +} + +fn set_version(version: felt252) -> Span { + return cheatcode::<'set_version'>(array![version].span()); +} + +fn pop_log(log: felt252) -> Span { + return cheatcode::<'pop_log'>(array![log].span()); +} + +fn pop_l2_to_l1_message(message: felt252) -> Span { + return cheatcode::<'pop_l2_to_l1_message'>(array![message].span()); +} diff --git a/debug_utils/sierra-emu/scripts/check-corelib-version.sh b/debug_utils/sierra-emu/scripts/check-corelib-version.sh new file mode 100755 index 0000000000..2458d67551 --- /dev/null +++ b/debug_utils/sierra-emu/scripts/check-corelib-version.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# Script to check the corelib version matches. + +_result=$(grep "version = \"$1\"" corelib/Scarb.toml) + +if [ $? -ne 0 ]; then + echo "corelib version mismatch, please re-run 'make deps'" + exit 1 +fi diff --git a/debug_utils/sierra-emu/src/args.rs b/debug_utils/sierra-emu/src/args.rs new file mode 100644 index 0000000000..2108748de4 --- /dev/null +++ b/debug_utils/sierra-emu/src/args.rs @@ -0,0 +1,32 @@ +use clap::Parser; +use std::{num::ParseIntError, path::PathBuf, str::FromStr}; + +#[derive(Debug, Parser)] +pub struct CmdArgs { + pub program: PathBuf, + pub entry_point: EntryPoint, + + pub args: Vec, + #[clap(long)] + pub available_gas: Option, + + #[clap(long, short)] + pub output: Option, +} + +#[derive(Clone, Debug)] +pub enum EntryPoint { + Number(u64), + String(String), +} + +impl FromStr for EntryPoint { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + Ok(match s.chars().next() { + Some(x) if x.is_numeric() => Self::Number(s.parse()?), + _ => Self::String(s.to_string()), + }) + } +} diff --git a/debug_utils/sierra-emu/src/debug.rs b/debug_utils/sierra-emu/src/debug.rs new file mode 100644 index 0000000000..c35e849274 --- /dev/null +++ b/debug_utils/sierra-emu/src/debug.rs @@ -0,0 +1,582 @@ +use cairo_lang_sierra::{ + extensions::{ + array::ArrayConcreteLibfunc, + boolean::BoolConcreteLibfunc, + bounded_int::BoundedIntConcreteLibfunc, + boxing::BoxConcreteLibfunc, + bytes31::Bytes31ConcreteLibfunc, + casts::CastConcreteLibfunc, + circuit::{CircuitConcreteLibfunc, CircuitTypeConcrete}, + const_type::ConstConcreteLibfunc, + core::{CoreConcreteLibfunc, CoreLibfunc, CoreType, CoreTypeConcrete}, + coupon::CouponConcreteLibfunc, + debug::DebugConcreteLibfunc, + ec::EcConcreteLibfunc, + enm::EnumConcreteLibfunc, + felt252::{Felt252BinaryOperationConcrete, Felt252BinaryOperator, Felt252Concrete}, + felt252_dict::{Felt252DictConcreteLibfunc, Felt252DictEntryConcreteLibfunc}, + gas::GasConcreteLibfunc, + int::{ + signed::SintConcrete, signed128::Sint128Concrete, unsigned::UintConcrete, + unsigned128::Uint128Concrete, unsigned256::Uint256Concrete, + unsigned512::Uint512Concrete, IntOperator, + }, + lib_func::{BranchSignature, ParamSignature}, + mem::MemConcreteLibfunc, + nullable::NullableConcreteLibfunc, + pedersen::PedersenConcreteLibfunc, + poseidon::PoseidonConcreteLibfunc, + range::IntRangeConcreteLibfunc, + starknet::{ + secp256::{Secp256ConcreteLibfunc, Secp256OpConcreteLibfunc}, + testing::TestingConcreteLibfunc, + StarknetConcreteLibfunc, StarknetTypeConcrete, + }, + structure::StructConcreteLibfunc, + }, + ids::ConcreteTypeId, + program_registry::ProgramRegistry, +}; + +use crate::Value; + +pub fn libfunc_to_name(value: &CoreConcreteLibfunc) -> &'static str { + match value { + CoreConcreteLibfunc::ApTracking(value) => match value { + cairo_lang_sierra::extensions::ap_tracking::ApTrackingConcreteLibfunc::Revoke(_) => { + "revoke_ap_tracking" + } + cairo_lang_sierra::extensions::ap_tracking::ApTrackingConcreteLibfunc::Enable(_) => { + "enable_ap_tracking" + } + cairo_lang_sierra::extensions::ap_tracking::ApTrackingConcreteLibfunc::Disable(_) => { + "disable_ap_tracking" + } + }, + CoreConcreteLibfunc::Array(value) => match value { + ArrayConcreteLibfunc::New(_) => "array_new", + ArrayConcreteLibfunc::SpanFromTuple(_) => "span_from_tuple", + ArrayConcreteLibfunc::Append(_) => "array_append", + ArrayConcreteLibfunc::PopFront(_) => "array_pop_front", + ArrayConcreteLibfunc::PopFrontConsume(_) => "array_pop_front_consume", + ArrayConcreteLibfunc::Get(_) => "array_get", + ArrayConcreteLibfunc::Slice(_) => "array_slice", + ArrayConcreteLibfunc::Len(_) => "array_len", + ArrayConcreteLibfunc::SnapshotPopFront(_) => "array_snapshot_pop_front", + ArrayConcreteLibfunc::SnapshotPopBack(_) => "array_snapshot_pop_back", + ArrayConcreteLibfunc::TupleFromSpan(_) => "array_tuple_from_span", + ArrayConcreteLibfunc::SnapshotMultiPopFront(_) => "array_snapshot_multi_pop_front", + ArrayConcreteLibfunc::SnapshotMultiPopBack(_) => "array_snapshot_multi_pop_back", + }, + CoreConcreteLibfunc::BranchAlign(_) => "branch_align", + CoreConcreteLibfunc::Bool(value) => match value { + BoolConcreteLibfunc::And(_) => "bool_and", + BoolConcreteLibfunc::Not(_) => "bool_not", + BoolConcreteLibfunc::Xor(_) => "bool_xor", + BoolConcreteLibfunc::Or(_) => "bool_or", + BoolConcreteLibfunc::ToFelt252(_) => "bool_to_felt252", + }, + CoreConcreteLibfunc::Box(value) => match value { + BoxConcreteLibfunc::Into(_) => "box_into", + BoxConcreteLibfunc::Unbox(_) => "box_unbox", + BoxConcreteLibfunc::ForwardSnapshot(_) => "box_forward_snapshot", + }, + CoreConcreteLibfunc::Cast(value) => match value { + CastConcreteLibfunc::Downcast(_) => "downcast", + CastConcreteLibfunc::Upcast(_) => "upcast", + }, + CoreConcreteLibfunc::Coupon(value) => match value { + CouponConcreteLibfunc::Buy(_) => "coupon_buy", + CouponConcreteLibfunc::Refund(_) => "coupon_refund", + }, + CoreConcreteLibfunc::CouponCall(_) => "coupon_call", + CoreConcreteLibfunc::Drop(_) => "drop", + CoreConcreteLibfunc::Dup(_) => "dup", + CoreConcreteLibfunc::Ec(value) => match value { + EcConcreteLibfunc::IsZero(_) => "ec_is_zero", + EcConcreteLibfunc::Neg(_) => "ec_neg", + EcConcreteLibfunc::StateAdd(_) => "ec_state_add", + EcConcreteLibfunc::TryNew(_) => "ec_try_new", + EcConcreteLibfunc::StateFinalize(_) => "ec_state_finalize", + EcConcreteLibfunc::StateInit(_) => "ec_state_init", + EcConcreteLibfunc::StateAddMul(_) => "ec_state_add_mul", + EcConcreteLibfunc::PointFromX(_) => "ec_point_from_x", + EcConcreteLibfunc::UnwrapPoint(_) => "ec_unwrap_point", + EcConcreteLibfunc::Zero(_) => "ec_zero", + }, + CoreConcreteLibfunc::Felt252(value) => match value { + Felt252Concrete::BinaryOperation(op) => match op { + Felt252BinaryOperationConcrete::WithVar(op) => match &op.operator { + Felt252BinaryOperator::Add => "felt252_add", + Felt252BinaryOperator::Sub => "felt252_sub", + Felt252BinaryOperator::Mul => "felt252_mul", + Felt252BinaryOperator::Div => "felt252_div", + }, + Felt252BinaryOperationConcrete::WithConst(op) => match &op.operator { + Felt252BinaryOperator::Add => "felt252_const_add", + Felt252BinaryOperator::Sub => "felt252_const_sub", + Felt252BinaryOperator::Mul => "felt252_const_mul", + Felt252BinaryOperator::Div => "felt252_const_div", + }, + }, + Felt252Concrete::Const(_) => "felt252_const", + Felt252Concrete::IsZero(_) => "felt252_is_zero", + }, + CoreConcreteLibfunc::Const(value) => match value { + ConstConcreteLibfunc::AsBox(_) => "const_as_box", + ConstConcreteLibfunc::AsImmediate(_) => "const_as_immediate", + }, + CoreConcreteLibfunc::FunctionCall(_) => "function_call", + CoreConcreteLibfunc::Gas(value) => match value { + GasConcreteLibfunc::WithdrawGas(_) => "withdraw_gas", + GasConcreteLibfunc::RedepositGas(_) => "redeposit_gas", + GasConcreteLibfunc::GetAvailableGas(_) => "get_available_gas", + GasConcreteLibfunc::BuiltinWithdrawGas(_) => "builtin_withdraw_gas", + GasConcreteLibfunc::GetBuiltinCosts(_) => "get_builtin_costs", + GasConcreteLibfunc::GetUnspentGas(_) => todo!(), + }, + CoreConcreteLibfunc::Uint8(value) => match value { + UintConcrete::Const(_) => "u8_const", + UintConcrete::Operation(op) => match &op.operator { + IntOperator::OverflowingAdd => "u8_overflowing_add", + IntOperator::OverflowingSub => "u8_overflowing_sub", + }, + UintConcrete::SquareRoot(_) => "u8_sqrt", + UintConcrete::Equal(_) => "u8_eq", + UintConcrete::ToFelt252(_) => "u8_to_felt252", + UintConcrete::FromFelt252(_) => "u8_from_felt252", + UintConcrete::IsZero(_) => "u8_is_zero", + UintConcrete::Divmod(_) => "u8_divmod", + UintConcrete::WideMul(_) => "u8_wide_mul", + UintConcrete::Bitwise(_) => "u8_bitwise", + }, + CoreConcreteLibfunc::Uint16(value) => match value { + UintConcrete::Const(_) => "u16_const", + UintConcrete::Operation(op) => match &op.operator { + IntOperator::OverflowingAdd => "u16_overflowing_add", + IntOperator::OverflowingSub => "u16_overflowing_sub", + }, + UintConcrete::SquareRoot(_) => "u16_sqrt", + UintConcrete::Equal(_) => "u16_eq", + UintConcrete::ToFelt252(_) => "u16_to_felt252", + UintConcrete::FromFelt252(_) => "u16_from_felt252", + UintConcrete::IsZero(_) => "u16_is_zero", + UintConcrete::Divmod(_) => "u16_divmod", + UintConcrete::WideMul(_) => "u16_wide_mul", + UintConcrete::Bitwise(_) => "u16_bitwise", + }, + CoreConcreteLibfunc::Uint32(value) => match value { + UintConcrete::Const(_) => "u32_const", + UintConcrete::Operation(op) => match &op.operator { + IntOperator::OverflowingAdd => "u32_overflowing_add", + IntOperator::OverflowingSub => "u32_overflowing_sub", + }, + UintConcrete::SquareRoot(_) => "u32_sqrt", + UintConcrete::Equal(_) => "u32_eq", + UintConcrete::ToFelt252(_) => "u32_to_felt252", + UintConcrete::FromFelt252(_) => "u32_from_felt252", + UintConcrete::IsZero(_) => "u32_is_zero", + UintConcrete::Divmod(_) => "u32_divmod", + UintConcrete::WideMul(_) => "u32_wide_mul", + UintConcrete::Bitwise(_) => "u32_bitwise", + }, + CoreConcreteLibfunc::Uint64(value) => match value { + UintConcrete::Const(_) => "u64_const", + UintConcrete::Operation(op) => match &op.operator { + IntOperator::OverflowingAdd => "u64_overflowing_add", + IntOperator::OverflowingSub => "u64_overflowing_sub", + }, + UintConcrete::SquareRoot(_) => "u64_sqrt", + UintConcrete::Equal(_) => "u64_eq", + UintConcrete::ToFelt252(_) => "u64_to_felt252", + UintConcrete::FromFelt252(_) => "u64_from_felt252", + UintConcrete::IsZero(_) => "u64_is_zero", + UintConcrete::Divmod(_) => "u64_divmod", + UintConcrete::WideMul(_) => "u64_wide_mul", + UintConcrete::Bitwise(_) => "u64_bitwise", + }, + CoreConcreteLibfunc::Uint128(value) => match value { + Uint128Concrete::Operation(op) => match &op.operator { + IntOperator::OverflowingAdd => "u128_overflowing_add", + IntOperator::OverflowingSub => "u128_overflowing_sub", + }, + Uint128Concrete::Divmod(_) => "u128_divmod", + Uint128Concrete::GuaranteeMul(_) => "u128_guarantee_mul", + Uint128Concrete::MulGuaranteeVerify(_) => "u128_mul_guarantee_verify", + Uint128Concrete::Equal(_) => "u128_equal", + Uint128Concrete::SquareRoot(_) => "u128_sqrt", + Uint128Concrete::Const(_) => "u128_const", + Uint128Concrete::FromFelt252(_) => "u128_from_felt", + Uint128Concrete::ToFelt252(_) => "u128_to_felt252", + Uint128Concrete::IsZero(_) => "u128_is_zero", + Uint128Concrete::Bitwise(_) => "u128_bitwise", + Uint128Concrete::ByteReverse(_) => "u128_byte_reverse", + }, + CoreConcreteLibfunc::Uint256(value) => match value { + Uint256Concrete::IsZero(_) => "u256_is_zero", + Uint256Concrete::Divmod(_) => "u256_divmod", + Uint256Concrete::SquareRoot(_) => "u256_sqrt", + Uint256Concrete::InvModN(_) => "u256_inv_mod_n", + }, + CoreConcreteLibfunc::Uint512(value) => match value { + Uint512Concrete::DivModU256(_) => "u512_divmod_u256", + }, + CoreConcreteLibfunc::Sint8(value) => match value { + SintConcrete::Const(_) => "i8_const", + SintConcrete::Equal(_) => "i8_eq", + SintConcrete::ToFelt252(_) => "i8_to_felt252", + SintConcrete::FromFelt252(_) => "i8_from_felt252", + SintConcrete::Operation(op) => match &op.operator { + IntOperator::OverflowingAdd => "i8_overflowing_add", + IntOperator::OverflowingSub => "i8_overflowing_sub", + }, + SintConcrete::Diff(_) => "i8_diff", + SintConcrete::WideMul(_) => "i8_wide_mul", + }, + CoreConcreteLibfunc::Sint16(value) => match value { + SintConcrete::Const(_) => "i16_const", + SintConcrete::Equal(_) => "i16_eq", + SintConcrete::ToFelt252(_) => "i16_to_felt252", + SintConcrete::FromFelt252(_) => "i16_from_felt252", + SintConcrete::Operation(op) => match &op.operator { + IntOperator::OverflowingAdd => "i16_overflowing_add", + IntOperator::OverflowingSub => "i16_overflowing_sub", + }, + SintConcrete::Diff(_) => "i16_diff", + SintConcrete::WideMul(_) => "i16_wide_mul", + }, + CoreConcreteLibfunc::Sint32(value) => match value { + SintConcrete::Const(_) => "i32_const", + SintConcrete::Equal(_) => "i32_eq", + SintConcrete::ToFelt252(_) => "i32_to_felt252", + SintConcrete::FromFelt252(_) => "i32_from_felt252", + SintConcrete::Operation(op) => match &op.operator { + IntOperator::OverflowingAdd => "i32_overflowing_add", + IntOperator::OverflowingSub => "i32_overflowing_sub", + }, + SintConcrete::Diff(_) => "i32_diff", + SintConcrete::WideMul(_) => "i32_wide_mul", + }, + CoreConcreteLibfunc::Sint64(value) => match value { + SintConcrete::Const(_) => "i64_const", + SintConcrete::Equal(_) => "i64_eq", + SintConcrete::ToFelt252(_) => "i64_to_felt252", + SintConcrete::FromFelt252(_) => "i64_from_felt252", + SintConcrete::Operation(op) => match &op.operator { + IntOperator::OverflowingAdd => "i64_overflowing_add", + IntOperator::OverflowingSub => "i64_overflowing_sub", + }, + SintConcrete::Diff(_) => "i64_diff", + SintConcrete::WideMul(_) => "i64_wide_mul", + }, + CoreConcreteLibfunc::Sint128(value) => match value { + Sint128Concrete::Const(_) => "i128_const", + Sint128Concrete::Equal(_) => "i128_eq", + Sint128Concrete::ToFelt252(_) => "i128_to_felt252", + Sint128Concrete::FromFelt252(_) => "i128_from_felt252", + Sint128Concrete::Operation(op) => match &op.operator { + IntOperator::OverflowingAdd => "i128_overflowing_add", + IntOperator::OverflowingSub => "i128_overflowing_sub", + }, + Sint128Concrete::Diff(_) => "i128_diff", + }, + CoreConcreteLibfunc::Mem(value) => match value { + MemConcreteLibfunc::StoreTemp(_) => "store_temp", + MemConcreteLibfunc::StoreLocal(_) => "store_local", + MemConcreteLibfunc::FinalizeLocals(_) => "finalize_locals", + MemConcreteLibfunc::AllocLocal(_) => "alloc_local", + MemConcreteLibfunc::Rename(_) => "rename", + }, + CoreConcreteLibfunc::Nullable(value) => match value { + NullableConcreteLibfunc::Null(_) => "nullable_null", + NullableConcreteLibfunc::NullableFromBox(_) => "nullable_from_box", + NullableConcreteLibfunc::MatchNullable(_) => "match_nullable", + NullableConcreteLibfunc::ForwardSnapshot(_) => "nullable_forward_snapshot", + }, + CoreConcreteLibfunc::UnwrapNonZero(_) => "unwrap_non_zero", + CoreConcreteLibfunc::UnconditionalJump(_) => "jump", + CoreConcreteLibfunc::Enum(value) => match value { + EnumConcreteLibfunc::Init(_) => "enum_init", + EnumConcreteLibfunc::FromBoundedInt(_) => "enum_from_bounded_int", + EnumConcreteLibfunc::Match(_) => "enum_match", + EnumConcreteLibfunc::SnapshotMatch(_) => "enum_snapshot_match", + }, + CoreConcreteLibfunc::Struct(value) => match value { + StructConcreteLibfunc::Construct(_) => "struct_construct", + StructConcreteLibfunc::Deconstruct(_) => "struct_deconstruct", + StructConcreteLibfunc::SnapshotDeconstruct(_) => "struct_snapshot_deconstruct", + }, + CoreConcreteLibfunc::Felt252Dict(value) => match value { + Felt252DictConcreteLibfunc::New(_) => "felt252dict_new", + Felt252DictConcreteLibfunc::Squash(_) => "felt252dict_squash", + }, + CoreConcreteLibfunc::Felt252DictEntry(value) => match value { + Felt252DictEntryConcreteLibfunc::Get(_) => "felt252dict_get", + Felt252DictEntryConcreteLibfunc::Finalize(_) => "felt252dict_finalize", + }, + CoreConcreteLibfunc::Pedersen(value) => match value { + PedersenConcreteLibfunc::PedersenHash(_) => "pedersen_hash", + }, + CoreConcreteLibfunc::Poseidon(value) => match value { + PoseidonConcreteLibfunc::HadesPermutation(_) => "hades_permutation", + }, + CoreConcreteLibfunc::Starknet(value) => match value { + StarknetConcreteLibfunc::CallContract(_) => "call_contract", + StarknetConcreteLibfunc::ClassHashConst(_) => "class_hash_const", + StarknetConcreteLibfunc::ClassHashTryFromFelt252(_) => "class_hash_try_from_felt252", + StarknetConcreteLibfunc::ClassHashToFelt252(_) => "class_hash_to_felt252", + StarknetConcreteLibfunc::ContractAddressConst(_) => "contract_address_const", + StarknetConcreteLibfunc::ContractAddressTryFromFelt252(_) => { + "contract_address_try_from_felt252" + } + StarknetConcreteLibfunc::ContractAddressToFelt252(_) => "contract_address_to_felt252", + StarknetConcreteLibfunc::StorageRead(_) => "storage_read", + StarknetConcreteLibfunc::StorageWrite(_) => "storage_write", + StarknetConcreteLibfunc::StorageBaseAddressConst(_) => "storage_base_address_const", + StarknetConcreteLibfunc::StorageBaseAddressFromFelt252(_) => { + "storage_base_address_from_felt252" + } + StarknetConcreteLibfunc::StorageAddressFromBase(_) => "storage_address_from_base", + StarknetConcreteLibfunc::StorageAddressFromBaseAndOffset(_) => { + "storage_address_from_base_and_offset" + } + StarknetConcreteLibfunc::StorageAddressToFelt252(_) => "storage_address_to_felt252", + StarknetConcreteLibfunc::StorageAddressTryFromFelt252(_) => { + "storage_address_try_from_felt252" + } + StarknetConcreteLibfunc::EmitEvent(_) => "emit_event", + StarknetConcreteLibfunc::GetBlockHash(_) => "get_block_hash", + StarknetConcreteLibfunc::GetExecutionInfo(_) => "get_exec_info_v1", + StarknetConcreteLibfunc::GetExecutionInfoV2(_) => "get_exec_info_v2", + StarknetConcreteLibfunc::Deploy(_) => "deploy", + StarknetConcreteLibfunc::Keccak(_) => "keccak", + StarknetConcreteLibfunc::LibraryCall(_) => "library_call", + StarknetConcreteLibfunc::ReplaceClass(_) => "replace_class", + StarknetConcreteLibfunc::SendMessageToL1(_) => "send_message_to_l1", + StarknetConcreteLibfunc::Testing(value) => match value { + TestingConcreteLibfunc::Cheatcode(_) => "cheatcode", + }, + StarknetConcreteLibfunc::Secp256(value) => match value { + Secp256ConcreteLibfunc::K1(value) => match value { + Secp256OpConcreteLibfunc::New(_) => "secp256k1_new", + Secp256OpConcreteLibfunc::Add(_) => "secp256k1_add", + Secp256OpConcreteLibfunc::Mul(_) => "secp256k1_mul", + Secp256OpConcreteLibfunc::GetPointFromX(_) => "secp256k1_get_point_from_x", + Secp256OpConcreteLibfunc::GetXy(_) => "secp256k1_get_xy", + }, + Secp256ConcreteLibfunc::R1(value) => match value { + Secp256OpConcreteLibfunc::New(_) => "secp256r1_new", + Secp256OpConcreteLibfunc::Add(_) => "secp256r1_add", + Secp256OpConcreteLibfunc::Mul(_) => "secp256r1_mul", + Secp256OpConcreteLibfunc::GetPointFromX(_) => "secp256r1_get_point_from_x", + Secp256OpConcreteLibfunc::GetXy(_) => "secp256r1_get_xy", + }, + }, + StarknetConcreteLibfunc::Sha256ProcessBlock(_) => "sha256_process_block", + StarknetConcreteLibfunc::Sha256StateHandleInit(_) => "sha256_state_handle_init", + StarknetConcreteLibfunc::Sha256StateHandleDigest(_) => "sha256_state_handle_digest", + StarknetConcreteLibfunc::GetClassHashAt(_) => "get_class_hash_at", + StarknetConcreteLibfunc::MetaTxV0(_) => todo!(), + }, + CoreConcreteLibfunc::Debug(value) => match value { + DebugConcreteLibfunc::Print(_) => "debug_print", + }, + CoreConcreteLibfunc::SnapshotTake(_) => "snapshot_take", + CoreConcreteLibfunc::Bytes31(value) => match value { + Bytes31ConcreteLibfunc::Const(_) => "bytes31_const", + Bytes31ConcreteLibfunc::ToFelt252(_) => "bytes31_to_felt252", + Bytes31ConcreteLibfunc::TryFromFelt252(_) => "bytes31_try_from_felt252", + }, + CoreConcreteLibfunc::Circuit(selector) => match selector { + CircuitConcreteLibfunc::AddInput(_) => "circuit_add_input", + CircuitConcreteLibfunc::Eval(_) => "circuit_eval", + CircuitConcreteLibfunc::GetDescriptor(_) => "circuit_get_descriptor", + CircuitConcreteLibfunc::InitCircuitData(_) => "circuit_init_circuit_data", + CircuitConcreteLibfunc::GetOutput(_) => "circuit_get_output", + CircuitConcreteLibfunc::TryIntoCircuitModulus(_) => "circuit_try_into_circuit_modulus", + CircuitConcreteLibfunc::FailureGuaranteeVerify(_) => "circuit_failure_guarantee_verify", + CircuitConcreteLibfunc::IntoU96Guarantee(_) => "circuit_into_u96_guarantee", + CircuitConcreteLibfunc::U96GuaranteeVerify(_) => "circuit_u96_guarantee_verify", + CircuitConcreteLibfunc::U96LimbsLessThanGuaranteeVerify(_) => { + "circuit_u96_limbs_less_than_guarantee_verify" + } + CircuitConcreteLibfunc::U96SingleLimbLessThanGuaranteeVerify(_) => { + "circuit_u96_single_limb_less_than_guarantee_verify" + } + }, + CoreConcreteLibfunc::BoundedInt(selector) => match selector { + BoundedIntConcreteLibfunc::Add(_) => "bounded_int_add", + BoundedIntConcreteLibfunc::Sub(_) => "bounded_int_sub", + BoundedIntConcreteLibfunc::Mul(_) => "bounded_int_mul", + BoundedIntConcreteLibfunc::DivRem(_) => "bounded_int_div_rem", + BoundedIntConcreteLibfunc::Constrain(_) => "bounded_int_constrain", + BoundedIntConcreteLibfunc::IsZero(_) => "bounded_int_is_zero", + BoundedIntConcreteLibfunc::WrapNonZero(_) => "bounded_int_wrap_non_zero", + BoundedIntConcreteLibfunc::TrimMin(_) => "bounded_int_trim_min", + BoundedIntConcreteLibfunc::TrimMax(_) => "bounded_int_trim_max", + }, + CoreConcreteLibfunc::IntRange(selector) => match selector { + IntRangeConcreteLibfunc::TryNew(_) => "int_range_try_new", + IntRangeConcreteLibfunc::PopFront(_) => "int_range_pop_front", + }, + CoreConcreteLibfunc::Blake(_) => todo!(), + CoreConcreteLibfunc::Felt252SquashedDict(_) => todo!(), + CoreConcreteLibfunc::Trace(_) => todo!(), + CoreConcreteLibfunc::QM31(_) => todo!(), + } +} + +pub fn type_to_name( + ty_id: &ConcreteTypeId, + registry: &ProgramRegistry, +) -> String { + let ty = registry.get_type(ty_id).unwrap(); + match ty { + CoreTypeConcrete::Array(info) => { + format!("Array<{}>", type_to_name(&info.ty, registry)) + } + CoreTypeConcrete::Box(info) => { + format!("Box<{}>", type_to_name(&info.ty, registry)) + } + CoreTypeConcrete::Uninitialized(info) => { + format!("Uninitialized<{}>", type_to_name(&info.ty, registry)) + } + CoreTypeConcrete::NonZero(info) => { + format!("NonZero<{}>", type_to_name(&info.ty, registry)) + } + CoreTypeConcrete::Nullable(info) => { + format!("Nullable<{}>", type_to_name(&info.ty, registry)) + } + CoreTypeConcrete::Span(info) => { + format!("Span<{}>", type_to_name(&info.ty, registry)) + } + CoreTypeConcrete::Snapshot(info) => { + format!("Snapshot<{}>", type_to_name(&info.ty, registry)) + } + CoreTypeConcrete::Struct(info) => { + let fields = info + .members + .iter() + .map(|ty_id| type_to_name(ty_id, registry)) + .collect::>(); + let fields = fields.join(", "); + + format!("Struct<{}>", fields) + } + CoreTypeConcrete::Enum(info) => { + let fields = info + .variants + .iter() + .map(|ty_id| type_to_name(ty_id, registry)) + .collect::>(); + let fields = fields.join(", "); + + format!("Enum<{}>", fields) + } + CoreTypeConcrete::Felt252Dict(_) => String::from("Felt252Dict"), + CoreTypeConcrete::Felt252DictEntry(_) => String::from("Felt252DictEntry"), + CoreTypeConcrete::SquashedFelt252Dict(_) => String::from("SquashedFelt252Dict"), + CoreTypeConcrete::Starknet(selector) => match selector { + StarknetTypeConcrete::ClassHash(_) => String::from("Starknet::ClassHash"), + StarknetTypeConcrete::ContractAddress(_) => String::from("Starknet::ContractAddress"), + StarknetTypeConcrete::StorageBaseAddress(_) => { + String::from("Starknet::StorageBaseAddress") + } + StarknetTypeConcrete::StorageAddress(_) => String::from("Starknet::StorageAddress"), + StarknetTypeConcrete::System(_) => String::from("Starknet::System"), + StarknetTypeConcrete::Secp256Point(_) => String::from("Starknet::Secp256Point"), + StarknetTypeConcrete::Sha256StateHandle(_) => { + String::from("Starknet::Sha256StateHandle") + } + }, + + CoreTypeConcrete::Bitwise(_) => String::from("Bitwise"), + CoreTypeConcrete::Circuit(selector) => match selector { + CircuitTypeConcrete::AddMod(_) => String::from("AddMod"), + CircuitTypeConcrete::MulMod(_) => String::from("MulMod"), + CircuitTypeConcrete::AddModGate(_) => String::from("AddModGate"), + CircuitTypeConcrete::Circuit(_) => String::from("Circuit"), + CircuitTypeConcrete::CircuitData(_) => String::from("CircuitData"), + CircuitTypeConcrete::CircuitOutputs(_) => String::from("CircuitOutputs"), + CircuitTypeConcrete::CircuitPartialOutputs(_) => String::from("CircuitPartialOutputs"), + CircuitTypeConcrete::CircuitDescriptor(_) => String::from("CircuitDescriptor"), + CircuitTypeConcrete::CircuitFailureGuarantee(_) => { + String::from("CircuitFailureGuarantee") + } + CircuitTypeConcrete::CircuitInput(_) => String::from("CircuitInput"), + CircuitTypeConcrete::CircuitInputAccumulator(_) => { + String::from("CircuitInputAccumulator") + } + CircuitTypeConcrete::CircuitModulus(_) => String::from("CircuitModulus"), + CircuitTypeConcrete::InverseGate(_) => String::from("InverseGate"), + CircuitTypeConcrete::MulModGate(_) => String::from("MulModGate"), + CircuitTypeConcrete::SubModGate(_) => String::from("SubModGate"), + CircuitTypeConcrete::U96Guarantee(_) => String::from("U96Guarantee"), + CircuitTypeConcrete::U96LimbsLessThanGuarantee(_) => { + String::from("U96LimbsLessThanGuarantee") + } + }, + CoreTypeConcrete::Const(_) => String::from("Const"), + CoreTypeConcrete::Coupon(_) => String::from("Coupon"), + CoreTypeConcrete::EcOp(_) => String::from("EcOp"), + CoreTypeConcrete::EcPoint(_) => String::from("EcPoint"), + CoreTypeConcrete::EcState(_) => String::from("EcState"), + CoreTypeConcrete::Felt252(_) => String::from("Felt252"), + CoreTypeConcrete::GasBuiltin(_) => String::from("GasBuiltin"), + CoreTypeConcrete::BuiltinCosts(_) => String::from("BuiltinCosts"), + CoreTypeConcrete::Uint8(_) => String::from("Uint8"), + CoreTypeConcrete::Uint16(_) => String::from("Uint16"), + CoreTypeConcrete::Uint32(_) => String::from("Uint32"), + CoreTypeConcrete::Uint64(_) => String::from("Uint64"), + CoreTypeConcrete::Uint128(_) => String::from("Uint128"), + CoreTypeConcrete::Uint128MulGuarantee(_) => String::from("Uint128MulGuarantee"), + CoreTypeConcrete::Sint8(_) => String::from("Sint8"), + CoreTypeConcrete::Sint16(_) => String::from("Sint16"), + CoreTypeConcrete::Sint32(_) => String::from("Sint32"), + CoreTypeConcrete::Sint64(_) => String::from("Sint64"), + CoreTypeConcrete::Sint128(_) => String::from("Sint128"), + CoreTypeConcrete::RangeCheck(_) => String::from("RangeCheck"), + CoreTypeConcrete::RangeCheck96(_) => String::from("RangeCheck96"), + CoreTypeConcrete::Pedersen(_) => String::from("Pedersen"), + CoreTypeConcrete::Poseidon(_) => String::from("Poseidon"), + CoreTypeConcrete::SegmentArena(_) => String::from("SegmentArena"), + CoreTypeConcrete::Bytes31(_) => String::from("Bytes31"), + CoreTypeConcrete::BoundedInt(_) => String::from("BoundedInt"), + CoreTypeConcrete::IntRange(info) => { + format!("IntRange<{}>", type_to_name(&info.ty, registry)) + } + CoreTypeConcrete::Blake(_) => todo!(), + CoreTypeConcrete::QM31(_) => todo!(), + } +} + +/// prints all the signature information, used while debugging to learn +/// how to implement a certain libfunc. +#[allow(dead_code)] +pub fn debug_signature( + registry: &ProgramRegistry, + params: &[ParamSignature], + branches: &[BranchSignature], + args: &[Value], +) { + println!( + "Params: {:#?}", + params + .iter() + .map(|p| type_to_name(&p.ty, registry)) + .collect::>() + ); + println!( + "Branches: {:#?}", + branches + .iter() + .map(|b| { + b.vars + .iter() + .map(|vars| type_to_name(&vars.ty, registry)) + .collect::>() + }) + .collect::>() + ); + println!("Args: {:#?}", args); +} diff --git a/debug_utils/sierra-emu/src/dump.rs b/debug_utils/sierra-emu/src/dump.rs new file mode 100644 index 0000000000..f8e5dbba75 --- /dev/null +++ b/debug_utils/sierra-emu/src/dump.rs @@ -0,0 +1,127 @@ +use crate::value::Value; +use cairo_lang_sierra::{ids::VarId, program::StatementIdx}; +use cairo_lang_utils::ordered_hash_map::OrderedHashMap; +use serde::{ser::SerializeMap, Serialize}; +use starknet_crypto::Felt; +use std::collections::BTreeMap; + +#[derive(Clone, Debug, Default, Serialize)] +pub struct ProgramTrace { + pub states: Vec, + // TODO: Syscall data. +} + +impl ProgramTrace { + pub fn new() -> Self { + Self { states: Vec::new() } + } + + pub fn push(&mut self, state: StateDump) { + self.states.push(state); + } +} + +#[derive(Clone, Debug)] +pub struct StateDump { + pub statement_idx: StatementIdx, + pub items: BTreeMap, +} + +impl StateDump { + pub fn new(statement_idx: StatementIdx, state: OrderedHashMap) -> Self { + Self { + statement_idx, + items: state + .into_iter() + .map(|(id, value)| (id.id, value)) + .collect(), + } + } +} + +impl Serialize for StateDump { + fn serialize(&self, s: S) -> Result + where + S: serde::Serializer, + { + let mut s = s.serialize_map(Some(2))?; + + s.serialize_entry("statementIdx", &self.statement_idx.0)?; + s.serialize_entry("preStateDump", &self.items)?; + + s.end() + } +} + +#[derive(Debug, Clone)] +pub struct ContractExecutionResult { + pub remaining_gas: u64, + pub failure_flag: bool, + pub return_values: Vec, + pub error_msg: Option, +} + +impl ContractExecutionResult { + pub fn from_trace(trace: &ProgramTrace) -> Option { + let last = trace.states.last()?; + Self::from_state(last) + } + + pub fn from_state(state: &StateDump) -> Option { + let mut remaining_gas = None; + let mut error_msg = None; + let mut failure_flag = false; + let mut return_values = Vec::new(); + + for value in state.items.values() { + match value { + Value::U64(gas) => remaining_gas = Some(*gas), + Value::Enum { + self_ty: _, + index, + payload, + } => { + failure_flag = (*index) != 0; + + if let Value::Struct(inner) = &**payload { + if !failure_flag { + if let Value::Struct(inner) = &inner[0] { + if let Value::Array { ty: _, data } = &inner[0] { + for value in data.iter() { + if let Value::Felt(x) = value { + return_values.push(*x); + } + } + } + } + } else if let Value::Array { ty: _, data } = &inner[1] { + let mut error_felt_vec = Vec::new(); + for value in data.iter() { + if let Value::Felt(x) = value { + error_felt_vec.push(*x); + } + } + let bytes_err: Vec<_> = error_felt_vec + .iter() + .flat_map(|felt| felt.to_bytes_be().to_vec()) + // remove null chars + .filter(|b| *b != 0) + .collect(); + let str_error = String::from_utf8(bytes_err).unwrap().to_owned(); + error_msg = Some(str_error); + } + } + } + Value::Unit => {} + _ => None?, + } + } + + Some(Self { + remaining_gas: remaining_gas.unwrap_or(0), + return_values, + error_msg, + failure_flag, + }) + } +} diff --git a/debug_utils/sierra-emu/src/gas.rs b/debug_utils/sierra-emu/src/gas.rs new file mode 100644 index 0000000000..4dc78b6a8c --- /dev/null +++ b/debug_utils/sierra-emu/src/gas.rs @@ -0,0 +1,242 @@ +use cairo_lang_runner::token_gas_cost; +use cairo_lang_sierra::{ + extensions::gas::CostTokenType, + ids::FunctionId, + program::{Program, StatementIdx}, +}; +use cairo_lang_sierra_ap_change::{ + ap_change_info::ApChangeInfo, calc_ap_changes, + compute::calc_ap_changes as linear_calc_ap_changes, ApChangeError, +}; +use cairo_lang_sierra_gas::{ + compute_postcost_info, compute_precost_info, gas_info::GasInfo, CostError, +}; +use cairo_lang_utils::{casts::IntoOrPanic, ordered_hash_map::OrderedHashMap}; +use serde::Serialize; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize)] +pub struct BuiltinCosts { + pub r#const: u64, + pub pedersen: u64, + pub bitwise: u64, + pub ecop: u64, + pub poseidon: u64, + pub add_mod: u64, + pub mul_mod: u64, +} + +impl Default for BuiltinCosts { + fn default() -> Self { + Self { + r#const: token_gas_cost(CostTokenType::Const) as u64, + pedersen: token_gas_cost(CostTokenType::Pedersen) as u64, + bitwise: token_gas_cost(CostTokenType::Bitwise) as u64, + ecop: token_gas_cost(CostTokenType::EcOp) as u64, + poseidon: token_gas_cost(CostTokenType::Poseidon) as u64, + add_mod: token_gas_cost(CostTokenType::AddMod) as u64, + mul_mod: token_gas_cost(CostTokenType::MulMod) as u64, + } + } +} + +impl From for [u64; 7] { + // Order matters, for the libfunc impl + // https://github.com/starkware-libs/sequencer/blob/1b7252f8a30244d39614d7666aa113b81291808e/crates/blockifier/src/execution/entry_point_execution.rs#L208 + fn from(value: BuiltinCosts) -> Self { + [ + value.r#const, + value.pedersen, + value.bitwise, + value.ecop, + value.poseidon, + value.add_mod, + value.mul_mod, + ] + } +} + +/// Holds global gas info. +#[derive(Debug, Default)] +pub struct GasMetadata { + pub ap_change_info: ApChangeInfo, + pub gas_info: GasInfo, +} + +/// Configuration for metadata computation. +#[derive(Debug, Clone)] +pub struct MetadataComputationConfig { + pub function_set_costs: OrderedHashMap>, + // ignored, its always used + pub linear_gas_solver: bool, + pub linear_ap_change_solver: bool, +} + +impl Default for MetadataComputationConfig { + fn default() -> Self { + Self { + function_set_costs: Default::default(), + linear_gas_solver: true, + linear_ap_change_solver: true, + } + } +} + +/// Error for metadata calculations. +#[derive(Debug, thiserror::Error, Eq, PartialEq)] +pub enum GasMetadataError { + #[error(transparent)] + ApChangeError(#[from] ApChangeError), + #[error(transparent)] + CostError(#[from] CostError), + #[error("Not enough gas to run the operation. Required: {:?}, Available: {:?}.", gas.0, gas.1)] + NotEnoughGas { gas: Box<(u64, u64)> }, +} + +impl GasMetadata { + pub fn new( + sierra_program: &Program, + config: Option, + ) -> Result { + if let Some(metadata_config) = config { + calc_metadata(sierra_program, metadata_config) + } else { + calc_metadata_ap_change_only(sierra_program) + } + } + + /// Returns the initial value for the gas counter. + /// If `available_gas` is None returns 0. + pub fn get_initial_available_gas( + &self, + func: &FunctionId, + available_gas: Option, + ) -> Result { + let Some(available_gas) = available_gas else { + return Ok(0); + }; + + // In case we don't have any costs - it means no gas equations were solved (and we are in + // the case of no gas checking enabled) - so the gas builtin is irrelevant, and we + // can return any value. + let Some(required_gas) = self.initial_required_gas(func) else { + return Ok(0); + }; + + available_gas + .checked_sub(required_gas) + .ok_or(GasMetadataError::NotEnoughGas { + gas: Box::new((required_gas, available_gas)), + }) + } + + pub fn initial_required_gas(&self, func: &FunctionId) -> Option { + if self.gas_info.function_costs.is_empty() { + return None; + } + Some( + self.gas_info.function_costs[func] + .iter() + .map(|(token_type, val)| val.into_or_panic::() * token_gas_cost(*token_type)) + .sum::() as u64, + ) + } + + pub fn get_gas_costs_for_statement(&self, idx: StatementIdx) -> Vec<(u64, CostTokenType)> { + let mut costs = Vec::new(); + for cost_type in CostTokenType::iter_casm_tokens() { + if let Some(cost_count) = + self.get_gas_cost_for_statement_and_cost_token_type(idx, *cost_type) + { + if cost_count > 0 { + costs.push((cost_count, *cost_type)); + } + } + } + costs + } + + pub fn get_gas_cost_for_statement_and_cost_token_type( + &self, + idx: StatementIdx, + cost_type: CostTokenType, + ) -> Option { + self.gas_info + .variable_values + .get(&(idx, cost_type)) + .copied() + .map(|x| { + x.try_into() + .expect("gas cost couldn't be converted to u128, should never happen") + }) + } +} + +impl Clone for GasMetadata { + fn clone(&self) -> Self { + Self { + ap_change_info: ApChangeInfo { + variable_values: self.ap_change_info.variable_values.clone(), + function_ap_change: self.ap_change_info.function_ap_change.clone(), + }, + gas_info: GasInfo { + variable_values: self.gas_info.variable_values.clone(), + function_costs: self.gas_info.function_costs.clone(), + }, + } + } +} + +// Methods from https://github.com/starkware-libs/cairo/blob/fbdbbe4c42a6808eccbff8436078f73d0710c772/crates/cairo-lang-sierra-to-casm/src/metadata.rs#L71 + +/// Calculates the metadata for a Sierra program, with ap change info only. +fn calc_metadata_ap_change_only(program: &Program) -> Result { + Ok(GasMetadata { + ap_change_info: calc_ap_changes(program, |_, _| 0)?, + gas_info: GasInfo { + variable_values: Default::default(), + function_costs: Default::default(), + }, + }) +} + +/// Calculates the metadata for a Sierra program. +/// +/// `no_eq_solver` uses a linear-time algorithm for calculating the gas, instead of solving +/// equations. +fn calc_metadata( + program: &Program, + config: MetadataComputationConfig, +) -> Result { + let pre_gas_info = compute_precost_info(program)?; + + let ap_change_info = if config.linear_ap_change_solver { + linear_calc_ap_changes + } else { + calc_ap_changes + }(program, |idx, token_type| { + pre_gas_info.variable_values[&(idx, token_type)] as usize + })?; + + let enforced_function_costs: OrderedHashMap = config + .function_set_costs + .iter() + .map(|(func, costs)| (func.clone(), costs[&CostTokenType::Const])) + .collect(); + let post_gas_info = compute_postcost_info( + program, + &|idx| { + ap_change_info + .variable_values + .get(idx) + .copied() + .unwrap_or_default() + }, + &pre_gas_info, + &enforced_function_costs, + )?; + + Ok(GasMetadata { + ap_change_info, + gas_info: pre_gas_info.combine(post_gas_info), + }) +} diff --git a/debug_utils/sierra-emu/src/lib.rs b/debug_utils/sierra-emu/src/lib.rs new file mode 100644 index 0000000000..7c5049bdbd --- /dev/null +++ b/debug_utils/sierra-emu/src/lib.rs @@ -0,0 +1,60 @@ +use cairo_lang_sierra::{ + extensions::core::{CoreLibfunc, CoreType}, + ids::ConcreteTypeId, + program::{GenFunction, Program, StatementIdx}, + program_registry::ProgramRegistry, +}; + +pub use self::{dump::*, gas::BuiltinCosts, value::*, vm::VirtualMachine}; + +mod debug; +mod dump; +mod gas; +pub mod starknet; +mod test_utils; +mod value; +mod vm; + +pub fn find_entry_point_by_idx( + program: &Program, + entry_point_idx: usize, +) -> Option<&GenFunction> { + program + .funcs + .iter() + .find(|x| x.id.id == entry_point_idx as u64) +} + +pub fn find_entry_point_by_name<'a>( + program: &'a Program, + name: &str, +) -> Option<&'a GenFunction> { + program + .funcs + .iter() + .find(|x| x.id.debug_name.as_ref().map(|x| x.as_str()) == Some(name)) +} + +// If type is invisible to sierra (i.e. a single element container), +// finds it's actual concrete type recursively. +// If not, returns the current type +pub fn find_real_type( + registry: &ProgramRegistry, + ty: &ConcreteTypeId, +) -> ConcreteTypeId { + match registry.get_type(ty).unwrap() { + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Box(info) => { + find_real_type(registry, &info.ty) + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Uninitialized(info) => { + find_real_type(registry, &info.ty) + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Span(info) => { + find_real_type(registry, &info.ty) + } + cairo_lang_sierra::extensions::core::CoreTypeConcrete::Snapshot(info) => { + find_real_type(registry, &info.ty) + } + _ => ty.clone(), + } +} diff --git a/debug_utils/sierra-emu/src/main.rs b/debug_utils/sierra-emu/src/main.rs new file mode 100644 index 0000000000..d8508928d8 --- /dev/null +++ b/debug_utils/sierra-emu/src/main.rs @@ -0,0 +1,206 @@ +use self::args::CmdArgs; +use cairo_lang_sierra::{ + extensions::{ + circuit::CircuitTypeConcrete, core::CoreTypeConcrete, starknet::StarknetTypeConcrete, + }, + ProgramParser, +}; +use clap::Parser; +use sierra_emu::{starknet::StubSyscallHandler, Value, VirtualMachine}; +use std::{ + fs::{self, File}, + io::stdout, + sync::Arc, +}; +use tracing::{debug, info, Level}; +use tracing_subscriber::{EnvFilter, FmtSubscriber}; + +mod args; + +fn main() -> Result<(), Box> { + let args = CmdArgs::parse(); + + tracing::subscriber::set_global_default( + FmtSubscriber::builder() + .with_env_filter(EnvFilter::from_default_env()) + .with_max_level(Level::TRACE) + .finish(), + )?; + + info!("Loading the Sierra program from disk."); + let source_code = fs::read_to_string(args.program)?; + + info!("Parsing the Sierra program."); + let program = Arc::new( + ProgramParser::new() + .parse(&source_code) + .map_err(|e| e.to_string())?, + ); + + info!("Preparing the virtual machine."); + let mut vm = VirtualMachine::new(program.clone()); + + debug!("Pushing the entry point's frame."); + let function = program + .funcs + .iter() + .find(|f| match &args.entry_point { + args::EntryPoint::Number(x) => f.id.id == *x, + args::EntryPoint::String(x) => f.id.debug_name.as_deref() == Some(x.as_str()), + }) + .unwrap(); + + debug!( + "Entry point argument types: {:?}", + function.signature.param_types + ); + let mut iter = args.args.into_iter(); + vm.push_frame( + function.id.clone(), + function + .signature + .param_types + .iter() + .map(|type_id| { + let type_info = vm.registry().get_type(type_id).unwrap(); + match type_info { + CoreTypeConcrete::Felt252(_) => Value::parse_felt(&iter.next().unwrap()), + CoreTypeConcrete::GasBuiltin(_) => Value::U64(args.available_gas.unwrap()), + CoreTypeConcrete::RangeCheck(_) + | CoreTypeConcrete::RangeCheck96(_) + | CoreTypeConcrete::Bitwise(_) + | CoreTypeConcrete::Pedersen(_) + | CoreTypeConcrete::Poseidon(_) + | CoreTypeConcrete::SegmentArena(_) + | CoreTypeConcrete::Circuit( + CircuitTypeConcrete::AddMod(_) | CircuitTypeConcrete::MulMod(_), + ) => Value::Unit, + CoreTypeConcrete::Starknet(inner) => match inner { + StarknetTypeConcrete::System(_) => Value::Unit, + _ => todo!(), + }, + _ => todo!(), + } + }) + .collect::>(), + ); + + info!("Running the program."); + let syscall_handler = &mut StubSyscallHandler::default(); + let trace = vm.run_with_trace(syscall_handler); + + match args.output { + Some(path) => serde_json::to_writer(File::create(path)?, &trace)?, + None => serde_json::to_writer(stdout().lock(), &trace)?, + }; + + Ok(()) +} + +#[cfg(test)] +mod test { + use std::path::Path; + + use cairo_lang_compiler::CompilerConfig; + use cairo_lang_starknet::compile::compile_path; + use cairo_lang_starknet_classes::contract_class::version_id_from_serialized_sierra_program; + use sierra_emu::{starknet::StubSyscallHandler, ContractExecutionResult, VirtualMachine}; + + #[test] + fn test_contract() { + let path = Path::new("programs/hello_starknet.cairo"); + + let contract = compile_path( + path, + None, + CompilerConfig { + replace_ids: true, + ..Default::default() + }, + ) + .unwrap(); + + let sierra_program = contract.extract_sierra_program().unwrap(); + + let (sierra_version, _) = + version_id_from_serialized_sierra_program(&contract.sierra_program).unwrap(); + + let entry_point = contract.entry_points_by_type.external.first().unwrap(); + + let mut vm = VirtualMachine::new_starknet( + sierra_program.clone().into(), + &contract.entry_points_by_type, + sierra_version, + ); + + let calldata = [2.into()]; + let initial_gas = 1000000; + + let syscall_handler = &mut StubSyscallHandler::default(); + + // Set the VM at the contract entrypoint + vm.call_contract( + entry_point.selector.clone().into(), + initial_gas, + calldata, + None, + ); + + // Run all the steps generating a program execution trace. (Not to be confused with a proof trace) + let _trace = vm.run_with_trace(syscall_handler); + + // let trace_str = serde_json::to_string_pretty(&trace).unwrap(); + // std::fs::write("contract_trace.json", trace_str).unwrap(); + } + + #[test] + fn test_contract_constructor() { + let path = Path::new("programs/hello_starknet.cairo"); + + let contract = compile_path( + path, + None, + CompilerConfig { + replace_ids: true, + ..Default::default() + }, + ) + .unwrap(); + + let (sierra_version, _) = + version_id_from_serialized_sierra_program(&contract.sierra_program).unwrap(); + + let sierra_program = contract.extract_sierra_program().unwrap(); + + let entry_point = contract.entry_points_by_type.constructor.first().unwrap(); + + let mut vm = VirtualMachine::new_starknet( + sierra_program.clone().into(), + &contract.entry_points_by_type, + sierra_version, + ); + + let calldata = [2.into()]; + let initial_gas = 1000000; + + let syscall_handler = &mut StubSyscallHandler::default(); + vm.call_contract( + entry_point.selector.clone().into(), + initial_gas, + calldata, + None, + ); + + let trace = vm.run_with_trace(syscall_handler); + + assert!(!syscall_handler.storage.is_empty()); + + let result = ContractExecutionResult::from_trace(&trace).unwrap(); + assert!(!result.failure_flag); + assert_eq!(result.return_values.len(), 0); + assert_eq!(result.error_msg, None); + + // let trace_str = serde_json::to_string_pretty(&trace).unwrap(); + // std::fs::write("contract_trace.json", trace_str).unwrap(); + } +} diff --git a/debug_utils/sierra-emu/src/starknet.rs b/debug_utils/sierra-emu/src/starknet.rs new file mode 100644 index 0000000000..1673eab287 --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet.rs @@ -0,0 +1,851 @@ +use std::{ + collections::{BTreeMap, VecDeque}, + iter::once, +}; + +pub use self::{ + block_info::BlockInfo, execution_info::ExecutionInfo, execution_info_v2::ExecutionInfoV2, + resource_bounds::ResourceBounds, secp256k1_point::Secp256k1Point, + secp256r1_point::Secp256r1Point, tx_info::TxInfo, tx_v2_info::TxV2Info, u256::U256, +}; +use k256::elliptic_curve::{ + generic_array::GenericArray, + sec1::{FromEncodedPoint, ToEncodedPoint}, +}; +use sec1::point::Coordinates; +use serde::Serialize; +use starknet_types_core::felt::Felt; + +mod block_info; +mod execution_info; +mod execution_info_v2; +mod resource_bounds; +mod secp256k1_point; +mod secp256r1_point; +mod tx_info; +mod tx_v2_info; +mod u256; + +pub type SyscallResult = Result>; + +pub trait StarknetSyscallHandler { + fn get_block_hash(&mut self, block_number: u64, remaining_gas: &mut u64) + -> SyscallResult; + + fn get_execution_info(&mut self, remaining_gas: &mut u64) -> SyscallResult; + + fn get_execution_info_v2(&mut self, remaining_gas: &mut u64) -> SyscallResult; + + fn deploy( + &mut self, + class_hash: Felt, + contract_address_salt: Felt, + calldata: Vec, + deploy_from_zero: bool, + remaining_gas: &mut u64, + ) -> SyscallResult<(Felt, Vec)>; + + fn replace_class(&mut self, class_hash: Felt, remaining_gas: &mut u64) -> SyscallResult<()>; + + fn library_call( + &mut self, + class_hash: Felt, + function_selector: Felt, + calldata: Vec, + remaining_gas: &mut u64, + ) -> SyscallResult>; + + fn call_contract( + &mut self, + address: Felt, + entry_point_selector: Felt, + calldata: Vec, + remaining_gas: &mut u64, + ) -> SyscallResult>; + + fn storage_read( + &mut self, + address_domain: u32, + address: Felt, + remaining_gas: &mut u64, + ) -> SyscallResult; + + fn storage_write( + &mut self, + address_domain: u32, + address: Felt, + value: Felt, + remaining_gas: &mut u64, + ) -> SyscallResult<()>; + + fn emit_event( + &mut self, + keys: Vec, + data: Vec, + remaining_gas: &mut u64, + ) -> SyscallResult<()>; + + fn send_message_to_l1( + &mut self, + to_address: Felt, + payload: Vec, + remaining_gas: &mut u64, + ) -> SyscallResult<()>; + + fn keccak(&mut self, input: Vec, remaining_gas: &mut u64) -> SyscallResult; + + fn secp256k1_new( + &mut self, + x: U256, + y: U256, + remaining_gas: &mut u64, + ) -> SyscallResult>; + + fn secp256k1_add( + &mut self, + p0: Secp256k1Point, + p1: Secp256k1Point, + remaining_gas: &mut u64, + ) -> SyscallResult; + + fn secp256k1_mul( + &mut self, + p: Secp256k1Point, + m: U256, + remaining_gas: &mut u64, + ) -> SyscallResult; + + fn secp256k1_get_point_from_x( + &mut self, + x: U256, + y_parity: bool, + remaining_gas: &mut u64, + ) -> SyscallResult>; + + fn secp256k1_get_xy( + &mut self, + p: Secp256k1Point, + remaining_gas: &mut u64, + ) -> SyscallResult<(U256, U256)>; + + fn secp256r1_new( + &mut self, + x: U256, + y: U256, + remaining_gas: &mut u64, + ) -> SyscallResult>; + + fn secp256r1_add( + &mut self, + p0: Secp256r1Point, + p1: Secp256r1Point, + remaining_gas: &mut u64, + ) -> SyscallResult; + + fn secp256r1_mul( + &mut self, + p: Secp256r1Point, + m: U256, + remaining_gas: &mut u64, + ) -> SyscallResult; + + fn secp256r1_get_point_from_x( + &mut self, + x: U256, + y_parity: bool, + remaining_gas: &mut u64, + ) -> SyscallResult>; + + fn secp256r1_get_xy( + &mut self, + p: Secp256r1Point, + remaining_gas: &mut u64, + ) -> SyscallResult<(U256, U256)>; + + fn sha256_process_block( + &mut self, + prev_state: [u32; 8], + current_block: [u32; 16], + remaining_gas: &mut u64, + ) -> SyscallResult<[u32; 8]>; + + fn cheatcode(&mut self, _selector: Felt, _input: Vec) -> Vec { + unimplemented!() + } +} + +/// A (somewhat) usable implementation of the starknet syscall handler trait. +/// +/// Currently gas is not deducted. +#[derive(Debug, PartialEq, Eq, Clone, Serialize)] +pub struct StubSyscallHandler { + pub storage: BTreeMap<(u32, Felt), Felt>, + pub events: Vec, + pub execution_info: ExecutionInfoV2, + pub logs: BTreeMap, +} + +/// Event emitted by the emit_event syscall. +#[derive(Debug, PartialEq, Eq, Clone, Serialize)] +pub struct StubEvent { + pub keys: Vec, + pub data: Vec, +} + +#[derive(Debug, PartialEq, Eq, Clone, Serialize)] +pub struct ContractLogs { + pub events: VecDeque, + pub l2_to_l1_messages: VecDeque, +} + +type L2ToL1Message = (Felt, Vec); + +impl Default for StubSyscallHandler { + fn default() -> Self { + Self { + storage: BTreeMap::new(), + events: Vec::new(), + execution_info: ExecutionInfoV2 { + block_info: BlockInfo { + block_number: 0, + block_timestamp: 0, + sequencer_address: 666.into(), + }, + tx_info: TxV2Info { + version: 1.into(), + account_contract_address: 1.into(), + max_fee: 0, + signature: vec![1.into()], + transaction_hash: 1.into(), + chain_id: 1.into(), + nonce: 0.into(), + resource_bounds: vec![], + tip: 0, + paymaster_data: vec![], + nonce_data_availability_mode: 0, + fee_data_availability_mode: 0, + account_deployment_data: vec![], + }, + caller_address: 2.into(), + contract_address: 3.into(), + entry_point_selector: 4.into(), + }, + logs: BTreeMap::new(), + } + } +} + +impl StarknetSyscallHandler for StubSyscallHandler { + fn get_block_hash( + &mut self, + block_number: u64, + _remaining_gas: &mut u64, + ) -> SyscallResult { + Ok(block_number.into()) + } + + fn get_execution_info(&mut self, _remaining_gas: &mut u64) -> SyscallResult { + Ok(ExecutionInfo { + block_info: self.execution_info.block_info, + tx_info: TxInfo { + version: self.execution_info.tx_info.version, + account_contract_address: self.execution_info.tx_info.account_contract_address, + max_fee: self.execution_info.tx_info.max_fee, + signature: self.execution_info.tx_info.signature.clone(), + transaction_hash: self.execution_info.tx_info.transaction_hash, + chain_id: self.execution_info.tx_info.chain_id, + nonce: self.execution_info.tx_info.nonce, + }, + caller_address: self.execution_info.caller_address, + contract_address: self.execution_info.contract_address, + entry_point_selector: self.execution_info.entry_point_selector, + }) + } + + fn get_execution_info_v2( + &mut self, + _remaining_gas: &mut u64, + ) -> SyscallResult { + Ok(self.execution_info.clone()) + } + + fn deploy( + &mut self, + _class_hash: Felt, + _contract_address_salt: Felt, + _calldata: Vec, + _deploy_from_zero: bool, + _remaining_gas: &mut u64, + ) -> SyscallResult<(Felt, Vec)> { + unimplemented!() + } + + fn replace_class(&mut self, _class_hash: Felt, _remaining_gas: &mut u64) -> SyscallResult<()> { + unimplemented!() + } + + fn library_call( + &mut self, + _class_hash: Felt, + _function_selector: Felt, + _calldata: Vec, + _remaining_gas: &mut u64, + ) -> SyscallResult> { + unimplemented!() + } + + fn call_contract( + &mut self, + _address: Felt, + _entry_point_selector: Felt, + _calldata: Vec, + _remaining_gas: &mut u64, + ) -> SyscallResult> { + unimplemented!() + } + + fn storage_read( + &mut self, + address_domain: u32, + address: Felt, + _remaining_gas: &mut u64, + ) -> SyscallResult { + if let Some(value) = self.storage.get(&(address_domain, address)) { + Ok(*value) + } else { + Err(vec![Felt::from_bytes_be_slice(b"address not found")]) + } + } + + fn storage_write( + &mut self, + address_domain: u32, + address: Felt, + value: Felt, + _remaining_gas: &mut u64, + ) -> SyscallResult<()> { + self.storage.insert((address_domain, address), value); + Ok(()) + } + + fn emit_event( + &mut self, + keys: Vec, + data: Vec, + _remaining_gas: &mut u64, + ) -> SyscallResult<()> { + self.events.push(StubEvent { + keys: keys.to_vec(), + data: data.to_vec(), + }); + Ok(()) + } + + fn send_message_to_l1( + &mut self, + _to_address: Felt, + _payload: Vec, + _remaining_gas: &mut u64, + ) -> SyscallResult<()> { + unimplemented!() + } + + fn keccak(&mut self, input: Vec, gas: &mut u64) -> SyscallResult { + let length = input.len(); + + if length % 17 != 0 { + let error_msg = b"Invalid keccak input size"; + let felt_error = Felt::from_bytes_be_slice(error_msg); + return Err(vec![felt_error]); + } + + let n_chunks = length / 17; + let mut state = [0u64; 25]; + + const KECCAK_ROUND_COST: u64 = 180000; + for i in 0..n_chunks { + if *gas < KECCAK_ROUND_COST { + let error_msg = b"Syscall out of gas"; + let felt_error = Felt::from_bytes_be_slice(error_msg); + return Err(vec![felt_error]); + } + + *gas -= KECCAK_ROUND_COST; + let chunk = &input[i * 17..(i + 1) * 17]; //(request.input_start + i * 17)?; + for (i, val) in chunk.iter().enumerate() { + state[i] ^= val; + } + keccak::f1600(&mut state) + } + + // state[0] and state[1] conform the hash_high (u128) + // state[2] and state[3] conform the hash_low (u128) + SyscallResult::Ok(U256 { + lo: state[0] as u128 | ((state[1] as u128) << 64), + hi: state[2] as u128 | ((state[3] as u128) << 64), + }) + } + + fn secp256k1_new( + &mut self, + x: U256, + y: U256, + _remaining_gas: &mut u64, + ) -> SyscallResult> { + // The following unwraps should be unreachable because the iterator we provide has the + // expected number of bytes. + let point = k256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + x.hi.to_be_bytes().into_iter().chain(x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + y.hi.to_be_bytes().into_iter().chain(y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ); + + if bool::from(point.is_some()) { + Ok(Some(Secp256k1Point { x, y })) + } else { + Ok(None) + } + } + + fn secp256k1_add( + &mut self, + p0: Secp256k1Point, + p1: Secp256k1Point, + _remaining_gas: &mut u64, + ) -> SyscallResult { + // The inner unwraps should be unreachable because the iterator we provide has the expected + // number of bytes. The outer unwraps depend on the felt values, which should be valid since + // they'll be provided by secp256 syscalls. + let p0 = k256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p0.x.hi + .to_be_bytes() + .into_iter() + .chain(p0.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p0.y.hi + .to_be_bytes() + .into_iter() + .chain(p0.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + let p1 = k256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p1.x.hi + .to_be_bytes() + .into_iter() + .chain(p1.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p1.y.hi + .to_be_bytes() + .into_iter() + .chain(p1.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + + let p = p0 + p1; + + let p = p.to_encoded_point(false); + let (x, y) = match p.coordinates() { + Coordinates::Uncompressed { x, y } => (x, y), + _ => { + // This should be unreachable because we explicitly asked for the uncompressed + // encoding. + unreachable!() + } + }; + + // The following two unwraps should be safe because the array always has 32 bytes. The other + // four are definitely safe because the slicing guarantees its length to be the right one. + let x: [u8; 32] = x.as_slice().try_into().unwrap(); + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Secp256k1Point { + x: U256 { + hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), + }, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + }) + } + + fn secp256k1_mul( + &mut self, + p: Secp256k1Point, + m: U256, + _remaining_gas: &mut u64, + ) -> SyscallResult { + // The inner unwrap should be unreachable because the iterator we provide has the expected + // number of bytes. The outer unwrap depends on the felt values, which should be valid since + // they'll be provided by secp256 syscalls. + let p = k256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p.x.hi.to_be_bytes().into_iter().chain(p.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p.y.hi.to_be_bytes().into_iter().chain(p.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + let m: k256::Scalar = k256::elliptic_curve::ScalarPrimitive::from_slice(&{ + let mut buf = [0u8; 32]; + buf[0..16].copy_from_slice(&m.hi.to_be_bytes()); + buf[16..32].copy_from_slice(&m.lo.to_be_bytes()); + buf + }) + .map_err(|_| { + vec![Felt::from_bytes_be( + b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0invalid scalar", + )] + })? + .into(); + + let p = p * m; + + let p = p.to_encoded_point(false); + let (x, y) = match p.coordinates() { + Coordinates::Uncompressed { x, y } => (x, y), + _ => { + // This should be unreachable because we explicitly asked for the uncompressed + // encoding. + unreachable!() + } + }; + + // The following two unwraps should be safe because the array always has 32 bytes. The other + // four are definitely safe because the slicing guarantees its length to be the right one. + let x: [u8; 32] = x.as_slice().try_into().unwrap(); + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Secp256k1Point { + x: U256 { + hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), + }, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + }) + } + + fn secp256k1_get_point_from_x( + &mut self, + x: U256, + y_parity: bool, + _remaining_gas: &mut u64, + ) -> SyscallResult> { + // The inner unwrap should be unreachable because the iterator we provide has the expected + // number of bytes. The outer unwrap depends on the encoding format, which should be valid + // since it's hardcoded.. + let point = k256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_bytes( + k256::CompressedPoint::from_exact_iter( + once(0x02 | y_parity as u8) + .chain(x.hi.to_be_bytes()) + .chain(x.lo.to_be_bytes()), + ) + .unwrap(), + ) + .unwrap(), + ); + + if bool::from(point.is_some()) { + // This unwrap has already been checked in the `if` expression's condition. + let p = point.unwrap(); + + let p = p.to_encoded_point(false); + let y = match p.coordinates() { + Coordinates::Uncompressed { y, .. } => y, + _ => { + // This should be unreachable because we explicitly asked for the uncompressed + // encoding. + unreachable!() + } + }; + + // The following unwrap should be safe because the array always has 32 bytes. The other + // two are definitely safe because the slicing guarantees its length to be the right + // one. + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Some(Secp256k1Point { + x, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + })) + } else { + Ok(None) + } + } + + fn secp256k1_get_xy( + &mut self, + p: Secp256k1Point, + _remaining_gas: &mut u64, + ) -> SyscallResult<(U256, U256)> { + Ok((p.x, p.y)) + } + + fn secp256r1_new( + &mut self, + x: U256, + y: U256, + _remaining_gas: &mut u64, + ) -> SyscallResult> { + // The following unwraps should be unreachable because the iterator we provide has the + // expected number of bytes. + let point = p256::ProjectivePoint::from_encoded_point( + &k256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + x.hi.to_be_bytes().into_iter().chain(x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + y.hi.to_be_bytes().into_iter().chain(y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ); + + if bool::from(point.is_some()) { + Ok(Some(Secp256r1Point { x, y })) + } else { + Ok(None) + } + } + + fn secp256r1_add( + &mut self, + p0: Secp256r1Point, + p1: Secp256r1Point, + _remaining_gas: &mut u64, + ) -> SyscallResult { + // The inner unwraps should be unreachable because the iterator we provide has the expected + // number of bytes. The outer unwraps depend on the felt values, which should be valid since + // they'll be provided by secp256 syscalls. + let p0 = p256::ProjectivePoint::from_encoded_point( + &p256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p0.x.hi + .to_be_bytes() + .into_iter() + .chain(p0.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p0.y.hi + .to_be_bytes() + .into_iter() + .chain(p0.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + let p1 = p256::ProjectivePoint::from_encoded_point( + &p256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p1.x.hi + .to_be_bytes() + .into_iter() + .chain(p1.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p1.y.hi + .to_be_bytes() + .into_iter() + .chain(p1.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + + let p = p0 + p1; + + let p = p.to_encoded_point(false); + let (x, y) = match p.coordinates() { + Coordinates::Uncompressed { x, y } => (x, y), + _ => { + // This should be unreachable because we explicitly asked for the uncompressed + // encoding. + unreachable!() + } + }; + + // The following two unwraps should be safe because the array always has 32 bytes. The other + // four are definitely safe because the slicing guarantees its length to be the right one. + let x: [u8; 32] = x.as_slice().try_into().unwrap(); + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Secp256r1Point { + x: U256 { + hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), + }, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + }) + } + + fn secp256r1_mul( + &mut self, + p: Secp256r1Point, + m: U256, + _remaining_gas: &mut u64, + ) -> SyscallResult { + // The inner unwrap should be unreachable because the iterator we provide has the expected + // number of bytes. The outer unwrap depends on the felt values, which should be valid since + // they'll be provided by secp256 syscalls. + let p = p256::ProjectivePoint::from_encoded_point( + &p256::EncodedPoint::from_affine_coordinates( + &GenericArray::from_exact_iter( + p.x.hi.to_be_bytes().into_iter().chain(p.x.lo.to_be_bytes()), + ) + .unwrap(), + &GenericArray::from_exact_iter( + p.y.hi.to_be_bytes().into_iter().chain(p.y.lo.to_be_bytes()), + ) + .unwrap(), + false, + ), + ) + .unwrap(); + let m: p256::Scalar = p256::elliptic_curve::ScalarPrimitive::from_slice(&{ + let mut buf = [0u8; 32]; + buf[0..16].copy_from_slice(&m.hi.to_be_bytes()); + buf[16..32].copy_from_slice(&m.lo.to_be_bytes()); + buf + }) + .map_err(|_| { + vec![Felt::from_bytes_be( + b"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0invalid scalar", + )] + })? + .into(); + + let p = p * m; + let p = p.to_encoded_point(false); + let (x, y) = match p.coordinates() { + Coordinates::Uncompressed { x, y } => (x, y), + _ => { + // This should be unreachable because we explicitly asked for the uncompressed + // encoding. + unreachable!() + } + }; + + // The following two unwraps should be safe because the array always has 32 bytes. The other + // four are definitely safe because the slicing guarantees its length to be the right one. + let x: [u8; 32] = x.as_slice().try_into().unwrap(); + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Secp256r1Point { + x: U256 { + hi: u128::from_be_bytes(x[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(x[16..32].try_into().unwrap()), + }, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + }) + } + + fn secp256r1_get_point_from_x( + &mut self, + x: U256, + y_parity: bool, + _remaining_gas: &mut u64, + ) -> SyscallResult> { + let point = p256::ProjectivePoint::from_encoded_point( + &p256::EncodedPoint::from_bytes( + p256::CompressedPoint::from_exact_iter( + once(0x02 | y_parity as u8) + .chain(x.hi.to_be_bytes()) + .chain(x.lo.to_be_bytes()), + ) + .unwrap(), + ) + .unwrap(), + ); + + if bool::from(point.is_some()) { + let p = point.unwrap(); + + let p = p.to_encoded_point(false); + let y = match p.coordinates() { + Coordinates::Uncompressed { y, .. } => y, + _ => unreachable!(), + }; + + let y: [u8; 32] = y.as_slice().try_into().unwrap(); + Ok(Some(Secp256r1Point { + x, + y: U256 { + hi: u128::from_be_bytes(y[0..16].try_into().unwrap()), + lo: u128::from_be_bytes(y[16..32].try_into().unwrap()), + }, + })) + } else { + Ok(None) + } + } + + fn secp256r1_get_xy( + &mut self, + p: Secp256r1Point, + _remaining_gas: &mut u64, + ) -> SyscallResult<(U256, U256)> { + Ok((p.x, p.y)) + } + + fn sha256_process_block( + &mut self, + prev_state: [u32; 8], + current_block: [u32; 16], + _remaining_gas: &mut u64, + ) -> SyscallResult<[u32; 8]> { + let mut state = prev_state; + let data_as_bytes = sha2::digest::generic_array::GenericArray::from_exact_iter( + current_block.iter().flat_map(|x| x.to_be_bytes()), + ) + .unwrap(); + sha2::compress256(&mut state, &[data_as_bytes]); + Ok(state) + } +} diff --git a/debug_utils/sierra-emu/src/starknet/block_info.rs b/debug_utils/sierra-emu/src/starknet/block_info.rs new file mode 100644 index 0000000000..4896acbcc4 --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet/block_info.rs @@ -0,0 +1,20 @@ +use crate::Value; +use serde::Serialize; +use starknet_types_core::felt::Felt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +pub struct BlockInfo { + pub block_number: u64, + pub block_timestamp: u64, + pub sequencer_address: Felt, +} + +impl BlockInfo { + pub(crate) fn into_value(self) -> Value { + Value::Struct(vec![ + Value::U64(self.block_number), + Value::U64(self.block_timestamp), + Value::Felt(self.sequencer_address), + ]) + } +} diff --git a/debug_utils/sierra-emu/src/starknet/execution_info.rs b/debug_utils/sierra-emu/src/starknet/execution_info.rs new file mode 100644 index 0000000000..aceb072bc4 --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet/execution_info.rs @@ -0,0 +1,25 @@ +use super::{BlockInfo, TxInfo}; +use crate::Value; +use cairo_lang_sierra::ids::ConcreteTypeId; +use starknet_types_core::felt::Felt; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct ExecutionInfo { + pub block_info: BlockInfo, + pub tx_info: TxInfo, + pub caller_address: Felt, + pub contract_address: Felt, + pub entry_point_selector: Felt, +} + +impl ExecutionInfo { + pub(crate) fn into_value(self, felt252_ty: ConcreteTypeId) -> Value { + Value::Struct(vec![ + self.block_info.into_value(), + self.tx_info.into_value(felt252_ty), + Value::Felt(self.caller_address), + Value::Felt(self.contract_address), + Value::Felt(self.entry_point_selector), + ]) + } +} diff --git a/debug_utils/sierra-emu/src/starknet/execution_info_v2.rs b/debug_utils/sierra-emu/src/starknet/execution_info_v2.rs new file mode 100644 index 0000000000..0ce8c9f7b7 --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet/execution_info_v2.rs @@ -0,0 +1,30 @@ +use super::{BlockInfo, TxV2Info}; +use crate::Value; +use cairo_lang_sierra::ids::ConcreteTypeId; +use serde::Serialize; +use starknet_types_core::felt::Felt; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +pub struct ExecutionInfoV2 { + pub block_info: BlockInfo, + pub tx_info: TxV2Info, + pub caller_address: Felt, + pub contract_address: Felt, + pub entry_point_selector: Felt, +} + +impl ExecutionInfoV2 { + pub(crate) fn into_value( + self, + felt252_ty: ConcreteTypeId, + resource_bounds_ty: ConcreteTypeId, + ) -> Value { + Value::Struct(vec![ + self.block_info.into_value(), + self.tx_info.into_value(felt252_ty, resource_bounds_ty), + Value::Felt(self.caller_address), + Value::Felt(self.contract_address), + Value::Felt(self.entry_point_selector), + ]) + } +} diff --git a/debug_utils/sierra-emu/src/starknet/resource_bounds.rs b/debug_utils/sierra-emu/src/starknet/resource_bounds.rs new file mode 100644 index 0000000000..c9a17cc97e --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet/resource_bounds.rs @@ -0,0 +1,20 @@ +use crate::Value; +use serde::Serialize; +use starknet_types_core::felt::Felt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +pub struct ResourceBounds { + pub resource: Felt, + pub max_amount: u64, + pub max_price_per_unit: u128, +} + +impl ResourceBounds { + pub(crate) fn into_value(self) -> Value { + Value::Struct(vec![ + Value::Felt(self.resource), + Value::U64(self.max_amount), + Value::U128(self.max_price_per_unit), + ]) + } +} diff --git a/debug_utils/sierra-emu/src/starknet/secp256k1_point.rs b/debug_utils/sierra-emu/src/starknet/secp256k1_point.rs new file mode 100644 index 0000000000..0c4844baa1 --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet/secp256k1_point.rs @@ -0,0 +1,27 @@ +use super::U256; +use crate::Value; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Secp256k1Point { + pub x: U256, + pub y: U256, +} + +impl Secp256k1Point { + #[allow(unused)] + pub fn into_value(self) -> Value { + Value::Struct(vec![ + Value::Struct(vec![Value::U128(self.x.lo), Value::U128(self.x.hi)]), + Value::Struct(vec![Value::U128(self.y.lo), Value::U128(self.y.hi)]), + ]) + } + + pub fn from_value(v: Value) -> Self { + let Value::Struct(mut v) = v else { panic!() }; + + let y = U256::from_value(v.remove(1)); + let x = U256::from_value(v.remove(0)); + + Self { x, y } + } +} diff --git a/debug_utils/sierra-emu/src/starknet/secp256r1_point.rs b/debug_utils/sierra-emu/src/starknet/secp256r1_point.rs new file mode 100644 index 0000000000..5036f78ee1 --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet/secp256r1_point.rs @@ -0,0 +1,27 @@ +use super::U256; +use crate::Value; + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Secp256r1Point { + pub x: U256, + pub y: U256, +} + +impl Secp256r1Point { + #[allow(unused)] + pub fn into_value(self) -> Value { + Value::Struct(vec![ + Value::Struct(vec![Value::U128(self.x.lo), Value::U128(self.x.hi)]), + Value::Struct(vec![Value::U128(self.y.lo), Value::U128(self.y.hi)]), + ]) + } + + pub fn from_value(v: Value) -> Self { + let Value::Struct(mut v) = v else { panic!() }; + + let y = U256::from_value(v.remove(1)); + let x = U256::from_value(v.remove(0)); + + Self { x, y } + } +} diff --git a/debug_utils/sierra-emu/src/starknet/tx_info.rs b/debug_utils/sierra-emu/src/starknet/tx_info.rs new file mode 100644 index 0000000000..ac9206d821 --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet/tx_info.rs @@ -0,0 +1,31 @@ +use crate::Value; +use cairo_lang_sierra::ids::ConcreteTypeId; +use starknet_types_core::felt::Felt; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct TxInfo { + pub version: Felt, + pub account_contract_address: Felt, + pub max_fee: u128, + pub signature: Vec, + pub transaction_hash: Felt, + pub chain_id: Felt, + pub nonce: Felt, +} + +impl TxInfo { + pub(crate) fn into_value(self, felt252_ty: ConcreteTypeId) -> Value { + Value::Struct(vec![ + Value::Felt(self.version), + Value::Felt(self.account_contract_address), + Value::U128(self.max_fee), + Value::Struct(vec![Value::Array { + ty: felt252_ty, + data: self.signature.into_iter().map(Value::Felt).collect(), + }]), + Value::Felt(self.transaction_hash), + Value::Felt(self.chain_id), + Value::Felt(self.nonce), + ]) + } +} diff --git a/debug_utils/sierra-emu/src/starknet/tx_v2_info.rs b/debug_utils/sierra-emu/src/starknet/tx_v2_info.rs new file mode 100644 index 0000000000..77083dec76 --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet/tx_v2_info.rs @@ -0,0 +1,66 @@ +use super::ResourceBounds; +use crate::Value; +use cairo_lang_sierra::ids::ConcreteTypeId; +use serde::Serialize; +use starknet_types_core::felt::Felt; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)] +pub struct TxV2Info { + pub version: Felt, + pub account_contract_address: Felt, + pub max_fee: u128, + pub signature: Vec, + pub transaction_hash: Felt, + pub chain_id: Felt, + pub nonce: Felt, + pub resource_bounds: Vec, + pub tip: u128, + pub paymaster_data: Vec, + pub nonce_data_availability_mode: u32, + pub fee_data_availability_mode: u32, + pub account_deployment_data: Vec, +} + +impl TxV2Info { + pub(crate) fn into_value( + self, + felt252_ty: ConcreteTypeId, + resource_bounds_ty: ConcreteTypeId, + ) -> Value { + Value::Struct(vec![ + Value::Felt(self.version), + Value::Felt(self.account_contract_address), + Value::U128(self.max_fee), + Value::Struct(vec![Value::Array { + ty: felt252_ty.clone(), + data: self.signature.into_iter().map(Value::Felt).collect(), + }]), + Value::Felt(self.transaction_hash), + Value::Felt(self.chain_id), + Value::Felt(self.nonce), + Value::Struct(vec![Value::Array { + ty: resource_bounds_ty, + data: self + .resource_bounds + .into_iter() + .map(ResourceBounds::into_value) + .collect(), + }]), + Value::U128(self.tip), + Value::Struct(vec![Value::Array { + ty: felt252_ty.clone(), + data: self.paymaster_data.into_iter().map(Value::Felt).collect(), + }]), + Value::U32(self.nonce_data_availability_mode), + Value::U32(self.fee_data_availability_mode), + Value::Struct(vec![Value::Array { + ty: felt252_ty, + data: self + .account_deployment_data + .into_iter() + .map(Value::Felt) + .collect(), + }]), + ]) + } +} diff --git a/debug_utils/sierra-emu/src/starknet/u256.rs b/debug_utils/sierra-emu/src/starknet/u256.rs new file mode 100644 index 0000000000..43b68030ce --- /dev/null +++ b/debug_utils/sierra-emu/src/starknet/u256.rs @@ -0,0 +1,22 @@ +use crate::Value; + +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub struct U256 { + pub lo: u128, + pub hi: u128, +} + +impl U256 { + #[allow(unused)] + pub(crate) fn into_value(self) -> Value { + Value::Struct(vec![Value::U128(self.lo), Value::U128(self.hi)]) + } + + pub fn from_value(v: Value) -> Self { + let Value::Struct(v) = v else { panic!() }; + let Value::U128(lo) = v[0] else { panic!() }; + let Value::U128(hi) = v[1] else { panic!() }; + + Self { lo, hi } + } +} diff --git a/debug_utils/sierra-emu/src/test_utils.rs b/debug_utils/sierra-emu/src/test_utils.rs new file mode 100644 index 0000000000..49bf3243aa --- /dev/null +++ b/debug_utils/sierra-emu/src/test_utils.rs @@ -0,0 +1,84 @@ +#![cfg(test)] + +use std::{fs, path::Path, sync::Arc}; + +use cairo_lang_compiler::{ + compile_prepared_db, db::RootDatabase, diagnostics::DiagnosticsReporter, + project::setup_project, CompilerConfig, +}; +use cairo_lang_filesystem::db::init_dev_corelib; +use cairo_lang_sierra::program::Program; + +use crate::{starknet::StubSyscallHandler, Value, VirtualMachine}; + +#[macro_export] +macro_rules! load_cairo { + ( $( $program:tt )+ ) => { + $crate::test_utils::load_cairo_from_str(stringify!($($program)+)) + }; +} + +pub(crate) fn load_cairo_from_str(cairo_str: &str) -> (String, Program) { + let mut file = tempfile::Builder::new() + .prefix("test_") + .suffix(".cairo") + .tempfile() + .unwrap(); + let mut db = RootDatabase::default(); + + fs::write(&mut file, cairo_str).unwrap(); + + init_dev_corelib( + &mut db, + Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("corelib/src"), + ); + + let main_crate_ids = setup_project(&mut db, file.path()).unwrap(); + + let sierra_with_dbg = compile_prepared_db( + &db, + main_crate_ids, + CompilerConfig { + diagnostics_reporter: DiagnosticsReporter::stderr(), + replace_ids: true, + ..Default::default() + }, + ) + .unwrap(); + + let module_name = file.path().with_extension(""); + let module_name = module_name.file_name().unwrap().to_str().unwrap(); + (module_name.to_string(), sierra_with_dbg.program) +} + +pub fn run_test_program(sierra_program: Program) -> Vec { + let function = sierra_program + .funcs + .iter() + .find(|f| { + f.id.debug_name + .as_ref() + .map(|name| name.as_str().contains("main")) + .unwrap_or_default() + }) + .unwrap(); + + let mut vm = VirtualMachine::new(Arc::new(sierra_program.clone())); + + let initial_gas = 1000000; + + let args: &[Value] = &[]; + vm.call_program(function, initial_gas, args.iter().cloned()); + + let syscall_handler = &mut StubSyscallHandler::default(); + let trace = vm.run_with_trace(syscall_handler); + + trace + .states + .last() + .unwrap() + .items + .values() + .cloned() + .collect() +} diff --git a/debug_utils/sierra-emu/src/value.rs b/debug_utils/sierra-emu/src/value.rs new file mode 100644 index 0000000000..8f6e540f12 --- /dev/null +++ b/debug_utils/sierra-emu/src/value.rs @@ -0,0 +1,241 @@ +use cairo_lang_sierra::{ + extensions::{ + circuit::CircuitTypeConcrete, + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + starknet::StarknetTypeConcrete, + ConcreteType, + }, + ids::ConcreteTypeId, + program_registry::ProgramRegistry, +}; +use num_bigint::{BigInt, BigUint}; +use serde::Serialize; +use starknet_types_core::felt::Felt; +use std::{collections::HashMap, fmt::Debug, ops::Range}; + +use crate::{debug::type_to_name, gas::BuiltinCosts}; + +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +pub enum Value { + Array { + ty: ConcreteTypeId, + data: Vec, + }, + BoundedInt { + range: Range, + value: BigInt, + }, + Circuit(Vec), + CircuitModulus(BigUint), + CircuitOutputs(Vec), + Enum { + self_ty: ConcreteTypeId, + index: usize, + payload: Box, + }, + Felt(Felt), + Bytes31(Felt), + FeltDict { + ty: ConcreteTypeId, + data: HashMap, + }, + FeltDictEntry { + ty: ConcreteTypeId, + data: HashMap, + key: Felt, + }, + EcPoint { + x: Felt, + y: Felt, + }, + EcState { + x0: Felt, + y0: Felt, + x1: Felt, + y1: Felt, + }, + I128(i128), + I64(i64), + I32(i32), + I16(i16), + I8(i8), + Struct(Vec), + U256(u128, u128), + U128(u128), + U16(u16), + U32(u32), + U64(u64), + U8(u8), + Uninitialized { + ty: ConcreteTypeId, + }, + BuiltinCosts(BuiltinCosts), + Unit, +} + +impl Value { + pub fn default_for_type( + registry: &ProgramRegistry, + type_id: &ConcreteTypeId, + ) -> Self { + match registry.get_type(type_id).unwrap() { + CoreTypeConcrete::Uint8(_) => Value::U8(0), + CoreTypeConcrete::Uint32(_) => Value::U32(0), + CoreTypeConcrete::Uint64(_) => Value::U64(0), + CoreTypeConcrete::Uint16(_) => Value::U16(0), + CoreTypeConcrete::Uint128(_) => Value::U128(0), + CoreTypeConcrete::Felt252(_) => Value::Felt(0.into()), + CoreTypeConcrete::Enum(info) => Value::Enum { + self_ty: type_id.clone(), + index: 0, + payload: Box::new(Value::default_for_type(registry, &info.variants[0])), + }, + CoreTypeConcrete::Struct(info) => Value::Struct( + info.members + .iter() + .map(|member| Value::default_for_type(registry, member)) + .collect(), + ), + x => panic!("type {:?} has no default value implementation", x.info()), + } + } + + pub fn is( + &self, + registry: &ProgramRegistry, + type_id: &ConcreteTypeId, + ) -> bool { + let ty = registry.get_type(type_id).unwrap(); + let res = match ty { + CoreTypeConcrete::Array(info) => { + matches!(self, Self::Array { ty, .. } if *ty == info.ty) + } + CoreTypeConcrete::BoundedInt(info) => { + matches!(self, Self::BoundedInt { range, .. } if range.start == info.range.lower && range.end == info.range.upper) + } + CoreTypeConcrete::Enum(_) => { + matches!(self, Self::Enum { self_ty, .. } if self_ty == type_id) + } + CoreTypeConcrete::Felt252(_) => matches!(self, Self::Felt(_)), + CoreTypeConcrete::Bytes31(_) => matches!(self, Self::Bytes31(_)), + CoreTypeConcrete::Felt252Dict(info) => { + matches!(self, Self::FeltDict { ty, .. } if *ty == info.ty) + } + CoreTypeConcrete::GasBuiltin(_) => matches!(self, Self::U64(_)), + CoreTypeConcrete::NonZero(info) => self.is(registry, &info.ty), + CoreTypeConcrete::Sint128(_) => matches!(self, Self::I128(_)), + CoreTypeConcrete::Sint32(_) => matches!(self, Self::I32(_)), + CoreTypeConcrete::Sint8(_) => matches!(self, Self::I8(_)), + CoreTypeConcrete::Snapshot(info) => self.is(registry, &info.ty), + CoreTypeConcrete::Starknet( + StarknetTypeConcrete::ClassHash(_) + | StarknetTypeConcrete::ContractAddress(_) + | StarknetTypeConcrete::StorageBaseAddress(_) + | StarknetTypeConcrete::StorageAddress(_), + ) => matches!(self, Self::Felt(_)), + CoreTypeConcrete::Struct(info) => { + matches!(self, Self::Struct(members) + if members.len() == info.members.len() + && members + .iter() + .zip(&info.members) + .all(|(value, ty)| value.is(registry, ty)) + ) + } + CoreTypeConcrete::Uint8(_) => matches!(self, Self::U8(_)), + CoreTypeConcrete::Uint32(_) => matches!(self, Self::U32(_)), + CoreTypeConcrete::Uint128(_) => { + matches!(self, Self::U128(_)) + } + + // Unused builtins (mapped to `Value::Unit`). + CoreTypeConcrete::RangeCheck(_) + | CoreTypeConcrete::SegmentArena(_) + | CoreTypeConcrete::RangeCheck96(_) + | CoreTypeConcrete::Starknet(StarknetTypeConcrete::System(_)) => { + matches!(self, Self::Unit) + } + + // To do: + CoreTypeConcrete::Coupon(_) => todo!(), + CoreTypeConcrete::Bitwise(_) => matches!(self, Self::Unit), + CoreTypeConcrete::Box(info) => self.is(registry, &info.ty), + + // Circuit related types + CoreTypeConcrete::Circuit(selector) => match selector { + CircuitTypeConcrete::Circuit(_) => matches!(self, Self::Circuit(_)), + CircuitTypeConcrete::CircuitData(_) => matches!(self, Self::Circuit(_)), + CircuitTypeConcrete::CircuitOutputs(_) => matches!(self, Self::CircuitOutputs(_)), + CircuitTypeConcrete::CircuitInput(_) => matches!(self, Self::Unit), + CircuitTypeConcrete::CircuitInputAccumulator(_) => matches!(self, Self::Circuit(_)), + CircuitTypeConcrete::CircuitModulus(_) => matches!(self, Self::CircuitModulus(_)), + CircuitTypeConcrete::U96Guarantee(_) => matches!(self, Self::U128(_)), + CircuitTypeConcrete::CircuitDescriptor(_) + | CircuitTypeConcrete::CircuitFailureGuarantee(_) + | CircuitTypeConcrete::AddMod(_) + | CircuitTypeConcrete::MulMod(_) + | CircuitTypeConcrete::AddModGate(_) + | CircuitTypeConcrete::CircuitPartialOutputs(_) + | CircuitTypeConcrete::InverseGate(_) + | CircuitTypeConcrete::MulModGate(_) + | CircuitTypeConcrete::SubModGate(_) + | CircuitTypeConcrete::U96LimbsLessThanGuarantee(_) => { + matches!(self, Self::Unit) + } + }, + CoreTypeConcrete::Const(_) => todo!(), + CoreTypeConcrete::EcOp(_) => matches!(self, Self::Unit), + CoreTypeConcrete::EcPoint(_) => matches!(self, Self::EcPoint { .. }), + CoreTypeConcrete::EcState(_) => matches!(self, Self::EcState { .. }), + CoreTypeConcrete::BuiltinCosts(_) => matches!(self, Self::BuiltinCosts(_)), + CoreTypeConcrete::Uint16(_) => matches!(self, Self::U16(_)), + CoreTypeConcrete::Uint64(_) => matches!(self, Self::U64(_)), + CoreTypeConcrete::Uint128MulGuarantee(_) => matches!(self, Self::Unit), + CoreTypeConcrete::Sint16(_) => matches!(self, Self::I16(_)), + CoreTypeConcrete::Sint64(_) => matches!(self, Self::I64(_)), + CoreTypeConcrete::Nullable(info) => self.is(registry, &info.ty), + CoreTypeConcrete::Uninitialized(_) => matches!(self, Self::Uninitialized { .. }), + CoreTypeConcrete::Felt252DictEntry(info) => { + matches!(self, Self::FeltDictEntry { ty, .. } if *ty == info.ty) + } + CoreTypeConcrete::SquashedFelt252Dict(info) => { + matches!(self, Self::FeltDict { ty, .. } if *ty == info.ty) + } + CoreTypeConcrete::Pedersen(_) => matches!(self, Self::Unit), + CoreTypeConcrete::Poseidon(_) => matches!(self, Self::Unit), + CoreTypeConcrete::Span(_) => todo!(), + CoreTypeConcrete::Starknet(inner) => match inner { + StarknetTypeConcrete::ClassHash(_) + | StarknetTypeConcrete::ContractAddress(_) + | StarknetTypeConcrete::StorageBaseAddress(_) + | StarknetTypeConcrete::StorageAddress(_) => matches!(self, Self::Felt(_)), + StarknetTypeConcrete::System(_) => matches!(self, Self::Unit), + StarknetTypeConcrete::Secp256Point(_) => matches!(self, Self::Struct(_)), + StarknetTypeConcrete::Sha256StateHandle(_) => matches!(self, Self::Struct { .. }), + }, + CoreTypeConcrete::IntRange(_) => todo!(), + CoreTypeConcrete::Blake(_) => todo!(), + CoreTypeConcrete::QM31(_) => todo!(), + }; + + if !res { + dbg!( + "value is mismatch", + ty.info(), + self, + type_to_name(type_id, registry) + ); + } + + res + } + + #[doc(hidden)] + pub fn parse_felt(value: &str) -> Self { + Self::Felt(if value.starts_with("0x") { + Felt::from_hex(value).unwrap() + } else { + Felt::from_dec_str(value).unwrap() + }) + } +} diff --git a/debug_utils/sierra-emu/src/vm.rs b/debug_utils/sierra-emu/src/vm.rs new file mode 100644 index 0000000000..86b754b47b --- /dev/null +++ b/debug_utils/sierra-emu/src/vm.rs @@ -0,0 +1,520 @@ +use crate::{ + debug::libfunc_to_name, + gas::{BuiltinCosts, GasMetadata, MetadataComputationConfig}, + starknet::StarknetSyscallHandler, + ContractExecutionResult, ProgramTrace, StateDump, Value, +}; +use cairo_lang_sierra::{ + edit_state, + extensions::{ + circuit::CircuitTypeConcrete, + core::{CoreConcreteLibfunc, CoreLibfunc, CoreType, CoreTypeConcrete}, + gas::CostTokenType, + starknet::StarknetTypeConcrete, + ConcreteLibfunc, ConcreteType, + }, + ids::{ConcreteLibfuncId, FunctionId, VarId}, + program::{GenFunction, GenStatement, Invocation, Program, StatementIdx}, + program_registry::ProgramRegistry, +}; +use cairo_lang_starknet_classes::{ + casm_contract_class::ENTRY_POINT_COST, compiler_version::VersionId, + contract_class::ContractEntryPoints, +}; +use cairo_lang_utils::ordered_hash_map::OrderedHashMap; +use smallvec::{smallvec, SmallVec}; +use starknet_types_core::felt::Felt; +use std::{cmp::Ordering, fmt::Debug, sync::Arc}; +use tracing::{debug, trace}; + +mod ap_tracking; +mod array; +mod bool; +mod bounded_int; +mod r#box; +mod branch_align; +mod bytes31; +mod cast; +mod circuit; +mod r#const; +mod drop; +mod dup; +mod ec; +mod r#enum; +mod felt252; +mod felt252_dict; +mod felt252_dict_entry; +mod function_call; +mod gas; +mod int128; +mod jump; +mod mem; +mod pedersen; +mod poseidon; +mod snapshot_take; +mod starknet; +mod r#struct; +mod uint128; +mod uint16; +mod uint252; +mod uint32; +mod uint512; +mod uint64; +mod uint8; + +#[derive(Clone)] +pub struct VirtualMachine { + pub program: Arc, + pub registry: Arc>, + frames: Vec, + pub gas: GasMetadata, + entry_points: Option, + builtin_costs: BuiltinCosts, +} + +impl Debug for VirtualMachine { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VirtualMachine") + .field("frames", &self.frames) + .field("gas", &self.gas) + .finish_non_exhaustive() + } +} + +impl VirtualMachine { + pub fn new(program: Arc) -> Self { + let registry = ProgramRegistry::new(&program).unwrap(); + Self { + gas: GasMetadata::new(&program, Some(MetadataComputationConfig::default())).unwrap(), + program, + registry: Arc::new(registry), + frames: Vec::new(), + entry_points: None, + builtin_costs: Default::default(), + } + } +} + +impl VirtualMachine { + pub fn new_starknet( + program: Arc, + entry_points: &ContractEntryPoints, + sierra_version: VersionId, + ) -> Self { + let no_eq_solver = match sierra_version.major.cmp(&1) { + Ordering::Less => false, + Ordering::Equal => sierra_version.minor >= 4, + Ordering::Greater => true, + }; + + let registry = ProgramRegistry::new(&program).unwrap(); + Self { + gas: GasMetadata::new( + &program, + Some(MetadataComputationConfig { + function_set_costs: entry_points + .constructor + .iter() + .chain(entry_points.external.iter()) + .chain(entry_points.l1_handler.iter()) + .map(|x| { + ( + FunctionId::new(x.function_idx as u64), + [(CostTokenType::Const, ENTRY_POINT_COST)].into(), + ) + }) + .collect(), + linear_gas_solver: no_eq_solver, + linear_ap_change_solver: no_eq_solver, + }), + ) + .unwrap(), + program, + registry: Arc::new(registry), + frames: Vec::new(), + entry_points: Some(entry_points.clone()), + builtin_costs: Default::default(), + } + } + + pub fn registry(&self) -> &ProgramRegistry { + &self.registry + } + + /// Utility to call a contract. + pub fn call_contract( + &mut self, + selector: Felt, + initial_gas: u64, + calldata: I, + builtin_costs: Option, + ) where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + { + self.builtin_costs = builtin_costs.unwrap_or_default(); + let args: Vec<_> = calldata.into_iter().map(Value::Felt).collect(); + let entry_points = self.entry_points.as_ref().expect("contract should have"); + let selector_uint = selector.to_biguint(); + let function_idx = entry_points + .constructor + .iter() + .chain(entry_points.external.iter()) + .chain(entry_points.l1_handler.iter()) + .find(|x| x.selector == selector_uint) + .map(|x| x.function_idx) + .expect("function id not found"); + let function = &self.program.funcs[function_idx]; + + self.push_frame( + function.id.clone(), + function + .signature + .param_types + .iter() + .map(|type_id| { + let type_info = self.registry().get_type(type_id).unwrap(); + match type_info { + CoreTypeConcrete::GasBuiltin(_) => Value::U64(initial_gas), + // Add the calldata structure + CoreTypeConcrete::Struct(inner) => { + let member = self.registry().get_type(&inner.members[0]).unwrap(); + match member { + CoreTypeConcrete::Snapshot(inner) => { + let inner = self.registry().get_type(&inner.ty).unwrap(); + match inner { + CoreTypeConcrete::Array(inner) => { + let felt_ty = &inner.ty; + Value::Struct(vec![Value::Array { + ty: felt_ty.clone(), + data: args.clone(), + }]) + } + _ => unreachable!(), + } + } + _ => unreachable!(), + } + } + CoreTypeConcrete::BuiltinCosts(_) => { + Value::BuiltinCosts(builtin_costs.unwrap_or_default()) + } + CoreTypeConcrete::Starknet(StarknetTypeConcrete::System(_)) => Value::Unit, + CoreTypeConcrete::RangeCheck(_) + | CoreTypeConcrete::RangeCheck96(_) + | CoreTypeConcrete::Circuit( + CircuitTypeConcrete::MulMod(_) | CircuitTypeConcrete::AddMod(_), + ) + | CoreTypeConcrete::Pedersen(_) + | CoreTypeConcrete::Poseidon(_) + | CoreTypeConcrete::Bitwise(_) + | CoreTypeConcrete::EcOp(_) + | CoreTypeConcrete::SegmentArena(_) => Value::Unit, + x => { + todo!("{:?}", x.info()) + } + } + }) + .collect::>(), + ); + } + + /// Utility to call a contract. + pub fn call_program( + &mut self, + function: &GenFunction, + initial_gas: u64, + args: I, + ) where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + { + let mut iter = args.into_iter(); + self.push_frame( + function.id.clone(), + function + .signature + .param_types + .iter() + .map(|type_id| { + let type_info = self.registry().get_type(type_id).unwrap(); + match type_info { + CoreTypeConcrete::GasBuiltin(_) => Value::U64(initial_gas), + CoreTypeConcrete::RangeCheck(_) + | CoreTypeConcrete::RangeCheck96(_) + | CoreTypeConcrete::Bitwise(_) + | CoreTypeConcrete::Pedersen(_) + | CoreTypeConcrete::Poseidon(_) + | CoreTypeConcrete::SegmentArena(_) + | CoreTypeConcrete::Circuit( + CircuitTypeConcrete::AddMod(_) | CircuitTypeConcrete::MulMod(_), + ) => Value::Unit, + CoreTypeConcrete::Starknet(inner) => match inner { + StarknetTypeConcrete::System(_) => Value::Unit, + _ => todo!(), + }, + _ => iter.next().unwrap(), + } + }) + .collect::>(), + ); + } + + /// Effectively a function call (for entry points). + pub fn push_frame(&mut self, function_id: FunctionId, args: I) + where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + { + let function = self.registry.get_function(&function_id).unwrap(); + + let args = args.into_iter(); + assert_eq!(args.len(), function.params.len()); + self.frames.push(SierraFrame { + _function_id: function_id, + state: function + .params + .iter() + .zip(args) + .map(|(param, value)| { + assert!(value.is(&self.registry, ¶m.ty)); + (param.id.clone(), value) + }) + .collect(), + + pc: function.entry_point, + }) + } + + /// Run a single statement and return the state before its execution. + pub fn step( + &mut self, + syscall_handler: &mut impl StarknetSyscallHandler, + ) -> Option<(StatementIdx, OrderedHashMap)> { + let frame = self.frames.last_mut()?; + + let pc_snapshot = frame.pc; + let state_snapshot = frame.state.clone(); + + debug!( + "Evaluating statement {} ({})", + frame.pc.0, &self.program.statements[frame.pc.0], + ); + trace!("values: \n{:#?}\n", state_snapshot); + match &self.program.statements[frame.pc.0] { + GenStatement::Invocation(invocation) => { + let libfunc = self.registry.get_libfunc(&invocation.libfunc_id).unwrap(); + debug!( + "Executing invocation of libfunc: {}", + libfunc_to_name(libfunc) + ); + let (state, values) = + edit_state::take_args(std::mem::take(&mut frame.state), invocation.args.iter()) + .unwrap(); + + match eval( + &self.registry, + &invocation.libfunc_id, + values, + syscall_handler, + &self.gas, + &frame.pc, + self.builtin_costs, + ) { + EvalAction::NormalBranch(branch_idx, results) => { + assert_eq!( + results.len(), + invocation.branches[branch_idx].results.len(), + "invocation of {invocation} returned the wrong number of values" + ); + + assert!( + results + .iter() + .zip( + &self + .registry + .get_libfunc(&invocation.libfunc_id) + .unwrap() + .branch_signatures()[branch_idx] + .vars + ) + .all(|(value, ret)| value.is(&self.registry, &ret.ty)), + "invocation of {} returned an invalid argument", + libfunc_to_name( + self.registry.get_libfunc(&invocation.libfunc_id).unwrap() + ) + ); + + frame.pc = frame.pc.next(&invocation.branches[branch_idx].target); + frame.state = edit_state::put_results( + state, + invocation.branches[branch_idx].results.iter().zip(results), + ) + .unwrap(); + } + EvalAction::FunctionCall(function_id, args) => { + let function = self.registry.get_function(&function_id).unwrap(); + frame.state = state; + self.frames.push(SierraFrame { + _function_id: function_id, + state: function + .params + .iter() + .map(|param| param.id.clone()) + .zip(args.iter().cloned()) + .collect(), + + pc: function.entry_point, + }); + } + } + } + GenStatement::Return(ids) => { + let mut curr_frame = self.frames.pop().unwrap(); + if let Some(prev_frame) = self.frames.last_mut() { + let (state, values) = + edit_state::take_args(std::mem::take(&mut curr_frame.state), ids.iter()) + .unwrap(); + assert!(state.is_empty()); + + let target_branch = match &self.program.statements[prev_frame.pc.0] { + GenStatement::Invocation(Invocation { branches, .. }) => { + assert_eq!(branches.len(), 1); + &branches[0] + } + _ => unreachable!(), + }; + + assert_eq!(target_branch.results.len(), values.len()); + prev_frame.pc = prev_frame.pc.next(&target_branch.target); + prev_frame.state = edit_state::put_results( + std::mem::take(&mut prev_frame.state), + target_branch.results.iter().zip(values), + ) + .unwrap(); + } + } + } + + Some((pc_snapshot, state_snapshot)) + } + + /// Run all the statement and return the trace. + pub fn run_with_trace( + &mut self, + syscall_handler: &mut impl StarknetSyscallHandler, + ) -> ProgramTrace { + let mut trace = ProgramTrace::new(); + + while let Some((statement_idx, state)) = self.step(syscall_handler) { + trace.push(StateDump::new(statement_idx, state)); + } + + trace + } + + /// Run all the statement and return the trace. + pub fn run( + &mut self, + syscall_handler: &mut impl StarknetSyscallHandler, + ) -> Option { + let mut last = None; + + while let Some((statement_idx, state)) = self.step(syscall_handler) { + last = Some(StateDump::new(statement_idx, state)); + } + + ContractExecutionResult::from_state(&last?) + } +} + +#[derive(Clone, Debug)] +struct SierraFrame { + _function_id: FunctionId, + + state: OrderedHashMap, + pc: StatementIdx, +} + +enum EvalAction { + NormalBranch(usize, SmallVec<[Value; 2]>), + FunctionCall(FunctionId, SmallVec<[Value; 2]>), +} + +fn eval<'a>( + registry: &'a ProgramRegistry, + id: &'a ConcreteLibfuncId, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, + gas: &GasMetadata, + statement_idx: &StatementIdx, + builtin_costs: BuiltinCosts, +) -> EvalAction { + match registry.get_libfunc(id).unwrap() { + CoreConcreteLibfunc::ApTracking(selector) => { + self::ap_tracking::eval(registry, selector, args) + } + CoreConcreteLibfunc::Array(selector) => self::array::eval(registry, selector, args), + CoreConcreteLibfunc::Bool(selector) => self::bool::eval(registry, selector, args), + CoreConcreteLibfunc::BoundedInt(selector) => { + self::bounded_int::eval(registry, selector, args) + } + CoreConcreteLibfunc::Box(selector) => self::r#box::eval(registry, selector, args), + CoreConcreteLibfunc::BranchAlign(info) => self::branch_align::eval(registry, info, args), + CoreConcreteLibfunc::Bytes31(selector) => self::bytes31::eval(registry, selector, args), + CoreConcreteLibfunc::Cast(selector) => self::cast::eval(registry, selector, args), + CoreConcreteLibfunc::Circuit(selector) => self::circuit::eval(registry, selector, args), + CoreConcreteLibfunc::Const(selector) => self::r#const::eval(registry, selector, args), + CoreConcreteLibfunc::Coupon(_) => todo!(), + CoreConcreteLibfunc::CouponCall(_) => todo!(), + CoreConcreteLibfunc::Debug(_) => todo!(), + CoreConcreteLibfunc::Drop(info) => self::drop::eval(registry, info, args), + CoreConcreteLibfunc::Dup(info) => self::dup::eval(registry, info, args), + CoreConcreteLibfunc::Ec(selector) => self::ec::eval(registry, selector, args), + CoreConcreteLibfunc::Enum(selector) => self::r#enum::eval(registry, selector, args), + CoreConcreteLibfunc::Felt252(selector) => self::felt252::eval(registry, selector, args), + CoreConcreteLibfunc::Felt252Dict(selector) => { + self::felt252_dict::eval(registry, selector, args) + } + CoreConcreteLibfunc::Felt252DictEntry(selector) => { + self::felt252_dict_entry::eval(registry, selector, args) + } + CoreConcreteLibfunc::FunctionCall(info) => self::function_call::eval(registry, info, args), + CoreConcreteLibfunc::Gas(selector) => { + self::gas::eval(registry, selector, args, gas, *statement_idx, builtin_costs) + } + CoreConcreteLibfunc::Mem(selector) => self::mem::eval(registry, selector, args), + CoreConcreteLibfunc::Nullable(_) => todo!(), + CoreConcreteLibfunc::Pedersen(selector) => self::pedersen::eval(registry, selector, args), + CoreConcreteLibfunc::Poseidon(selector) => self::poseidon::eval(registry, selector, args), + CoreConcreteLibfunc::Sint128(selector) => self::int128::eval(registry, selector, args), + CoreConcreteLibfunc::Sint16(_) => todo!(), + CoreConcreteLibfunc::Sint32(_) => todo!(), + CoreConcreteLibfunc::Sint64(_) => todo!(), + CoreConcreteLibfunc::Sint8(_) => todo!(), + CoreConcreteLibfunc::SnapshotTake(info) => self::snapshot_take::eval(registry, info, args), + CoreConcreteLibfunc::Starknet(selector) => { + self::starknet::eval(registry, selector, args, syscall_handler) + } + CoreConcreteLibfunc::Struct(selector) => self::r#struct::eval(registry, selector, args), + CoreConcreteLibfunc::Uint128(selector) => self::uint128::eval(registry, selector, args), + CoreConcreteLibfunc::Uint16(selector) => self::uint16::eval(registry, selector, args), + CoreConcreteLibfunc::Uint256(selector) => self::uint252::eval(registry, selector, args), + CoreConcreteLibfunc::Uint32(selector) => self::uint32::eval(registry, selector, args), + CoreConcreteLibfunc::Uint512(selector) => self::uint512::eval(registry, selector, args), + CoreConcreteLibfunc::Uint64(selector) => self::uint64::eval(registry, selector, args), + CoreConcreteLibfunc::Uint8(selector) => self::uint8::eval(registry, selector, args), + CoreConcreteLibfunc::UnconditionalJump(info) => self::jump::eval(registry, info, args), + CoreConcreteLibfunc::UnwrapNonZero(_info) => { + let [value] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![value]) + } + CoreConcreteLibfunc::IntRange(_) => todo!(), + CoreConcreteLibfunc::Blake(_) => todo!(), + CoreConcreteLibfunc::QM31(_) => todo!(), + CoreConcreteLibfunc::Felt252SquashedDict(_) => todo!(), + CoreConcreteLibfunc::Trace(_) => todo!(), + } +} diff --git a/debug_utils/sierra-emu/src/vm/ap_tracking.rs b/debug_utils/sierra-emu/src/vm/ap_tracking.rs new file mode 100644 index 0000000000..ae39eab889 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/ap_tracking.rs @@ -0,0 +1,53 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + ap_tracking::ApTrackingConcreteLibfunc, + core::{CoreLibfunc, CoreType}, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &ApTrackingConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + ApTrackingConcreteLibfunc::Revoke(info) => eval_revoke(registry, info, args), + ApTrackingConcreteLibfunc::Enable(info) => eval_enable(registry, info, args), + ApTrackingConcreteLibfunc::Disable(info) => eval_disable(registry, info, args), + } +} + +pub fn eval_revoke( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![]) +} + +pub fn eval_enable( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![]) +} + +pub fn eval_disable( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![]) +} diff --git a/debug_utils/sierra-emu/src/vm/array.rs b/debug_utils/sierra-emu/src/vm/array.rs new file mode 100644 index 0000000000..d5eecf9b68 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/array.rs @@ -0,0 +1,240 @@ +use super::EvalAction; +use crate::{find_real_type, Value}; +use cairo_lang_sierra::{ + extensions::{ + array::ArrayConcreteLibfunc, + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + lib_func::{SignatureAndTypeConcreteLibfunc, SignatureOnlyConcreteLibfunc}, + ConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &ArrayConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + ArrayConcreteLibfunc::New(info) => eval_new(registry, info, args), + ArrayConcreteLibfunc::SpanFromTuple(info) => eval_span_from_tuple(registry, info, args), + ArrayConcreteLibfunc::TupleFromSpan(_) => todo!(), + ArrayConcreteLibfunc::Append(info) => eval_append(registry, info, args), + ArrayConcreteLibfunc::PopFront(info) => eval_pop_front(registry, info, args), + ArrayConcreteLibfunc::PopFrontConsume(_) => todo!(), + ArrayConcreteLibfunc::Get(info) => eval_get(registry, info, args), + ArrayConcreteLibfunc::Slice(info) => eval_slice(registry, info, args), + ArrayConcreteLibfunc::Len(info) => eval_len(registry, info, args), + ArrayConcreteLibfunc::SnapshotPopFront(info) => { + eval_snapshot_pop_front(registry, info, args) + } + ArrayConcreteLibfunc::SnapshotPopBack(info) => eval_snapshot_pop_back(registry, info, args), + ArrayConcreteLibfunc::SnapshotMultiPopFront(info) => { + eval_snapshot_multi_pop_front(registry, info, args) + } + ArrayConcreteLibfunc::SnapshotMultiPopBack(_) => todo!(), + } +} + +fn eval_span_from_tuple( + registry: &ProgramRegistry, + info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Struct(data)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let ty = &info.branch_signatures()[0].vars[0].ty; + let ty = find_real_type(registry, ty); + + let CoreTypeConcrete::Array(info) = registry.get_type(&ty).unwrap() else { + panic!() + }; + + let value = Value::Array { + ty: info.ty.clone(), + data, + }; + + EvalAction::NormalBranch(0, smallvec![value]) +} + +pub fn eval_new( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [] = args.try_into().unwrap(); + + let type_info = registry + .get_type(&info.signature.branch_signatures[0].vars[0].ty) + .unwrap(); + let ty = match type_info { + CoreTypeConcrete::Array(info) => &info.ty, + _ => unreachable!(), + }; + + EvalAction::NormalBranch( + 0, + smallvec![Value::Array { + ty: ty.clone(), + data: Vec::new(), + }], + ) +} + +pub fn eval_append( + registry: &ProgramRegistry, + info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Array { ty, mut data }, item]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + assert_eq!(info.signature.param_signatures[1].ty, ty); + assert!(item.is(registry, &ty)); + data.push(item.clone()); + + EvalAction::NormalBranch(0, smallvec![Value::Array { ty, data }]) +} + +pub fn eval_get( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Array { data, .. }, Value::U32(index)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + match data.get(index as usize).cloned() { + Some(value) => EvalAction::NormalBranch(0, smallvec![range_check, value]), + None => EvalAction::NormalBranch(1, smallvec![range_check]), + } +} + +pub fn eval_slice( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Array { data, ty }, Value::U32(start), Value::U32(len)]: [Value; 4] = + args.try_into().unwrap() + else { + panic!() + }; + + match data.get(start as usize..(start + len) as usize) { + Some(value) => EvalAction::NormalBranch( + 0, + smallvec![ + range_check, + Value::Array { + data: value.to_vec(), + ty + } + ], + ), + None => EvalAction::NormalBranch(1, smallvec![range_check]), + } +} + +pub fn eval_len( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Array { data, .. }]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let array_len = data.len().try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![Value::U32(array_len)]) +} + +pub fn eval_pop_front( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Array { mut data, ty }]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + if !data.is_empty() { + let new_data = data.split_off(1); + let value = data[0].clone(); + EvalAction::NormalBranch(0, smallvec![Value::Array { data: new_data, ty }, value]) + } else { + EvalAction::NormalBranch(1, smallvec![Value::Array { data, ty }]) + } +} + +pub fn eval_snapshot_pop_front( + registry: &ProgramRegistry, + info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Array { mut data, ty }]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + if !data.is_empty() { + let new_data = data.split_off(1); + let value = data[0].clone(); + assert!(value.is(registry, &info.ty)); + EvalAction::NormalBranch(0, smallvec![Value::Array { data: new_data, ty }, value]) + } else { + EvalAction::NormalBranch(1, smallvec![Value::Array { data, ty }]) + } +} + +pub fn eval_snapshot_pop_back( + registry: &ProgramRegistry, + info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Array { mut data, ty }]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + if !data.is_empty() { + let new_data = data.split_off(data.len() - 1); + let value = new_data[0].clone(); + assert!(value.is(registry, &info.ty)); + EvalAction::NormalBranch(0, smallvec![Value::Array { data, ty }, value]) + } else { + EvalAction::NormalBranch(1, smallvec![Value::Array { data, ty }]) + } +} + +fn eval_snapshot_multi_pop_front( + registry: &ProgramRegistry, + info: &cairo_lang_sierra::extensions::array::ConcreteMultiPopLibfunc, + args: Vec, +) -> EvalAction { + let [rangecheck, Value::Array { mut data, ty }]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + let CoreTypeConcrete::Struct(popped_cty) = registry.get_type(&info.popped_ty).unwrap() else { + panic!() + }; + + if data.len() >= popped_cty.members.len() { + let new_data = data.split_off(popped_cty.members.len()); + let value = Value::Struct(data); + assert!(value.is(registry, &info.popped_ty)); + EvalAction::NormalBranch( + 0, + smallvec![rangecheck, Value::Array { data: new_data, ty }, value], + ) + } else { + EvalAction::NormalBranch(1, smallvec![rangecheck, Value::Array { data, ty }]) + } +} diff --git a/debug_utils/sierra-emu/src/vm/bool.rs b/debug_utils/sierra-emu/src/vm/bool.rs new file mode 100644 index 0000000000..8d632953a7 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/bool.rs @@ -0,0 +1,159 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + boolean::BoolConcreteLibfunc, + core::{CoreLibfunc, CoreType}, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &BoolConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + BoolConcreteLibfunc::And(info) => eval_and(registry, info, args), + BoolConcreteLibfunc::Not(info) => eval_not(registry, info, args), + BoolConcreteLibfunc::Xor(info) => eval_xor(registry, info, args), + BoolConcreteLibfunc::Or(info) => eval_or(registry, info, args), + BoolConcreteLibfunc::ToFelt252(info) => eval_to_felt252(registry, info, args), + } +} + +pub fn eval_and( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Enum { + self_ty, + index: lhs, + payload, + }, Value::Enum { + self_ty: _, + index: rhs, + payload: _, + }]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + let lhs = lhs != 0; + let rhs = rhs != 0; + + EvalAction::NormalBranch( + 0, + smallvec![Value::Enum { + self_ty, + index: (lhs && rhs) as usize, + payload + }], + ) +} + +pub fn eval_not( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Enum { + self_ty, + index: lhs, + payload, + }]: [Value; 1] = args.try_into().unwrap() + else { + panic!() + }; + + EvalAction::NormalBranch( + 0, + smallvec![Value::Enum { + self_ty, + index: (lhs == 0) as usize, + payload + }], + ) +} + +pub fn eval_xor( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Enum { + self_ty, + index: lhs, + payload, + }, Value::Enum { + self_ty: _, + index: rhs, + payload: _, + }]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + let lhs = lhs != 0; + let rhs = rhs != 0; + + EvalAction::NormalBranch( + 0, + smallvec![Value::Enum { + self_ty, + index: (lhs ^ rhs) as usize, + payload + }], + ) +} + +pub fn eval_or( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Enum { + self_ty, + index: lhs, + payload, + }, Value::Enum { + self_ty: _, + index: rhs, + payload: _, + }]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + let lhs = lhs != 0; + let rhs = rhs != 0; + + EvalAction::NormalBranch( + 0, + smallvec![Value::Enum { + self_ty, + index: (lhs || rhs) as usize, + payload + }], + ) +} + +pub fn eval_to_felt252( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Enum { + self_ty: _, + index: lhs, + payload: _, + }]: [Value; 1] = args.try_into().unwrap() + else { + panic!() + }; + + EvalAction::NormalBranch(0, smallvec![Value::Felt(lhs.into())]) +} diff --git a/debug_utils/sierra-emu/src/vm/bounded_int.rs b/debug_utils/sierra-emu/src/vm/bounded_int.rs new file mode 100644 index 0000000000..69fd992c1a --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/bounded_int.rs @@ -0,0 +1,398 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + bounded_int::{ + BoundedIntConcreteLibfunc, BoundedIntConstrainConcreteLibfunc, + BoundedIntDivRemConcreteLibfunc, BoundedIntTrimConcreteLibfunc, + }, + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + lib_func::SignatureOnlyConcreteLibfunc, + ConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use num_bigint::BigInt; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &BoundedIntConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + BoundedIntConcreteLibfunc::Add(info) => eval_add(registry, info, args), + BoundedIntConcreteLibfunc::Sub(info) => eval_sub(registry, info, args), + BoundedIntConcreteLibfunc::Mul(info) => eval_mul(registry, info, args), + BoundedIntConcreteLibfunc::DivRem(info) => eval_div_rem(registry, info, args), + BoundedIntConcreteLibfunc::Constrain(info) => eval_constrain(registry, info, args), + BoundedIntConcreteLibfunc::IsZero(info) => eval_is_zero(registry, info, args), + BoundedIntConcreteLibfunc::WrapNonZero(info) => eval_wrap_non_zero(registry, info, args), + BoundedIntConcreteLibfunc::TrimMin(info) | BoundedIntConcreteLibfunc::TrimMax(info) => { + eval_trim(registry, info, args) + } + } +} + +pub fn eval_add( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::BoundedInt { value: lhs, .. }, Value::BoundedInt { value: rhs, .. }]: [Value; 2] = + args.try_into().unwrap() + else { + panic!() + }; + + let range = match registry + .get_type(&info.signature.branch_signatures[0].vars[0].ty) + .unwrap() + { + CoreTypeConcrete::BoundedInt(info) => info.range.lower.clone()..info.range.upper.clone(), + CoreTypeConcrete::NonZero(info) => match registry.get_type(&info.ty).unwrap() { + CoreTypeConcrete::BoundedInt(info) => { + info.range.lower.clone()..info.range.upper.clone() + } + _ => unreachable!(), + }, + _ => unreachable!(), + }; + EvalAction::NormalBranch( + 0, + smallvec![Value::BoundedInt { + range, + value: lhs + rhs, + }], + ) +} + +pub fn eval_sub( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::BoundedInt { value: lhs, .. }, Value::BoundedInt { value: rhs, .. }]: [Value; 2] = + args.try_into().unwrap() + else { + panic!() + }; + + let range = match registry + .get_type(&info.signature.branch_signatures[0].vars[0].ty) + .unwrap() + { + CoreTypeConcrete::BoundedInt(info) => info.range.lower.clone()..info.range.upper.clone(), + CoreTypeConcrete::NonZero(info) => match registry.get_type(&info.ty).unwrap() { + CoreTypeConcrete::BoundedInt(info) => { + info.range.lower.clone()..info.range.upper.clone() + } + _ => unreachable!(), + }, + _ => unreachable!(), + }; + EvalAction::NormalBranch( + 0, + smallvec![Value::BoundedInt { + range, + value: lhs - rhs, + }], + ) +} + +pub fn eval_mul( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::BoundedInt { value: lhs, .. }, Value::BoundedInt { value: rhs, .. }]: [Value; 2] = + args.try_into().unwrap() + else { + panic!() + }; + + let range = match registry + .get_type(&info.signature.branch_signatures[0].vars[0].ty) + .unwrap() + { + CoreTypeConcrete::BoundedInt(info) => info.range.lower.clone()..info.range.upper.clone(), + CoreTypeConcrete::NonZero(info) => match registry.get_type(&info.ty).unwrap() { + CoreTypeConcrete::BoundedInt(info) => { + info.range.lower.clone()..info.range.upper.clone() + } + _ => unreachable!(), + }, + _ => unreachable!(), + }; + EvalAction::NormalBranch( + 0, + smallvec![Value::BoundedInt { + range, + value: lhs * rhs, + }], + ) +} + +pub fn eval_div_rem( + registry: &ProgramRegistry, + info: &BoundedIntDivRemConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::BoundedInt { + range: lhs_range, + value: lhs, + }, Value::BoundedInt { + range: rhs_range, + value: rhs, + }]: [Value; 3] = args.try_into().unwrap() + else { + panic!() + }; + assert_eq!(lhs_range.start, info.lhs.lower); + assert_eq!(lhs_range.end, info.lhs.upper); + assert_eq!(rhs_range.start, info.rhs.lower); + assert_eq!(rhs_range.end, info.rhs.upper); + + let quo = &lhs / &rhs; + let rem = lhs % rhs; + + let quo_range = match registry + .get_type(&info.branch_signatures()[0].vars[1].ty) + .unwrap() + { + CoreTypeConcrete::BoundedInt(info) => info.range.lower.clone()..info.range.upper.clone(), + _ => unreachable!(), + }; + let rem_range = match registry + .get_type(&info.branch_signatures()[0].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::BoundedInt(info) => info.range.lower.clone()..info.range.upper.clone(), + _ => unreachable!(), + }; + assert!(quo_range.contains(&quo)); + assert!(rem_range.contains(&rem)); + + EvalAction::NormalBranch( + 0, + smallvec![ + range_check, + Value::BoundedInt { + range: quo_range, + value: quo, + }, + Value::BoundedInt { + range: rem_range, + value: rem, + }, + ], + ) +} + +pub fn eval_constrain( + registry: &ProgramRegistry, + info: &BoundedIntConstrainConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, value]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + let value = match value { + Value::I8(value) => value.into(), + _ => todo!(), + }; + + if value < info.boundary { + let range = match registry + .get_type(&info.branch_signatures()[0].vars[1].ty) + .unwrap() + { + CoreTypeConcrete::BoundedInt(info) => { + info.range.lower.clone()..info.range.upper.clone() + } + CoreTypeConcrete::NonZero(info) => match registry.get_type(&info.ty).unwrap() { + CoreTypeConcrete::BoundedInt(info) => { + info.range.lower.clone()..info.range.upper.clone() + } + _ => unreachable!(), + }, + _ => unreachable!(), + }; + + EvalAction::NormalBranch( + 0, + smallvec![range_check, Value::BoundedInt { range, value }], + ) + } else { + let range = match registry + .get_type(&info.branch_signatures()[1].vars[1].ty) + .unwrap() + { + CoreTypeConcrete::BoundedInt(info) => { + info.range.lower.clone()..info.range.upper.clone() + } + CoreTypeConcrete::NonZero(info) => match registry.get_type(&info.ty).unwrap() { + CoreTypeConcrete::BoundedInt(info) => { + info.range.lower.clone()..info.range.upper.clone() + } + _ => unreachable!(), + }, + _ => unreachable!(), + }; + + EvalAction::NormalBranch( + 1, + smallvec![range_check, Value::BoundedInt { range, value }], + ) + } +} + +pub fn eval_is_zero( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + let is_zero = match value { + Value::I8(value) => value == 0, + _ => todo!(), + }; + + if is_zero { + EvalAction::NormalBranch(0, smallvec![]) + } else { + EvalAction::NormalBranch(1, smallvec![value]) + } +} + +pub fn eval_wrap_non_zero( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![value]) +} + +pub fn eval_trim( + registry: &ProgramRegistry, + info: &BoundedIntTrimConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + let value = match value { + Value::I8(v) => BigInt::from(v), + Value::I16(v) => BigInt::from(v), + Value::I32(v) => BigInt::from(v), + Value::I64(v) => BigInt::from(v), + Value::I128(v) => BigInt::from(v), + Value::U8(v) => BigInt::from(v), + Value::U16(v) => BigInt::from(v), + Value::U32(v) => BigInt::from(v), + Value::U64(v) => BigInt::from(v), + Value::U128(v) => BigInt::from(v), + _ => panic!("Not a valid integer type"), + }; + let is_invalid = value == info.trimmed_value; + let int_range = match registry + .get_type(&info.branch_signatures()[1].vars[0].ty) + .unwrap() + { + CoreTypeConcrete::BoundedInt(info) => info.range.clone(), + _ => panic!("should be bounded int"), + }; + + if !is_invalid { + let range = int_range.lower.clone()..int_range.upper.clone(); + EvalAction::NormalBranch(1, smallvec![Value::BoundedInt { range, value }]) + } else { + EvalAction::NormalBranch(0, smallvec![]) + } +} + +#[cfg(test)] +mod tests { + + use num_bigint::BigInt; + + use super::Value; + + use crate::{load_cairo, test_utils::run_test_program}; + + #[test] + fn test_trim_i8() { + let (_, program) = load_cairo!( + use core::internal::{OptionRev, bounded_int::BoundedInt}; + use core::internal::bounded_int; + fn main() -> BoundedInt<-127, 127> { + let num = match bounded_int::trim_min::(1) { + OptionRev::Some(n) => n, + OptionRev::None => 1, + }; + + num + } + ); + + let result = run_test_program(program); + let result = result.last().unwrap(); + let expected = Value::BoundedInt { + range: BigInt::from(-127)..BigInt::from(128), + value: BigInt::from(1u8), + }; + + assert_eq!(*result, expected); + } + + #[test] + fn test_trim_u32() { + let (_, program) = load_cairo!( + use core::internal::{OptionRev, bounded_int::BoundedInt}; + use core::internal::bounded_int; + fn main() -> BoundedInt<0, 4294967294> { + let num = match bounded_int::trim_max::(0xfffffffe) { + OptionRev::Some(n) => n, + OptionRev::None => 0, + }; + + num + } + ); + + let result = run_test_program(program); + let result = result.last().unwrap(); + let expected = Value::BoundedInt { + range: BigInt::from(0)..BigInt::from(4294967295u32), + value: BigInt::from(0xfffffffeu32), + }; + + assert_eq!(*result, expected); + } + + #[test] + fn test_trim_none() { + let (_, program) = load_cairo!( + use core::internal::{OptionRev, bounded_int::BoundedInt}; + use core::internal::bounded_int; + fn main() -> BoundedInt<-32767, 32767> { + let num = match bounded_int::trim_min::(-0x8000) { + OptionRev::Some(n) => n, + OptionRev::None => 0, + }; + + num + } + ); + + let result = run_test_program(program); + let result = result.last().unwrap(); + let expected = Value::BoundedInt { + range: BigInt::from(-32767)..BigInt::from(32768), + value: BigInt::from(0), + }; + + assert_eq!(*result, expected); + } +} diff --git a/debug_utils/sierra-emu/src/vm/box.rs b/debug_utils/sierra-emu/src/vm/box.rs new file mode 100644 index 0000000000..4504365de3 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/box.rs @@ -0,0 +1,53 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + boxing::BoxConcreteLibfunc, + core::{CoreLibfunc, CoreType}, + lib_func::SignatureAndTypeConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &BoxConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + BoxConcreteLibfunc::Into(info) => eval_into_box(registry, info, args), + BoxConcreteLibfunc::Unbox(info) => eval_unbox(registry, info, args), + BoxConcreteLibfunc::ForwardSnapshot(info) => eval_forward_snapshot(registry, info, args), + } +} + +pub fn eval_unbox( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![value]) +} + +pub fn eval_into_box( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![value]) +} + +pub fn eval_forward_snapshot( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![value]) +} diff --git a/debug_utils/sierra-emu/src/vm/branch_align.rs b/debug_utils/sierra-emu/src/vm/branch_align.rs new file mode 100644 index 0000000000..a37881ebaa --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/branch_align.rs @@ -0,0 +1,20 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![]) +} diff --git a/debug_utils/sierra-emu/src/vm/bytes31.rs b/debug_utils/sierra-emu/src/vm/bytes31.rs new file mode 100644 index 0000000000..5d372598aa --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/bytes31.rs @@ -0,0 +1,65 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + bytes31::Bytes31ConcreteLibfunc, + consts::SignatureAndConstConcreteLibfunc, + core::{CoreLibfunc, CoreType}, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use num_bigint::BigInt; +use smallvec::smallvec; +use starknet_crypto::Felt; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Bytes31ConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + Bytes31ConcreteLibfunc::Const(info) => eval_const(registry, info, args), + Bytes31ConcreteLibfunc::ToFelt252(info) => eval_to_felt252(registry, info, args), + Bytes31ConcreteLibfunc::TryFromFelt252(info) => eval_from_felt(registry, info, args), + } +} + +pub fn eval_const( + _registry: &ProgramRegistry, + info: &SignatureAndConstConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::Bytes31(info.c.clone().into())]) +} + +pub fn eval_from_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Felt(value)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + let max = Felt::from(BigInt::from(2).pow(248) - 1); + + if value <= max { + EvalAction::NormalBranch(0, smallvec![range_check, Value::Bytes31(value)]) + } else { + EvalAction::NormalBranch(1, smallvec![range_check]) + } +} + +pub fn eval_to_felt252( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Bytes31(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch(0, smallvec![Value::Felt(value)]) +} diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs new file mode 100644 index 0000000000..fd1f15f6fc --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -0,0 +1,107 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + casts::{CastConcreteLibfunc, DowncastConcreteLibfunc}, + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + lib_func::SignatureOnlyConcreteLibfunc, + ConcreteType, + }, + program_registry::ProgramRegistry, +}; +use num_bigint::BigInt; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &CastConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + CastConcreteLibfunc::Downcast(info) => eval_downcast(registry, info, args), + CastConcreteLibfunc::Upcast(info) => eval_upcast(registry, info, args), + } +} + +pub fn eval_downcast( + registry: &ProgramRegistry, + info: &DowncastConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, value]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + let value = match value { + Value::BoundedInt { value, .. } => value, + Value::U128(value) => BigInt::from(value), + Value::U64(value) => BigInt::from(value), + Value::U32(value) => BigInt::from(value), + Value::U16(value) => BigInt::from(value), + Value::U8(value) => BigInt::from(value), + _ => todo!(), + }; + + let range = info.to_range.lower.clone()..info.to_range.upper.clone(); + if range.contains(&value) { + EvalAction::NormalBranch( + 0, + smallvec![ + range_check, + match registry.get_type(&info.to_ty).unwrap() { + CoreTypeConcrete::Sint8(_) => Value::I8(value.try_into().unwrap()), + CoreTypeConcrete::Sint128(_) => Value::I128(value.try_into().unwrap()), + CoreTypeConcrete::Uint8(_) => Value::U8(value.try_into().unwrap()), + CoreTypeConcrete::Uint16(_) => Value::U16(value.try_into().unwrap()), + CoreTypeConcrete::Uint32(_) => Value::U32(value.try_into().unwrap()), + CoreTypeConcrete::Uint64(_) => Value::U64(value.try_into().unwrap()), + CoreTypeConcrete::Uint128(_) => Value::U128(value.try_into().unwrap()), + CoreTypeConcrete::BoundedInt(_) => Value::BoundedInt { range, value }, + x => todo!("{:?}", x.info()), + } + ], + ) + } else { + EvalAction::NormalBranch(1, smallvec![range_check]) + } +} + +pub fn eval_upcast( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + + let value = match value { + Value::BoundedInt { value, .. } => value, + Value::U128(value) => BigInt::from(value), + Value::U64(value) => BigInt::from(value), + Value::U32(value) => BigInt::from(value), + Value::U16(value) => BigInt::from(value), + Value::U8(value) => BigInt::from(value), + _ => todo!(), + }; + + EvalAction::NormalBranch( + 0, + smallvec![match registry + .get_type(&info.signature.branch_signatures[0].vars[0].ty) + .unwrap() + { + CoreTypeConcrete::Sint8(_) => Value::I8(value.try_into().unwrap()), + CoreTypeConcrete::Sint32(_) => Value::I32(value.try_into().unwrap()), + CoreTypeConcrete::Sint128(_) => Value::I128(value.try_into().unwrap()), + CoreTypeConcrete::Uint8(_) => Value::U8(value.try_into().unwrap()), + CoreTypeConcrete::Uint16(_) => Value::U16(value.try_into().unwrap()), + CoreTypeConcrete::Uint32(_) => Value::U32(value.try_into().unwrap()), + CoreTypeConcrete::Uint64(_) => Value::U64(value.try_into().unwrap()), + CoreTypeConcrete::Uint128(_) => Value::U128(value.try_into().unwrap()), + CoreTypeConcrete::Felt252(_) => Value::Felt(value.into()), + CoreTypeConcrete::Sint16(_) => todo!("Sint16"), + CoreTypeConcrete::Sint64(_) => todo!("Sint64"), + CoreTypeConcrete::BoundedInt(_) => todo!("BoundedInt"), + _ => todo!(), + }], + ) +} diff --git a/debug_utils/sierra-emu/src/vm/circuit.rs b/debug_utils/sierra-emu/src/vm/circuit.rs new file mode 100644 index 0000000000..17166148d2 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/circuit.rs @@ -0,0 +1,352 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + circuit::{ + CircuitConcreteLibfunc, CircuitTypeConcrete, ConcreteGetOutputLibFunc, + ConcreteU96LimbsLessThanGuaranteeVerifyLibfunc, + }, + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + lib_func::{SignatureAndTypeConcreteLibfunc, SignatureOnlyConcreteLibfunc}, + }, + program_registry::ProgramRegistry, +}; +use num_bigint::{BigInt, BigUint, Sign, ToBigInt}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &CircuitConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + CircuitConcreteLibfunc::AddInput(info) => eval_add_input(registry, info, args), + CircuitConcreteLibfunc::Eval(info) => eval_eval(registry, info, args), + CircuitConcreteLibfunc::GetDescriptor(info) => eval_get_descriptor(registry, info, args), + CircuitConcreteLibfunc::InitCircuitData(info) => { + eval_init_circuit_data(registry, info, args) + } + CircuitConcreteLibfunc::GetOutput(info) => eval_get_output(registry, info, args), + CircuitConcreteLibfunc::TryIntoCircuitModulus(info) => { + eval_try_into_circuit_modulus(registry, info, args) + } + CircuitConcreteLibfunc::FailureGuaranteeVerify(info) => { + eval_failure_guarantee_verify(registry, info, args) + } + CircuitConcreteLibfunc::IntoU96Guarantee(info) => { + eval_into_u96_guarantee(registry, info, args) + } + CircuitConcreteLibfunc::U96GuaranteeVerify(info) => { + eval_u96_guarantee_verify(registry, info, args) + } + CircuitConcreteLibfunc::U96LimbsLessThanGuaranteeVerify(info) => { + eval_u96_limbs_less_than_guarantee_verify(registry, info, args) + } + CircuitConcreteLibfunc::U96SingleLimbLessThanGuaranteeVerify(info) => { + eval_u96_single_limb_less_than_guarantee_verify(registry, info, args) + } + } +} + +pub fn eval_add_input( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Circuit(mut values), Value::Struct(members)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + assert_ne!(values.len(), values.capacity()); + + let [Value::U128(l0), Value::U128(l1), Value::U128(l2), Value::U128(l3)]: [Value; 4] = + members.try_into().unwrap() + else { + panic!() + }; + + let l0 = l0.to_le_bytes(); + let l1 = l1.to_le_bytes(); + let l2 = l2.to_le_bytes(); + let l3 = l3.to_le_bytes(); + values.push(BigUint::from_bytes_le(&[ + l0[0], l0[1], l0[2], l0[3], l0[4], l0[5], l0[6], l0[7], l0[8], l0[9], l0[10], + l0[11], // + l1[0], l1[1], l1[2], l1[3], l1[4], l1[5], l1[6], l1[7], l1[8], l1[9], l1[10], + l1[11], // + l2[0], l2[1], l2[2], l2[3], l2[4], l2[5], l2[6], l2[7], l2[8], l2[9], l2[10], + l2[11], // + l3[0], l3[1], l3[2], l3[3], l3[4], l3[5], l3[6], l3[7], l3[8], l3[9], l3[10], + l3[11], // + ])); + + EvalAction::NormalBranch( + (values.len() != values.capacity()) as usize, + smallvec![Value::Circuit(values)], + ) +} + +pub fn eval_eval( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + _args: Vec, +) -> EvalAction { + let [add_mod @ Value::Unit, mul_mod @ Value::Unit, _descripctor @ Value::Unit, Value::Circuit(inputs), Value::CircuitModulus(modulus), _, _]: [Value; 7] = _args.try_into().unwrap() + else { + panic!() + }; + let circ_info = match _registry.get_type(&_info.ty).unwrap() { + CoreTypeConcrete::Circuit(CircuitTypeConcrete::Circuit(info)) => &info.circuit_info, + _ => todo!(), + }; + let mut outputs = vec![None; 1 + circ_info.n_inputs + circ_info.values.len()]; + let mut add_gates = circ_info.add_offsets.iter().peekable(); + let mut mul_gates = circ_info.mul_offsets.iter(); + + outputs[0] = Some(BigUint::from(1_u8)); + + for (i, input) in inputs.iter().enumerate() { + outputs[i + 1] = Some(input.to_owned()); + } + + let success = loop { + while let Some(add_gate) = add_gates.peek() { + let lhs = outputs[add_gate.lhs].to_owned(); + let rhs = outputs[add_gate.rhs].to_owned(); + + match (lhs, rhs) { + (Some(l), Some(r)) => { + outputs[add_gate.output] = Some((l + r) % &modulus); + } + (None, Some(r)) => { + let res = match outputs[add_gate.output].to_owned() { + Some(res) => res, + None => break, + }; + // if it is a sub_gate the output index is store in lhs + outputs[add_gate.lhs] = Some((res + &modulus - r) % &modulus); + } + // there aren't enough gates computed for add_gate to compute + // the next gate so we need to compute a mul_gate + _ => break, + }; + + add_gates.next(); + } + + match mul_gates.next() { + Some(mul_gate) => { + let lhs = outputs[mul_gate.lhs].to_owned(); + let rhs = outputs[mul_gate.rhs].to_owned(); + + match (lhs, rhs) { + (Some(l), Some(r)) => { + outputs[mul_gate.output] = Some((l * r) % &modulus); + } + (None, Some(r)) => { + let res = match r.modinv(&modulus) { + Some(inv) => inv, + None => { + panic!("attempt to divide by 0"); + } + }; + // if it is a inv_gate the output index is store in lhs + outputs[mul_gate.lhs] = Some(res); + } + // this state should not be reached since it would mean that + // not all the circuit's inputs where filled + _ => unreachable!(), + } + } + None => break true, + } + }; + + let values = outputs + .into_iter() + .skip(1 + circ_info.n_inputs) + .collect::>>() + .expect("The circuit cannot be calculated"); + + if success { + EvalAction::NormalBranch( + 0, + smallvec![add_mod, mul_mod, Value::CircuitOutputs(values)], + ) + } else { + EvalAction::NormalBranch(1, smallvec![add_mod, mul_mod, Value::Unit, Value::Unit]) + } +} + +pub fn eval_get_output( + _registry: &ProgramRegistry, + _info: &ConcreteGetOutputLibFunc, + args: Vec, +) -> EvalAction { + let [Value::CircuitOutputs(outputs)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + let circuit_info = match _registry.get_type(&_info.circuit_ty).unwrap() { + CoreTypeConcrete::Circuit(CircuitTypeConcrete::Circuit(info)) => &info.circuit_info, + _ => todo!(), + }; + let gate_offset = *circuit_info.values.get(&_info.output_ty).unwrap(); + let output_idx = gate_offset - 1 - circuit_info.n_inputs; + let output = outputs[output_idx].to_owned(); + let output_big = output.to_bigint().unwrap(); + + let mask: BigInt = BigInt::from_bytes_be(Sign::Plus, &[255; 12]); + + let l0: BigInt = &output_big & &mask; + let l1: BigInt = (&output_big >> 96) & &mask; + let l2: BigInt = (&output_big >> 192) & &mask; + let l3: BigInt = (output_big >> 288) & &mask; + + let range = BigInt::ZERO..(BigInt::from(1) << 96); + let vec_values = vec![ + Value::BoundedInt { + range: range.clone(), + value: l0, + }, + Value::BoundedInt { + range: range.clone(), + value: l1, + }, + Value::BoundedInt { + range: range.clone(), + value: l2, + }, + Value::BoundedInt { range, value: l3 }, + ]; + + EvalAction::NormalBranch(0, smallvec![Value::Struct(vec_values), Value::Unit]) +} + +pub fn eval_u96_limbs_less_than_guarantee_verify( + _registry: &ProgramRegistry, + _info: &ConcreteU96LimbsLessThanGuaranteeVerifyLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::Unit]) +} + +pub fn eval_u96_single_limb_less_than_guarantee_verify( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::U128(0)]) +} + +pub fn eval_u96_guarantee_verify( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + _args: Vec, +) -> EvalAction { + let [range_check_96 @ Value::Unit, _]: [Value; 2] = _args.try_into().unwrap() else { + panic!() + }; + EvalAction::NormalBranch(0, smallvec![range_check_96]) +} + +pub fn eval_failure_guarantee_verify( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + _args: Vec, +) -> EvalAction { + let [rc96 @ Value::Unit, mul_mod @ Value::Unit, _, _, _]: [Value; 5] = + _args.try_into().unwrap() + else { + panic!() + }; + + EvalAction::NormalBranch(0, smallvec![rc96, mul_mod, Value::Unit]) +} + +pub fn eval_get_descriptor( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::Unit]) +} + +pub fn eval_init_circuit_data( + _registry: &ProgramRegistry, + info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check_96 @ Value::Unit]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let num_inputs = match _registry.get_type(&info.ty).unwrap() { + CoreTypeConcrete::Circuit(CircuitTypeConcrete::Circuit(info)) => info.circuit_info.n_inputs, + _ => todo!("{}", info.ty), + }; + + EvalAction::NormalBranch( + 0, + smallvec![ + range_check_96, + Value::Circuit(Vec::with_capacity(num_inputs)), + ], + ) +} + +pub fn eval_try_into_circuit_modulus( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Struct(members)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let [Value::BoundedInt { + range: r0, + value: l0, + }, Value::BoundedInt { + range: r1, + value: l1, + }, Value::BoundedInt { + range: r2, + value: l2, + }, Value::BoundedInt { + range: r3, + value: l3, + }]: [Value; 4] = members.try_into().unwrap() + else { + panic!() + }; + assert_eq!(r0, BigInt::ZERO..(BigInt::from(1) << 96)); + assert_eq!(r1, BigInt::ZERO..(BigInt::from(1) << 96)); + assert_eq!(r2, BigInt::ZERO..(BigInt::from(1) << 96)); + assert_eq!(r3, BigInt::ZERO..(BigInt::from(1) << 96)); + + let l0 = l0.to_biguint().unwrap(); + let l1 = l1.to_biguint().unwrap(); + let l2 = l2.to_biguint().unwrap(); + let l3 = l3.to_biguint().unwrap(); + + let value = l0 | (l1 << 96) | (l2 << 192) | (l3 << 288); + + // a CircuitModulus must not be neither 0 nor 1 + assert_ne!(value, 0_u8.into()); + assert_ne!(value, 1_u8.into()); + + EvalAction::NormalBranch(0, smallvec![Value::CircuitModulus(value)]) +} + +pub fn eval_into_u96_guarantee( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::BoundedInt { range, value }]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + assert_eq!(range, BigInt::ZERO..(BigInt::from(1) << 96)); + + EvalAction::NormalBranch(0, smallvec![Value::U128(value.try_into().unwrap())]) +} diff --git a/debug_utils/sierra-emu/src/vm/const.rs b/debug_utils/sierra-emu/src/vm/const.rs new file mode 100644 index 0000000000..be6c24e785 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/const.rs @@ -0,0 +1,165 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + const_type::{ + ConstAsBoxConcreteLibfunc, ConstAsImmediateConcreteLibfunc, ConstConcreteLibfunc, + }, + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + }, + ids::ConcreteTypeId, + program::GenericArg, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &ConstConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + ConstConcreteLibfunc::AsBox(info) => eval_as_box(registry, info, args), + ConstConcreteLibfunc::AsImmediate(info) => eval_as_immediate(registry, info, args), + } +} + +pub fn eval_as_immediate( + registry: &ProgramRegistry, + info: &ConstAsImmediateConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [] = args.try_into().unwrap(); + + let const_ty = match registry.get_type(&info.const_type).unwrap() { + CoreTypeConcrete::Const(x) => x, + _ => unreachable!(), + }; + EvalAction::NormalBranch( + 0, + smallvec![inner(registry, &const_ty.inner_ty, &const_ty.inner_data)], + ) +} + +pub fn eval_as_box( + registry: &ProgramRegistry, + info: &ConstAsBoxConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [] = args.try_into().unwrap(); + + let const_ty = match registry.get_type(&info.const_type).unwrap() { + CoreTypeConcrete::Const(x) => x, + _ => unreachable!(), + }; + EvalAction::NormalBranch( + 0, + smallvec![inner(registry, &const_ty.inner_ty, &const_ty.inner_data)], + ) +} + +fn inner( + registry: &ProgramRegistry, + type_id: &ConcreteTypeId, + inner_data: &[GenericArg], +) -> Value { + match registry.get_type(type_id).unwrap() { + CoreTypeConcrete::BoundedInt(info) => match inner_data { + [GenericArg::Type(type_id)] => match registry.get_type(type_id).unwrap() { + CoreTypeConcrete::Const(info) => inner(registry, &info.inner_ty, &info.inner_data), + _ => unreachable!(), + }, + [GenericArg::Value(value)] => { + assert!(value >= &info.range.lower && value < &info.range.upper); + Value::BoundedInt { + range: info.range.lower.clone()..info.range.upper.clone(), + value: value.clone(), + } + } + _ => unreachable!(), + }, + CoreTypeConcrete::Felt252(_) => match inner_data { + [GenericArg::Value(value)] => Value::Felt(value.into()), + _ => unreachable!(), + }, + CoreTypeConcrete::NonZero(_) => match inner_data { + [GenericArg::Type(type_id)] => match registry.get_type(type_id).unwrap() { + CoreTypeConcrete::Const(info) => inner(registry, &info.inner_ty, &info.inner_data), + _ => unreachable!(), + }, + _ => unreachable!(), + }, + CoreTypeConcrete::Sint128(_) => match inner_data { + [GenericArg::Value(value)] => Value::I128(value.try_into().unwrap()), + _ => unreachable!(), + }, + CoreTypeConcrete::Sint64(_) => match inner_data { + [GenericArg::Value(value)] => Value::U64(value.try_into().unwrap()), + _ => unreachable!(), + }, + CoreTypeConcrete::Sint32(_) => match inner_data { + [GenericArg::Value(value)] => Value::I32(value.try_into().unwrap()), + _ => unreachable!(), + }, + CoreTypeConcrete::Sint16(_) => match inner_data { + [GenericArg::Value(value)] => Value::I16(value.try_into().unwrap()), + _ => unreachable!(), + }, + CoreTypeConcrete::Sint8(_) => match inner_data { + [GenericArg::Value(value)] => Value::I8(value.try_into().unwrap()), + _ => unreachable!(), + }, + CoreTypeConcrete::Uint128(_) => match inner_data { + [GenericArg::Value(value)] => Value::U128(value.try_into().unwrap()), + [GenericArg::Type(type_id)] => match registry.get_type(type_id).unwrap() { + CoreTypeConcrete::Const(info) => inner(registry, &info.inner_ty, &info.inner_data), + _ => unreachable!(), + }, + _ => unreachable!(), + }, + CoreTypeConcrete::Uint64(_) => match inner_data { + [GenericArg::Value(value)] => Value::U64(value.try_into().unwrap()), + _ => unreachable!(), + }, + CoreTypeConcrete::Uint32(_) => match inner_data { + [GenericArg::Value(value)] => Value::U32(value.try_into().unwrap()), + [GenericArg::Type(type_id)] => match registry.get_type(type_id).unwrap() { + CoreTypeConcrete::Const(info) => inner(registry, &info.inner_ty, &info.inner_data), + _ => unreachable!(), + }, + _ => unreachable!(), + }, + CoreTypeConcrete::Uint16(_) => match inner_data { + [GenericArg::Value(value)] => Value::U16(value.try_into().unwrap()), + _ => unreachable!(), + }, + CoreTypeConcrete::Uint8(_) => match inner_data { + [GenericArg::Value(value)] => Value::U8(value.try_into().unwrap()), + _ => unreachable!(), + }, + CoreTypeConcrete::Struct(_) => { + let mut fields = Vec::new(); + + for field in inner_data { + match field { + GenericArg::Type(const_field_ty) => { + let field_type = registry.get_type(const_field_ty).unwrap(); + + match &field_type { + CoreTypeConcrete::Const(const_ty) => { + let field_value = + inner(registry, &const_ty.inner_ty, &const_ty.inner_data); + fields.push(field_value); + } + _ => unreachable!(), + }; + } + _ => unreachable!(), + } + } + + Value::Struct(fields) + } + _ => todo!("{}", type_id), + } +} diff --git a/debug_utils/sierra-emu/src/vm/drop.rs b/debug_utils/sierra-emu/src/vm/drop.rs new file mode 100644 index 0000000000..3c8142a1e0 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/drop.rs @@ -0,0 +1,20 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [_value] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![]) +} diff --git a/debug_utils/sierra-emu/src/vm/dup.rs b/debug_utils/sierra-emu/src/vm/dup.rs new file mode 100644 index 0000000000..117ef70b62 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/dup.rs @@ -0,0 +1,20 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![value.clone(), value]) +} diff --git a/debug_utils/sierra-emu/src/vm/ec.rs b/debug_utils/sierra-emu/src/vm/ec.rs new file mode 100644 index 0000000000..64ab9828ab --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/ec.rs @@ -0,0 +1,260 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + ec::EcConcreteLibfunc, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use num_traits::identities::Zero; +use rand::Rng; +use smallvec::smallvec; +use starknet_crypto::Felt; +use starknet_curve::curve_params::BETA; +use starknet_types_core::curve::{AffinePoint, ProjectivePoint}; +use std::ops::Mul; +use std::ops::Neg; + +// todo: verify these are correct. + +pub fn eval( + registry: &ProgramRegistry, + selector: &EcConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + EcConcreteLibfunc::IsZero(info) => eval_is_zero(registry, info, args), + EcConcreteLibfunc::Neg(info) => eval_neg(registry, info, args), + EcConcreteLibfunc::StateAdd(info) => eval_state_add(registry, info, args), + EcConcreteLibfunc::TryNew(info) => eval_new(registry, info, args), + EcConcreteLibfunc::StateFinalize(info) => eval_state_finalize(registry, info, args), + EcConcreteLibfunc::StateInit(info) => eval_state_init(registry, info, args), + EcConcreteLibfunc::StateAddMul(info) => eval_state_add_mul(registry, info, args), + EcConcreteLibfunc::PointFromX(info) => eval_point_from_x(registry, info, args), + EcConcreteLibfunc::UnwrapPoint(info) => eval_unwrap_point(registry, info, args), + EcConcreteLibfunc::Zero(_) => todo!(), + } +} + +pub fn eval_is_zero( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value @ Value::EcPoint { x: _, y }]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + // To check whether `(x, y) = (0, 0)` (the zero point), it is enough to check + // whether `y = 0`, since there is no point on the curve with y = 0. + if y.is_zero() { + EvalAction::NormalBranch(0, smallvec![]) + } else { + EvalAction::NormalBranch(1, smallvec![value]) + } +} + +pub fn eval_unwrap_point( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::EcPoint { x, y }]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + EvalAction::NormalBranch(0, smallvec![Value::Felt(x), Value::Felt(y)]) +} + +pub fn eval_neg( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::EcPoint { x, y }]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let point = AffinePoint::new(x, y).unwrap().neg(); + + EvalAction::NormalBranch( + 0, + smallvec![Value::EcPoint { + x: point.x(), + y: point.y(), + }], + ) +} + +pub fn eval_new( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Felt(x), Value::Felt(y)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + match AffinePoint::new(x, y) { + Ok(point) => EvalAction::NormalBranch( + 0, + smallvec![Value::EcPoint { + x: point.x(), + y: point.y(), + }], + ), + Err(_) => EvalAction::NormalBranch(1, smallvec![]), + } +} + +pub fn eval_state_init( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + _args: Vec, +) -> EvalAction { + let state = random_ec_point(); + + EvalAction::NormalBranch( + 0, + smallvec![Value::EcState { + x0: state.x(), + y0: state.y(), + x1: state.x(), + y1: state.y(), + }], + ) +} + +pub fn eval_state_add( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::EcState { x0, y0, x1, y1 }, Value::EcPoint { x, y }]: [Value; 2] = + args.try_into().unwrap() + else { + panic!() + }; + + let mut state = ProjectivePoint::from_affine(x0, y0).unwrap(); + let point = AffinePoint::new(x, y).unwrap(); + + state += &point; + let state = state.to_affine().unwrap(); + + EvalAction::NormalBranch( + 0, + smallvec![Value::EcState { + x0: state.x(), + y0: state.y(), + x1, + y1 + }], + ) +} + +pub fn eval_state_add_mul( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [ec @ Value::Unit, Value::EcState { x0, y0, x1, y1 }, Value::Felt(scalar), Value::EcPoint { x, y }]: [Value; 4] = + args.try_into().unwrap() + else { + panic!() + }; + + let mut state = ProjectivePoint::from_affine(x0, y0).unwrap(); + let point = ProjectivePoint::from_affine(x, y).unwrap(); + + state += &point.mul(scalar); + let state = state.to_affine().unwrap(); + + EvalAction::NormalBranch( + 0, + smallvec![ + ec, + Value::EcState { + x0: state.x(), + y0: state.y(), + x1, + y1 + } + ], + ) +} + +pub fn eval_state_finalize( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::EcState { x0, y0, x1, y1 }]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let state = ProjectivePoint::from_affine(x0, y0).unwrap(); + let random_point = ProjectivePoint::from_affine(x1, y1).unwrap(); + + if state.x() == random_point.x() && state.y() == random_point.y() { + EvalAction::NormalBranch(1, smallvec![]) + } else { + let point = &state - &random_point; + let point = point.to_affine().unwrap(); + EvalAction::NormalBranch( + 0, + smallvec![Value::EcPoint { + x: point.x(), + y: point.y(), + }], + ) + } +} + +pub fn eval_point_from_x( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Felt(x)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + // https://github.com/starkware-libs/cairo/blob/aaad921bba52e729dc24ece07fab2edf09ccfa15/crates/cairo-lang-sierra-to-casm/src/invocations/ec.rs#L63 + + let x2 = x * x; + let x3 = x2 * x; + let alpha_x_plus_beta = x + BETA; + let rhs = x3 + alpha_x_plus_beta; + let y = rhs.sqrt().unwrap_or_else(|| Felt::from(3) * rhs); + + match AffinePoint::new(x, y) { + Ok(point) => EvalAction::NormalBranch( + 0, + smallvec![ + range_check, + Value::EcPoint { + x: point.x(), + y: point.y(), + } + ], + ), + Err(_) => EvalAction::NormalBranch(1, smallvec![range_check]), + } +} + +fn random_ec_point() -> AffinePoint { + // https://github.com/starkware-libs/cairo/blob/aaad921bba52e729dc24ece07fab2edf09ccfa15/crates/cairo-lang-runner/src/casm_run/mod.rs#L1802 + let mut rng = rand::thread_rng(); + let (random_x, random_y) = loop { + // Randominzing 31 bytes to make sure is in range. + let x_bytes: [u8; 31] = rng.gen(); + let random_x = Felt::from_bytes_be_slice(&x_bytes); + let random_y_squared = random_x * random_x * random_x + random_x + BETA; + if let Some(random_y) = random_y_squared.sqrt() { + break (random_x, random_y); + } + }; + + AffinePoint::new(random_x, random_y).unwrap() +} diff --git a/debug_utils/sierra-emu/src/vm/enum.rs b/debug_utils/sierra-emu/src/vm/enum.rs new file mode 100644 index 0000000000..5fce56b98f --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/enum.rs @@ -0,0 +1,127 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + enm::{ + EnumConcreteLibfunc, EnumConcreteType, EnumFromBoundedIntConcreteLibfunc, + EnumInitConcreteLibfunc, + }, + lib_func::SignatureOnlyConcreteLibfunc, + ConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &EnumConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + EnumConcreteLibfunc::Init(info) => eval_init(registry, info, args), + EnumConcreteLibfunc::FromBoundedInt(info) => eval_from_bounded_int(registry, info, args), + EnumConcreteLibfunc::Match(info) => eval_match(registry, info, args), + EnumConcreteLibfunc::SnapshotMatch(info) => eval_snapshot_match(registry, info, args), + } +} + +pub fn eval_init( + registry: &ProgramRegistry, + info: &EnumInitConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + + let self_ty = &info.signature.branch_signatures[0].vars[0].ty; + let CoreTypeConcrete::Enum(EnumConcreteType { variants, .. }) = + registry.get_type(self_ty).unwrap() + else { + panic!() + }; + assert_eq!(info.n_variants, variants.len()); + assert!(info.index < info.n_variants); + assert!(value.is(registry, &variants[info.index])); + + EvalAction::NormalBranch( + 0, + smallvec![Value::Enum { + self_ty: self_ty.clone(), + index: info.index, + payload: Box::new(value), + }], + ) +} + +pub fn eval_from_bounded_int( + _registry: &ProgramRegistry, + info: &EnumFromBoundedIntConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::BoundedInt { range: _, value }]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let enm = Value::Enum { + self_ty: info.branch_signatures()[0].vars[0].ty.clone(), + index: value.try_into().unwrap(), + payload: Box::new(Value::Struct(vec![])), + }; + + EvalAction::NormalBranch(0, smallvec![enm]) +} + +pub fn eval_match( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Enum { + self_ty, + index, + payload, + }]: [Value; 1] = args.try_into().unwrap() + else { + panic!() + }; + assert_eq!(self_ty, info.signature.param_signatures[0].ty); + assert!(payload.is( + registry, + &info.signature.branch_signatures[index].vars[0].ty + )); + + EvalAction::NormalBranch(index, smallvec![*payload]) +} + +pub fn eval_snapshot_match( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Enum { + self_ty, + index, + payload, + }]: [Value; 1] = args.try_into().unwrap() + else { + panic!() + }; + + let ty = registry + .get_type(&info.signature.param_signatures[0].ty) + .unwrap(); + + if let CoreTypeConcrete::Snapshot(inner) = ty { + assert_eq!(inner.ty, self_ty); + } else { + panic!("expected snapshot type") + } + + assert!(payload.is( + registry, + &info.signature.branch_signatures[index].vars[0].ty + )); + + EvalAction::NormalBranch(index, smallvec![*payload]) +} diff --git a/debug_utils/sierra-emu/src/vm/felt252.rs b/debug_utils/sierra-emu/src/vm/felt252.rs new file mode 100644 index 0000000000..00c0c84ec1 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/felt252.rs @@ -0,0 +1,75 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + felt252::{ + Felt252BinaryOperationConcrete, Felt252BinaryOperator, Felt252Concrete, + Felt252ConstConcreteLibfunc, + }, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; +use starknet_crypto::Felt; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Felt252Concrete, + args: Vec, +) -> EvalAction { + match selector { + Felt252Concrete::BinaryOperation(info) => eval_operation(registry, info, args), + Felt252Concrete::Const(info) => eval_const(registry, info, args), + Felt252Concrete::IsZero(info) => eval_felt_is_zero(registry, info, args), + } +} + +pub fn eval_operation( + _registry: &ProgramRegistry, + info: &Felt252BinaryOperationConcrete, + args: Vec, +) -> EvalAction { + let res = match info { + Felt252BinaryOperationConcrete::WithVar(info) => { + let [Value::Felt(lhs), Value::Felt(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + match info.operator { + Felt252BinaryOperator::Add => lhs + rhs, + Felt252BinaryOperator::Sub => lhs - rhs, + Felt252BinaryOperator::Mul => lhs * rhs, + Felt252BinaryOperator::Div => lhs.field_div(&rhs.try_into().unwrap()), + } + } + Felt252BinaryOperationConcrete::WithConst(_info) => todo!(), + }; + + EvalAction::NormalBranch(0, smallvec![Value::Felt(res)]) +} + +pub fn eval_const( + _registry: &ProgramRegistry, + info: &Felt252ConstConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::Felt(info.c.clone().into())]) +} + +pub fn eval_felt_is_zero( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Felt(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + if value == Felt::ZERO { + EvalAction::NormalBranch(0, smallvec![]) + } else { + EvalAction::NormalBranch(1, smallvec![Value::Felt(value)]) + } +} diff --git a/debug_utils/sierra-emu/src/vm/felt252_dict.rs b/debug_utils/sierra-emu/src/vm/felt252_dict.rs new file mode 100644 index 0000000000..ca17e0e160 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/felt252_dict.rs @@ -0,0 +1,74 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + felt252_dict::Felt252DictConcreteLibfunc, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; +use std::collections::HashMap; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Felt252DictConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + Felt252DictConcreteLibfunc::New(info) => eval_new(registry, info, args), + Felt252DictConcreteLibfunc::Squash(info) => eval_squash(registry, info, args), + } +} + +pub fn eval_new( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [segment_arena @ Value::Unit]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let type_info = registry + .get_type(&info.signature.branch_signatures[0].vars[1].ty) + .unwrap(); + let ty = match type_info { + CoreTypeConcrete::Felt252Dict(info) => &info.ty, + _ => unreachable!(), + }; + + EvalAction::NormalBranch( + 0, + smallvec![ + segment_arena, + Value::FeltDict { + ty: ty.clone(), + data: HashMap::new(), + }, + ], + ) +} + +pub fn eval_squash( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U64(gas_builtin), segment_arena @ Value::Unit, Value::FeltDict { ty, data }]: [Value; 4] = + args.try_into().unwrap() + else { + panic!(); + }; + + EvalAction::NormalBranch( + 0, + smallvec![ + range_check, + Value::U64(gas_builtin), + segment_arena, + Value::FeltDict { ty, data } + ], + ) +} diff --git a/debug_utils/sierra-emu/src/vm/felt252_dict_entry.rs b/debug_utils/sierra-emu/src/vm/felt252_dict_entry.rs new file mode 100644 index 0000000000..b0efb201ff --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/felt252_dict_entry.rs @@ -0,0 +1,59 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + felt252_dict::Felt252DictEntryConcreteLibfunc, + lib_func::SignatureAndTypeConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Felt252DictEntryConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + Felt252DictEntryConcreteLibfunc::Get(info) => eval_get(registry, info, args), + Felt252DictEntryConcreteLibfunc::Finalize(info) => eval_finalize(registry, info, args), + } +} + +pub fn eval_get( + registry: &ProgramRegistry, + info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::FeltDict { ty, data }, Value::Felt(key)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + assert_eq!(info.ty, ty); + + EvalAction::NormalBranch( + 0, + smallvec![ + Value::FeltDictEntry { ty, data, key }, + Value::default_for_type(registry, &info.ty), + ], + ) +} + +pub fn eval_finalize( + registry: &ProgramRegistry, + info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::FeltDictEntry { ty, mut data, key }, value]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + assert_eq!(info.ty, ty); + assert!(value.is(registry, &ty)); + + data.insert(key, value); + + EvalAction::NormalBranch(0, smallvec![Value::FeltDict { ty, data }]) +} diff --git a/debug_utils/sierra-emu/src/vm/function_call.rs b/debug_utils/sierra-emu/src/vm/function_call.rs new file mode 100644 index 0000000000..3b72985445 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/function_call.rs @@ -0,0 +1,23 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + function_call::SignatureAndFunctionConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; + +pub fn eval( + registry: &ProgramRegistry, + info: &SignatureAndFunctionConcreteLibfunc, + args: Vec, +) -> EvalAction { + assert_eq!(args.len(), info.function.params.len()); + assert!(args + .iter() + .zip(&info.function.params) + .all(|(value, param)| value.is(registry, ¶m.ty))); + + EvalAction::FunctionCall(info.function.id.clone(), args.into_iter().collect()) +} diff --git a/debug_utils/sierra-emu/src/vm/gas.rs b/debug_utils/sierra-emu/src/vm/gas.rs new file mode 100644 index 0000000000..28bb661a5c --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/gas.rs @@ -0,0 +1,184 @@ +use super::EvalAction; +use crate::{ + gas::{BuiltinCosts, GasMetadata}, + Value, +}; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + gas::{CostTokenType, GasConcreteLibfunc}, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program::StatementIdx, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &GasConcreteLibfunc, + args: Vec, + gas: &GasMetadata, + statement_idx: StatementIdx, + builtin_costs: BuiltinCosts, +) -> EvalAction { + match selector { + GasConcreteLibfunc::WithdrawGas(info) => { + eval_withdraw_gas(registry, info, args, gas, statement_idx, builtin_costs) + } + GasConcreteLibfunc::RedepositGas(info) => { + eval_redeposit_gas(registry, info, args, gas, statement_idx, builtin_costs) + } + GasConcreteLibfunc::GetAvailableGas(_) => todo!(), + GasConcreteLibfunc::BuiltinWithdrawGas(info) => { + eval_builtin_withdraw_gas(registry, info, args, gas, statement_idx) + } + GasConcreteLibfunc::GetBuiltinCosts(info) => { + eval_get_builtin_costs(registry, info, args, builtin_costs) + } + GasConcreteLibfunc::GetUnspentGas(_) => todo!(), + } +} + +pub fn eval_builtin_withdraw_gas( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, + gas_meta: &GasMetadata, + statement_idx: StatementIdx, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U64(gas), Value::BuiltinCosts(builtin_costs)]: [Value; + 3] = args.try_into().unwrap() + else { + panic!() + }; + + let builtin_costs: [u64; 7] = builtin_costs.into(); + + let gas_cost = gas_meta.get_gas_costs_for_statement(statement_idx); + + let mut total_gas_cost = 0; + + for (cost_count, token_type) in &gas_cost { + if *cost_count == 0 { + continue; + } + + let builtin_costs_index = match token_type { + CostTokenType::Const => 0, + CostTokenType::Pedersen => 1, + CostTokenType::Bitwise => 2, + CostTokenType::EcOp => 3, + CostTokenType::Poseidon => 4, + CostTokenType::AddMod => 5, + CostTokenType::MulMod => 6, + _ => panic!(), + }; + + let cost_value = cost_count * builtin_costs[builtin_costs_index as usize]; + total_gas_cost += cost_value; + } + + let new_gas = gas.saturating_sub(total_gas_cost); + if gas >= total_gas_cost { + EvalAction::NormalBranch(0, smallvec![range_check, Value::U64(new_gas)]) + } else { + EvalAction::NormalBranch(1, smallvec![range_check, Value::U64(gas)]) + } +} + +pub fn eval_withdraw_gas( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, + gas_meta: &GasMetadata, + statement_idx: StatementIdx, + builtin_costs: BuiltinCosts, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U64(gas)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + let builtin_costs: [u64; 7] = builtin_costs.into(); + + let gas_cost = gas_meta.get_gas_costs_for_statement(statement_idx); + + let mut total_gas_cost = 0; + + for (cost_count, token_type) in &gas_cost { + if *cost_count == 0 { + continue; + } + + let builtin_costs_index = match token_type { + CostTokenType::Const => 0, + CostTokenType::Pedersen => 1, + CostTokenType::Bitwise => 2, + CostTokenType::EcOp => 3, + CostTokenType::Poseidon => 4, + CostTokenType::AddMod => 5, + CostTokenType::MulMod => 6, + _ => panic!(), + }; + + let cost_value = cost_count * builtin_costs[builtin_costs_index as usize]; + total_gas_cost += cost_value; + } + + let new_gas = gas.saturating_sub(total_gas_cost); + if gas >= total_gas_cost { + EvalAction::NormalBranch(0, smallvec![range_check, Value::U64(new_gas)]) + } else { + EvalAction::NormalBranch(1, smallvec![range_check, Value::U64(gas)]) + } +} + +pub fn eval_redeposit_gas( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, + gas_meta: &GasMetadata, + statement_idx: StatementIdx, + builtin_costs: BuiltinCosts, +) -> EvalAction { + let [Value::U64(gas)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let builtin_costs: [u64; 7] = builtin_costs.into(); + + let gas_cost = gas_meta.get_gas_costs_for_statement(statement_idx); + let mut total_gas_cost = 0; + for (cost_count, token_type) in &gas_cost { + if *cost_count == 0 { + continue; + } + + let builtin_costs_index = match token_type { + CostTokenType::Const => 0, + CostTokenType::Pedersen => 1, + CostTokenType::Bitwise => 2, + CostTokenType::EcOp => 3, + CostTokenType::Poseidon => 4, + CostTokenType::AddMod => 5, + CostTokenType::MulMod => 6, + _ => panic!(), + }; + + let cost_value = cost_count * builtin_costs[builtin_costs_index as usize]; + total_gas_cost += cost_value; + } + + let new_gas = gas.saturating_add(total_gas_cost); + + EvalAction::NormalBranch(0, smallvec![Value::U64(new_gas)]) +} + +pub fn eval_get_builtin_costs( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + _args: Vec, + builtin_costs: BuiltinCosts, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::BuiltinCosts(builtin_costs)]) +} diff --git a/debug_utils/sierra-emu/src/vm/int128.rs b/debug_utils/sierra-emu/src/vm/int128.rs new file mode 100644 index 0000000000..09f6a91e5b --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/int128.rs @@ -0,0 +1,193 @@ +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + int::{signed128::Sint128Concrete, IntOperationConcreteLibfunc, IntOperator}, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use num_bigint::{BigInt, BigUint, ToBigInt}; +use smallvec::smallvec; +use starknet_crypto::Felt; + +use crate::Value; + +use super::EvalAction; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Sint128Concrete, + args: Vec, +) -> EvalAction { + match selector { + Sint128Concrete::Const(_) => todo!("int128 const"), + Sint128Concrete::Operation(info) => eval_operation(registry, info, args), + Sint128Concrete::Equal(info) => eval_equal(registry, info, args), + Sint128Concrete::ToFelt252(info) => eval_to_felt(registry, info, args), + Sint128Concrete::FromFelt252(info) => eval_from_felt(registry, info, args), + Sint128Concrete::Diff(info) => eval_diff(registry, info, args), + } +} + +fn eval_diff( + _registry: &ProgramRegistry, + _selector: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::I128(lhs), Value::I128(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + if lhs >= rhs { + EvalAction::NormalBranch( + 0, + smallvec![range_check, Value::U128((lhs - rhs).try_into().unwrap())], + ) + } else { + EvalAction::NormalBranch( + 1, + smallvec![ + range_check, + Value::U128(lhs.wrapping_sub(rhs).try_into().unwrap()) + ], + ) + } +} + +fn eval_operation( + _registry: &ProgramRegistry, + selector: &IntOperationConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::I128(lhs), Value::I128(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let (result, overflow) = match selector.operator { + IntOperator::OverflowingAdd => lhs.overflowing_add(rhs), + IntOperator::OverflowingSub => lhs.overflowing_sub(rhs), + }; + + EvalAction::NormalBranch( + overflow as usize, + smallvec![range_check, Value::I128(result)], + ) +} + +fn eval_equal( + _registry: &ProgramRegistry, + _selector: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::I128(lhs), Value::I128(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch((lhs == rhs) as usize, smallvec![]) +} + +pub fn eval_to_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::I128(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch(0, smallvec![Value::Felt(value.into())]) +} + +pub fn eval_from_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Felt(value_felt)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + let prime = Felt::prime(); + let half_prime = &prime / BigUint::from(2u8); + + let min = Felt::from(i128::MIN).to_bigint(); + let max = Felt::from(i128::MAX).to_bigint(); + + let value = { + if value_felt.to_biguint() > half_prime { + (prime - value_felt.to_biguint()).to_bigint().unwrap() * BigInt::from(-1) + } else { + value_felt.to_bigint() + } + }; + + if value >= min || value <= max { + let value: i128 = value.try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![range_check, Value::I128(value)]) + } else { + EvalAction::NormalBranch(1, smallvec![range_check]) + } +} + +#[cfg(test)] +mod tests { + use crate::{load_cairo, test_utils::run_test_program, Value}; + + #[test] + fn test_eval_diff() { + let (_, program) = load_cairo!( + use core::integer; + + fn main() -> u128 { + integer::i128_diff(2, 1).unwrap() + } + ); + + let result = run_test_program(program); + + let Value::Enum { + self_ty: _, + index: _, + payload, + } = result.last().unwrap() + else { + panic!("No output"); + }; + + let expected = Value::Struct(vec![Value::U128(1)]); + + assert_eq!(**payload, expected); + } + + #[test] + fn test_eval_from_felt() { + let (_, program) = load_cairo!( + use core::integer; + + fn main() -> i128 { + 0x7fffffffffffffffffffffffffffffff_felt252 + .try_into() + .unwrap() + } + ); + + let result = run_test_program(program); + + let Value::Enum { + self_ty: _, + index: _, + payload, + } = result.last().unwrap() + else { + panic!("No output"); + }; + + let expected = Value::Struct(vec![Value::I128(0x7fffffffffffffffffffffffffffffff)]); + + assert_eq!(**payload, expected); + } +} diff --git a/debug_utils/sierra-emu/src/vm/jump.rs b/debug_utils/sierra-emu/src/vm/jump.rs new file mode 100644 index 0000000000..a37881ebaa --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/jump.rs @@ -0,0 +1,20 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![]) +} diff --git a/debug_utils/sierra-emu/src/vm/mem.rs b/debug_utils/sierra-emu/src/vm/mem.rs new file mode 100644 index 0000000000..346565a42a --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/mem.rs @@ -0,0 +1,83 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + lib_func::{SignatureAndTypeConcreteLibfunc, SignatureOnlyConcreteLibfunc}, + mem::MemConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &MemConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + MemConcreteLibfunc::StoreTemp(info) => eval_store_temp(registry, info, args), + MemConcreteLibfunc::StoreLocal(info) => eval_store_local(registry, info, args), + MemConcreteLibfunc::FinalizeLocals(info) => eval_finalize_locals(registry, info, args), + MemConcreteLibfunc::AllocLocal(info) => eval_alloc_local(registry, info, args), + MemConcreteLibfunc::Rename(info) => eval_rename(registry, info, args), + } +} + +pub fn eval_store_temp( + _registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![value]) +} + +pub fn eval_store_local( + registry: &ProgramRegistry, + _info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Uninitialized { ty }, value]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + assert!(value.is(registry, &ty)); + + EvalAction::NormalBranch(0, smallvec![value]) +} + +pub fn eval_finalize_locals( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![]) +} + +pub fn eval_alloc_local( + _registry: &ProgramRegistry, + info: &SignatureAndTypeConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [] = args.try_into().unwrap(); + + EvalAction::NormalBranch( + 0, + smallvec![Value::Uninitialized { + ty: info.ty.clone() + }], + ) +} + +pub fn eval_rename( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![value]) +} diff --git a/debug_utils/sierra-emu/src/vm/pedersen.rs b/debug_utils/sierra-emu/src/vm/pedersen.rs new file mode 100644 index 0000000000..04da05a4b9 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/pedersen.rs @@ -0,0 +1,39 @@ +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + lib_func::SignatureOnlyConcreteLibfunc, + pedersen::PedersenConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +use crate::Value; + +use super::EvalAction; + +pub fn eval( + registry: &ProgramRegistry, + selector: &PedersenConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + PedersenConcreteLibfunc::PedersenHash(info) => eval_pedersen_hash(registry, info, args), + } +} + +fn eval_pedersen_hash( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [pedersen @ Value::Unit, Value::Felt(lhs), Value::Felt(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let res = starknet_crypto::pedersen_hash(&lhs, &rhs); + + EvalAction::NormalBranch(0, smallvec![pedersen, Value::Felt(res),]) +} diff --git a/debug_utils/sierra-emu/src/vm/poseidon.rs b/debug_utils/sierra-emu/src/vm/poseidon.rs new file mode 100644 index 0000000000..d1a2048446 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/poseidon.rs @@ -0,0 +1,51 @@ +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + lib_func::SignatureOnlyConcreteLibfunc, + poseidon::PoseidonConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +use crate::Value; + +use super::EvalAction; + +pub fn eval( + registry: &ProgramRegistry, + selector: &PoseidonConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + PoseidonConcreteLibfunc::HadesPermutation(info) => { + eval_hades_permutation(registry, info, args) + } + } +} + +fn eval_hades_permutation( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [poseidon @ Value::Unit, Value::Felt(p1), Value::Felt(p2), Value::Felt(p3)]: [Value; 4] = + args.try_into().unwrap() + else { + panic!() + }; + + let mut state = [p1, p2, p3]; + + starknet_crypto::poseidon_permute_comp(&mut state); + + EvalAction::NormalBranch( + 0, + smallvec![ + poseidon, + Value::Felt(state[0]), + Value::Felt(state[1]), + Value::Felt(state[2]) + ], + ) +} diff --git a/debug_utils/sierra-emu/src/vm/snapshot_take.rs b/debug_utils/sierra-emu/src/vm/snapshot_take.rs new file mode 100644 index 0000000000..117ef70b62 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/snapshot_take.rs @@ -0,0 +1,20 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![value.clone(), value]) +} diff --git a/debug_utils/sierra-emu/src/vm/starknet.rs b/debug_utils/sierra-emu/src/vm/starknet.rs new file mode 100644 index 0000000000..4d039ebd9f --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/starknet.rs @@ -0,0 +1,1265 @@ +use super::EvalAction; +use crate::{ + starknet::{Secp256r1Point, StarknetSyscallHandler, U256}, + Value, +}; +use cairo_lang_sierra::{ + extensions::{ + consts::SignatureAndConstConcreteLibfunc, + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + lib_func::SignatureOnlyConcreteLibfunc, + starknet::StarknetConcreteLibfunc, + ConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use num_traits::One; +use smallvec::smallvec; +use starknet_types_core::felt::Felt; + +pub fn eval( + registry: &ProgramRegistry, + selector: &StarknetConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + match selector { + StarknetConcreteLibfunc::CallContract(info) => { + self::eval_call_contract(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::ClassHashConst(info) => { + eval_class_hash_const(registry, info, args) + } + StarknetConcreteLibfunc::ClassHashTryFromFelt252(info) => { + eval_class_hash_try_from_felt(registry, info, args) + } + StarknetConcreteLibfunc::ClassHashToFelt252(info) => { + eval_class_hash_to_felt(registry, info, args) + } + StarknetConcreteLibfunc::ContractAddressConst(info) => { + eval_contract_address_const(registry, info, args) + } + StarknetConcreteLibfunc::ContractAddressTryFromFelt252(info) => { + eval_contract_address_try_from_felt(registry, info, args) + } + StarknetConcreteLibfunc::ContractAddressToFelt252(info) => { + eval_contract_address_to_felt(registry, info, args) + } + StarknetConcreteLibfunc::StorageRead(info) => { + eval_storage_read(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::StorageWrite(info) => { + eval_storage_write(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::StorageBaseAddressConst(info) => { + eval_storage_base_address_const(registry, info, args) + } + StarknetConcreteLibfunc::StorageBaseAddressFromFelt252(info) => { + eval_storage_base_address_from_felt(registry, info, args) + } + StarknetConcreteLibfunc::StorageAddressFromBase(info) => { + eval_storage_address_from_base(registry, info, args) + } + StarknetConcreteLibfunc::StorageAddressFromBaseAndOffset(info) => { + eval_storage_address_from_base_and_offset(registry, info, args) + } + StarknetConcreteLibfunc::StorageAddressToFelt252(info) => { + eval_storage_address_to_felt(registry, info, args) + } + StarknetConcreteLibfunc::StorageAddressTryFromFelt252(info) => { + eval_storage_address_try_from_felt(registry, info, args) + } + StarknetConcreteLibfunc::EmitEvent(info) => { + eval_emit_event(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::GetBlockHash(info) => { + eval_get_block_hash(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::GetExecutionInfo(info) => { + eval_get_execution_info(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::GetExecutionInfoV2(info) => { + eval_get_execution_info_v2(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::Deploy(info) => eval_deploy(registry, info, args, syscall_handler), + StarknetConcreteLibfunc::Keccak(info) => eval_keccak(registry, info, args, syscall_handler), + StarknetConcreteLibfunc::Sha256ProcessBlock(info) => { + eval_sha256_process_block(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::Sha256StateHandleInit(info) => { + eval_sha256_state_handle_init(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::Sha256StateHandleDigest(info) => { + eval_sha256_state_handle_digest(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::LibraryCall(info) => { + eval_library_call(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::ReplaceClass(info) => { + eval_replace_class(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::SendMessageToL1(info) => { + eval_send_message_to_l1(registry, info, args, syscall_handler) + } + StarknetConcreteLibfunc::Testing(_info) => todo!(), + StarknetConcreteLibfunc::Secp256(info) => match info { + cairo_lang_sierra::extensions::starknet::secp256::Secp256ConcreteLibfunc::K1(info) => { + match info { + cairo_lang_sierra::extensions::starknet::secp256::Secp256OpConcreteLibfunc::New(_) => todo!(), + cairo_lang_sierra::extensions::starknet::secp256::Secp256OpConcreteLibfunc::Add(_) => todo!(), + cairo_lang_sierra::extensions::starknet::secp256::Secp256OpConcreteLibfunc::Mul(_) => todo!(), + cairo_lang_sierra::extensions::starknet::secp256::Secp256OpConcreteLibfunc::GetPointFromX(_) => todo!(), + cairo_lang_sierra::extensions::starknet::secp256::Secp256OpConcreteLibfunc::GetXy(_) => todo!(), + } + } + cairo_lang_sierra::extensions::starknet::secp256::Secp256ConcreteLibfunc::R1(info) => { + match info { + cairo_lang_sierra::extensions::starknet::secp256::Secp256OpConcreteLibfunc::New(info) => eval_secp_r_new(registry, info, args, syscall_handler), + cairo_lang_sierra::extensions::starknet::secp256::Secp256OpConcreteLibfunc::Add(info) => eval_secp_r_add(registry, info, args, syscall_handler), + cairo_lang_sierra::extensions::starknet::secp256::Secp256OpConcreteLibfunc::Mul(info) => eval_secp_r_mul(registry, info, args, syscall_handler), + cairo_lang_sierra::extensions::starknet::secp256::Secp256OpConcreteLibfunc::GetPointFromX(info) => eval_secp_r_get_point_from_x(registry, info, args, syscall_handler), + cairo_lang_sierra::extensions::starknet::secp256::Secp256OpConcreteLibfunc::GetXy(info) => secp_r_get_xy(registry, info, args, syscall_handler), + } + } + }, + StarknetConcreteLibfunc::GetClassHashAt(_) => todo!(), + StarknetConcreteLibfunc::MetaTxV0(_) => todo!(), + } +} + +fn eval_secp_r_add( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system @ Value::Unit, x, y]: [Value; 4] = args.try_into().unwrap() + else { + panic!() + }; + + let x = Secp256r1Point::from_value(x); + let y = Secp256r1Point::from_value(y); + + match syscall_handler.secp256r1_add(x, y, &mut gas) { + Ok(x) => EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system, x.into_value()]), + Err(r) => { + let r = Value::Struct(r.into_iter().map(Value::Felt).collect::>()); + EvalAction::NormalBranch(1, smallvec![Value::U64(gas), system, r]) + } + } +} + +fn eval_secp_r_mul( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system @ Value::Unit, x, n]: [Value; 4] = args.try_into().unwrap() + else { + panic!() + }; + + let x = Secp256r1Point::from_value(x); + let n = U256::from_value(n); + + match syscall_handler.secp256r1_mul(x, n, &mut gas) { + Ok(x) => EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system, x.into_value()]), + Err(r) => { + let r = Value::Struct(r.into_iter().map(Value::Felt).collect::>()); + EvalAction::NormalBranch(1, smallvec![Value::U64(gas), system, r]) + } + } +} + +fn eval_secp_r_new( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system @ Value::Unit, Value::Struct(x), Value::Struct(y)]: [Value; + 4] = args.try_into().unwrap() + else { + panic!() + }; + + let Value::U128(x_lo) = x[0] else { panic!() }; + let Value::U128(x_hi) = x[1] else { panic!() }; + let x = U256 { lo: x_lo, hi: x_hi }; + let Value::U128(y_lo) = y[0] else { panic!() }; + let Value::U128(y_hi) = y[1] else { panic!() }; + let y = U256 { lo: y_lo, hi: y_hi }; + + match syscall_handler.secp256r1_new(x, y, &mut gas) { + Ok(p) => { + let enum_ty = &info.branch_signatures()[0].vars[2].ty; + let value = match p { + Some(p) => Value::Enum { + self_ty: enum_ty.clone(), + index: 0, + payload: Box::new(p.into_value()), + }, + None => Value::Enum { + self_ty: enum_ty.clone(), + index: 1, + payload: Box::new(Value::Unit), + }, + }; + + EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system, value]) + } + Err(payload) => { + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let value = payload.into_iter().map(Value::Felt).collect::>(); + EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: value + } + ], + ) + } + } +} + +fn eval_secp_r_get_point_from_x( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system @ Value::Unit, Value::Struct(x), Value::Enum { + index: y_parity, .. + }]: [Value; 4] = args.try_into().unwrap() + else { + panic!() + }; + + let Value::U128(x_lo) = x[0] else { panic!() }; + let Value::U128(x_hi) = x[1] else { panic!() }; + let x = U256 { lo: x_lo, hi: x_hi }; + let y_parity = y_parity.is_one(); + + match syscall_handler.secp256r1_get_point_from_x(x, y_parity, &mut gas) { + Ok(p) => { + let enum_ty = &info.branch_signatures()[0].vars[2].ty; + let value = match p { + Some(p) => Value::Enum { + self_ty: enum_ty.clone(), + index: 0, + payload: Box::new(p.into_value()), + }, + None => Value::Enum { + self_ty: enum_ty.clone(), + index: 1, + payload: Box::new(Value::Unit), + }, + }; + + EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system, value]) + } + Err(payload) => { + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let value = payload.into_iter().map(Value::Felt).collect::>(); + EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: value + } + ], + ) + } + } +} + +fn secp_r_get_xy( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system @ Value::Unit, secp_value]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let secp_value = Secp256r1Point::from_value(secp_value); + + match syscall_handler.secp256r1_get_xy(secp_value, &mut gas) { + Ok(payload) => { + let (x, y) = (payload.0.into_value(), payload.1.into_value()); + EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system, x, y]) + } + Err(payload) => { + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let payload = payload.into_iter().map(Value::Felt).collect::>(); + EvalAction::NormalBranch( + 0, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: payload + } + ], + ) + } + } +} + +fn eval_class_hash_const( + _registry: &ProgramRegistry, + info: &SignatureAndConstConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::Felt(info.c.clone().into())]) +} + +fn eval_storage_base_address_const( + _registry: &ProgramRegistry, + info: &SignatureAndConstConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::Felt(info.c.clone().into())]) +} + +fn eval_contract_address_const( + _registry: &ProgramRegistry, + info: &SignatureAndConstConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::Felt(info.c.clone().into())]) +} + +fn eval_class_hash_try_from_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + // 2 ** 251 = 3618502788666131106986593281521497120414687020801267626233049500247285301248 + + let [range_check @ Value::Unit, Value::Felt(value)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + if value + < Felt::from_dec_str( + "3618502788666131106986593281521497120414687020801267626233049500247285301248", + ) + .unwrap() + { + EvalAction::NormalBranch(0, smallvec![range_check, Value::Felt(value)]) + } else { + EvalAction::NormalBranch(1, smallvec![range_check]) + } +} + +fn eval_contract_address_try_from_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + // 2 ** 251 = 3618502788666131106986593281521497120414687020801267626233049500247285301248 + + let [range_check @ Value::Unit, Value::Felt(value)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + if value + < Felt::from_dec_str( + "3618502788666131106986593281521497120414687020801267626233049500247285301248", + ) + .unwrap() + { + EvalAction::NormalBranch(0, smallvec![range_check, Value::Felt(value)]) + } else { + EvalAction::NormalBranch(1, smallvec![range_check]) + } +} + +fn eval_storage_address_try_from_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + // 2 ** 251 = 3618502788666131106986593281521497120414687020801267626233049500247285301248 + + let [range_check @ Value::Unit, Value::Felt(value)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + if value + < Felt::from_dec_str( + "3618502788666131106986593281521497120414687020801267626233049500247285301248", + ) + .unwrap() + { + EvalAction::NormalBranch(0, smallvec![range_check, Value::Felt(value)]) + } else { + EvalAction::NormalBranch(1, smallvec![range_check]) + } +} + +fn eval_storage_base_address_from_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check, value] = args.try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![range_check, value]) +} + +fn eval_storage_address_to_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![value]) +} + +fn eval_contract_address_to_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![value]) +} + +fn eval_class_hash_to_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![value]) +} + +fn eval_storage_address_from_base( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [value] = args.try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![value]) +} + +fn eval_storage_address_from_base_and_offset( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Felt(value), Value::U8(offset)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch(0, smallvec![Value::Felt(value + Felt::from(offset))]) +} + +fn eval_call_contract( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::Felt(address), Value::Felt(entry_point_selector), Value::Struct(calldata)]: [Value; 5] = + args.try_into().unwrap() + else { + panic!() + }; + + let [Value::Array { + ty: _, + data: calldata, + }]: [Value; 1] = calldata.try_into().unwrap() + else { + panic!() + }; + + let calldata = calldata + .into_iter() + .map(|x| match x { + Value::Felt(x) => x, + _ => unreachable!(), + }) + .collect(); + + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let result = syscall_handler.call_contract(address, entry_point_selector, calldata, &mut gas); + + match result { + Ok(return_values) => EvalAction::NormalBranch( + 0, + smallvec![ + Value::U64(gas), + system, + Value::Struct(vec![Value::Array { + ty: felt_ty, + data: return_values + .into_iter() + .map(Value::Felt) + .collect::>(), + }]) + ], + ), + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_storage_read( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::U32(address_domain), Value::Felt(storage_key)]: [Value; + 4] = args.try_into().unwrap() + else { + panic!() + }; + let error_felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let result = syscall_handler.storage_read(address_domain, storage_key, &mut gas); + + match result { + Ok(value) => { + EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system, Value::Felt(value)]) + } + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: error_felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_storage_write( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::U32(address_domain), Value::Felt(storage_key), Value::Felt(value)]: [Value; 5] = args.try_into().unwrap() else { + panic!() + }; + + let error_felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let result = syscall_handler.storage_write(address_domain, storage_key, value, &mut gas); + + match result { + Ok(_) => EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system]), + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: error_felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_emit_event( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::Struct(key_arr), Value::Struct(data_arr)]: [Value; 4] = + args.try_into().unwrap() + else { + panic!() + }; + + let [Value::Array { ty: _, data: keys }]: [Value; 1] = key_arr.try_into().unwrap() else { + panic!() + }; + + let [Value::Array { ty: _, data }]: [Value; 1] = data_arr.try_into().unwrap() else { + panic!() + }; + + let error_felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let keys = keys + .into_iter() + .map(|x| match x { + Value::Felt(x) => x, + _ => unreachable!(), + }) + .collect(); + let data = data + .into_iter() + .map(|x| match x { + Value::Felt(x) => x, + _ => unreachable!(), + }) + .collect(); + + let result = syscall_handler.emit_event(keys, data, &mut gas); + + match result { + Ok(_) => EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system]), + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: error_felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_get_block_hash( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::U64(block_number)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + let error_felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let result = syscall_handler.get_block_hash(block_number, &mut gas); + + match result { + Ok(res) => { + EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system, Value::Felt(res)]) + } + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: error_felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_get_execution_info( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let result = syscall_handler.get_execution_info(&mut gas); + + match result { + Ok(res) => EvalAction::NormalBranch( + 0, + smallvec![Value::U64(gas), system, res.into_value(felt_ty)], + ), + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_get_execution_info_v2( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let result = syscall_handler.get_execution_info_v2(&mut gas); + + let mut out_ty = registry + .get_type(&info.branch_signatures()[0].vars[2].ty) + .unwrap(); + let mut out_ty_id = &info.branch_signatures()[0].vars[2].ty; + + if let CoreTypeConcrete::Box(inner) = out_ty { + out_ty_id = &inner.ty; + out_ty = registry.get_type(&inner.ty).unwrap(); + }; + + if let CoreTypeConcrete::Struct(inner) = out_ty { + out_ty_id = &inner.members[1]; + out_ty = registry.get_type(&inner.members[1]).unwrap(); + }; + + if let CoreTypeConcrete::Box(inner) = out_ty { + out_ty_id = &inner.ty; + out_ty = registry.get_type(&inner.ty).unwrap(); + }; + + if let CoreTypeConcrete::Struct(inner) = out_ty { + out_ty_id = &inner.members[7]; + out_ty = registry.get_type(&inner.members[7]).unwrap(); + }; + + if let CoreTypeConcrete::Struct(inner) = out_ty { + out_ty_id = &inner.members[0]; + out_ty = registry.get_type(&inner.members[0]).unwrap(); + }; + if let CoreTypeConcrete::Snapshot(inner) = out_ty { + out_ty_id = &inner.ty; + out_ty = registry.get_type(&inner.ty).unwrap(); + }; + if let CoreTypeConcrete::Array(inner) = out_ty { + out_ty_id = &inner.ty; + }; + + match result { + Ok(res) => EvalAction::NormalBranch( + 0, + smallvec![ + Value::U64(gas), + system, + res.into_value(felt_ty, out_ty_id.clone()) + ], + ), + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_deploy( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::Felt(class_hash), Value::Felt(contract_address_salt), Value::Struct(calldata), Value::Enum { + self_ty: _, + index: deploy_from_zero, + payload: _, + }]: [Value; 6] = args.try_into().unwrap() + else { + panic!() + }; + + let deploy_from_zero = deploy_from_zero != 0; + + let [Value::Array { + ty: _, + data: calldata, + }]: [Value; 1] = calldata.try_into().unwrap() + else { + panic!() + }; + + let calldata = calldata + .into_iter() + .map(|x| match x { + Value::Felt(x) => x, + _ => unreachable!(), + }) + .collect(); + + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let result = syscall_handler.deploy( + class_hash, + contract_address_salt, + calldata, + deploy_from_zero, + &mut gas, + ); + + match result { + Ok((contract_address, return_values)) => EvalAction::NormalBranch( + 0, + smallvec![ + Value::U64(gas), + system, + Value::Felt(contract_address), + Value::Struct(vec![Value::Array { + ty: felt_ty, + data: return_values + .into_iter() + .map(Value::Felt) + .collect::>(), + }]) + ], + ), + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_keccak( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::Struct(input)]: [Value; 3] = args.try_into().unwrap() + else { + panic!() + }; + + let [Value::Array { ty: _, data: input }]: [Value; 1] = input.try_into().unwrap() else { + panic!() + }; + + let input = input + .into_iter() + .map(|x| match x { + Value::U64(x) => x, + _ => unreachable!(), + }) + .collect(); + + let result = syscall_handler.keccak(input, &mut gas); + + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + match result { + Ok(res) => { + EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system, res.into_value()]) + } + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_library_call( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::Felt(class_hash), Value::Felt(function_selector), Value::Struct(calldata)]: [Value; 5] = + args.try_into().unwrap() + else { + panic!() + }; + + let [Value::Array { + ty: _, + data: calldata, + }]: [Value; 1] = calldata.try_into().unwrap() + else { + panic!() + }; + + let calldata = calldata + .into_iter() + .map(|x| match x { + Value::Felt(x) => x, + _ => unreachable!(), + }) + .collect(); + + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let result = syscall_handler.library_call(class_hash, function_selector, calldata, &mut gas); + + match result { + Ok(return_values) => EvalAction::NormalBranch( + 0, + smallvec![ + Value::U64(gas), + system, + Value::Struct(vec![Value::Array { + ty: felt_ty, + data: return_values + .into_iter() + .map(Value::Felt) + .collect::>(), + }]) + ], + ), + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_replace_class( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::Felt(class_hash)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let result = syscall_handler.replace_class(class_hash, &mut gas); + + match result { + Ok(()) => EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system]), + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_send_message_to_l1( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::Felt(address), Value::Struct(payload)]: [Value; 4] = + args.try_into().unwrap() + else { + panic!() + }; + + let [Value::Array { + ty: _, + data: payload, + }]: [Value; 1] = payload.try_into().unwrap() + else { + panic!() + }; + + let payload = payload + .into_iter() + .map(|x| match x { + Value::Felt(x) => x, + _ => unreachable!(), + }) + .collect(); + + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let result = syscall_handler.send_message_to_l1(address, payload, &mut gas); + + match result { + Ok(()) => EvalAction::NormalBranch(0, smallvec![Value::U64(gas), system]), + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + +fn eval_sha256_state_handle_init( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, + _syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [value]: [Value; 1] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![value]) +} + +fn eval_sha256_state_handle_digest( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, + _syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [value]: [Value; 1] = args.try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![value]) +} + +fn eval_sha256_process_block( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::Struct(prev_state), Value::Struct(current_block)]: [Value; 4] = args.try_into().unwrap() else { + panic!() + }; + + let prev_state: [u32; 8] = prev_state + .into_iter() + .map(|v| { + let Value::U32(v) = v else { panic!() }; + v + }) + .collect::>() + .try_into() + .unwrap(); + let current_block: [u32; 16] = current_block + .into_iter() + .map(|v| { + let Value::U32(v) = v else { panic!() }; + v + }) + .collect::>() + .try_into() + .unwrap(); + + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + match syscall_handler.sha256_process_block(prev_state, current_block, &mut gas) { + Ok(payload) => { + let payload = payload.into_iter().map(Value::U32).collect::>(); + EvalAction::NormalBranch( + 0, + smallvec![Value::U64(gas), system, Value::Struct(payload)], + ) + } + Err(payload) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: payload.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} diff --git a/debug_utils/sierra-emu/src/vm/struct.rs b/debug_utils/sierra-emu/src/vm/struct.rs new file mode 100644 index 0000000000..1e84436df4 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/struct.rs @@ -0,0 +1,99 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + lib_func::SignatureOnlyConcreteLibfunc, + structure::{StructConcreteLibfunc, StructConcreteType}, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &StructConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + StructConcreteLibfunc::Construct(info) => eval_construct(registry, info, args), + StructConcreteLibfunc::Deconstruct(info) => eval_deconstruct(registry, info, args), + StructConcreteLibfunc::SnapshotDeconstruct(info) => { + eval_snapshot_deconstruct(registry, info, args) + } + } +} + +pub fn eval_construct( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let CoreTypeConcrete::Struct(StructConcreteType { members, .. }) = registry + .get_type(&info.signature.branch_signatures[0].vars[0].ty) + .unwrap() + else { + panic!() + }; + assert_eq!(args.len(), members.len()); + assert!(args + .iter() + .zip(members) + .all(|(value, ty)| value.is(registry, ty))); + + EvalAction::NormalBranch(0, smallvec![Value::Struct(args)]) +} + +pub fn eval_deconstruct( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Struct(values)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let CoreTypeConcrete::Struct(StructConcreteType { members, .. }) = registry + .get_type(&info.signature.param_signatures[0].ty) + .unwrap() + else { + panic!() + }; + assert_eq!(values.len(), members.len()); + assert!(values + .iter() + .zip(members) + .all(|(value, ty)| value.is(registry, ty))); + + EvalAction::NormalBranch(0, values.into()) +} + +pub fn eval_snapshot_deconstruct( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Struct(values)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let CoreTypeConcrete::Snapshot(snapshot_ty) = registry + .get_type(&info.signature.param_signatures[0].ty) + .unwrap() + else { + panic!() + }; + + let CoreTypeConcrete::Struct(StructConcreteType { members, .. }) = + registry.get_type(&snapshot_ty.ty).unwrap() + else { + panic!() + }; + assert_eq!(values.len(), members.len()); + assert!(values + .iter() + .zip(members) + .all(|(value, ty)| value.is(registry, ty))); + + EvalAction::NormalBranch(0, values.into()) +} diff --git a/debug_utils/sierra-emu/src/vm/uint128.rs b/debug_utils/sierra-emu/src/vm/uint128.rs new file mode 100644 index 0000000000..3da80ec7ab --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/uint128.rs @@ -0,0 +1,257 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + int::{ + unsigned128::{Uint128Concrete, Uint128Traits}, + IntConstConcreteLibfunc, IntOperationConcreteLibfunc, IntOperator, + }, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use num_bigint::BigUint; +use smallvec::smallvec; +use starknet_crypto::Felt; +use starknet_types_core::felt::NonZeroFelt; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Uint128Concrete, + args: Vec, +) -> EvalAction { + match selector { + Uint128Concrete::Const(info) => eval_const(registry, info, args), + Uint128Concrete::Operation(info) => eval_operation(registry, info, args), + Uint128Concrete::SquareRoot(info) => eval_square_root(registry, info, args), + Uint128Concrete::Equal(info) => eval_equal(registry, info, args), + Uint128Concrete::ToFelt252(info) => eval_to_felt(registry, info, args), + Uint128Concrete::FromFelt252(info) => eval_from_felt(registry, info, args), + Uint128Concrete::IsZero(info) => eval_is_zero(registry, info, args), + Uint128Concrete::Divmod(info) => eval_divmod(registry, info, args), + Uint128Concrete::Bitwise(info) => eval_bitwise(registry, info, args), + Uint128Concrete::GuaranteeMul(info) => eval_guarantee_mul(registry, info, args), + Uint128Concrete::MulGuaranteeVerify(info) => eval_guarantee_verify(registry, info, args), + Uint128Concrete::ByteReverse(info) => eval_byte_reverse(registry, info, args), + } +} + +pub fn eval_guarantee_mul( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U128(lhs), Value::U128(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + let mask128 = BigUint::from(u128::MAX); + let result = BigUint::from(lhs) * BigUint::from(rhs); + let high = Value::U128((&result >> 128u32).try_into().unwrap()); + let low = Value::U128((result & mask128).try_into().unwrap()); + + EvalAction::NormalBranch(0, smallvec![high, low, Value::Unit]) +} + +pub fn eval_square_root( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U128(value)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + let value_big = BigUint::from(value); + + let result: u64 = value_big.sqrt().try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![range_check, Value::U64(result)]) +} + +pub fn eval_guarantee_verify( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, _verify @ Value::Unit]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + EvalAction::NormalBranch(0, smallvec![range_check]) +} + +pub fn eval_divmod( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U128(x), Value::U128(y)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let val = Value::U128(x / y); + let rem = Value::U128(x % y); + + EvalAction::NormalBranch(0, smallvec![range_check, val, rem]) +} + +pub fn eval_operation( + _registry: &ProgramRegistry, + info: &IntOperationConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U128(lhs), Value::U128(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let (result, has_overflow) = match info.operator { + IntOperator::OverflowingAdd => lhs.overflowing_add(rhs), + IntOperator::OverflowingSub => lhs.overflowing_sub(rhs), + }; + + EvalAction::NormalBranch( + has_overflow as usize, + smallvec![range_check, Value::U128(result)], + ) +} + +pub fn eval_equal( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U128(lhs), Value::U128(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch((lhs == rhs) as usize, smallvec![]) +} + +pub fn eval_is_zero( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [vm_value @ Value::U128(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + if value == 0 { + EvalAction::NormalBranch(0, smallvec![]) + } else { + EvalAction::NormalBranch(1, smallvec![vm_value]) + } +} + +pub fn eval_bitwise( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [bitwise @ Value::Unit, Value::U128(lhs), Value::U128(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let and = lhs & rhs; + let or = lhs | rhs; + let xor = lhs ^ rhs; + + EvalAction::NormalBranch( + 0, + smallvec![bitwise, Value::U128(and), Value::U128(xor), Value::U128(or)], + ) +} + +pub fn eval_const( + _registry: &ProgramRegistry, + info: &IntConstConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::U128(info.c)]) +} + +pub fn eval_to_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U128(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch(0, smallvec![Value::Felt(value.into())]) +} + +pub fn eval_from_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Felt(value)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + let bound = Felt::from(u128::MAX) + 1; + + if value < bound { + let value: u128 = value.to_biguint().try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![range_check, Value::U128(value)]) + } else { + let (new_value, overflow) = value.div_rem(&NonZeroFelt::try_from(bound).unwrap()); + + let overflow: u128 = overflow.to_biguint().try_into().unwrap(); + let new_value: u128 = new_value.to_biguint().try_into().unwrap(); + EvalAction::NormalBranch( + 1, + smallvec![range_check, Value::U128(new_value), Value::U128(overflow)], + ) + } +} + +pub fn eval_byte_reverse( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [bitwise @ Value::Unit, Value::U128(value)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + let value = value.swap_bytes(); + + EvalAction::NormalBranch(0, smallvec![bitwise, Value::U128(value)]) +} + +#[cfg(test)] +mod test { + use crate::{load_cairo, test_utils::run_test_program, Value}; + + #[test] + fn test_square_root() { + let (_, program) = load_cairo!( + use core::num::traits::Sqrt; + fn main() -> u64 { + 0xffffffffffffffffffffffffffffffff_u128.sqrt() + } + ); + + let result = run_test_program(program); + + let Value::U64(payload) = result.last().unwrap() else { + panic!("No output"); + }; + + assert_eq!(*payload, 0xffffffffffffffff); + } +} diff --git a/debug_utils/sierra-emu/src/vm/uint16.rs b/debug_utils/sierra-emu/src/vm/uint16.rs new file mode 100644 index 0000000000..29d9269c57 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/uint16.rs @@ -0,0 +1,176 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + int::{ + unsigned::{Uint16Concrete, Uint16Traits}, + IntConstConcreteLibfunc, IntOperationConcreteLibfunc, IntOperator, + }, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; +use starknet_crypto::Felt; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Uint16Concrete, + args: Vec, +) -> EvalAction { + match selector { + Uint16Concrete::Const(info) => eval_const(registry, info, args), + Uint16Concrete::Operation(info) => eval_operation(registry, info, args), + Uint16Concrete::SquareRoot(_) => todo!(), + Uint16Concrete::Equal(info) => eval_equal(registry, info, args), + Uint16Concrete::ToFelt252(info) => eval_to_felt252(registry, info, args), + Uint16Concrete::FromFelt252(info) => eval_from_felt(registry, info, args), + Uint16Concrete::IsZero(info) => eval_is_zero(registry, info, args), + Uint16Concrete::Divmod(info) => eval_divmod(registry, info, args), + Uint16Concrete::WideMul(info) => eval_widemul(registry, info, args), + Uint16Concrete::Bitwise(info) => eval_bitwise(registry, info, args), + } +} + +pub fn eval_divmod( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U16(x), Value::U16(y)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let val = Value::U16(x / y); + let rem = Value::U16(x % y); + + EvalAction::NormalBranch(0, smallvec![range_check, val, rem]) +} + +pub fn eval_operation( + _registry: &ProgramRegistry, + info: &IntOperationConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U16(lhs), Value::U16(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let (result, has_overflow) = match info.operator { + IntOperator::OverflowingAdd => lhs.overflowing_add(rhs), + IntOperator::OverflowingSub => lhs.overflowing_sub(rhs), + }; + + EvalAction::NormalBranch( + has_overflow as usize, + smallvec![range_check, Value::U16(result)], + ) +} + +pub fn eval_from_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Felt(value)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + let max = Felt::from(u16::MAX); + + if value <= max { + let value: u16 = value.to_biguint().try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![range_check, Value::U16(value)]) + } else { + EvalAction::NormalBranch(1, smallvec![range_check]) + } +} + +pub fn eval_equal( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U16(lhs), Value::U16(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch((lhs == rhs) as usize, smallvec![]) +} + +pub fn eval_bitwise( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [bitwise @ Value::Unit, Value::U16(lhs), Value::U16(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let and = lhs & rhs; + let or = lhs | rhs; + let xor = lhs ^ rhs; + + EvalAction::NormalBranch( + 0, + smallvec![bitwise, Value::U16(and), Value::U16(xor), Value::U16(or)], + ) +} + +pub fn eval_is_zero( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [vm_value @ Value::U16(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + if value == 0 { + EvalAction::NormalBranch(0, smallvec![]) + } else { + EvalAction::NormalBranch(1, smallvec![vm_value]) + } +} + +pub fn eval_to_felt252( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U16(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch(0, smallvec![Value::Felt(value.into())]) +} + +pub fn eval_const( + _registry: &ProgramRegistry, + info: &IntConstConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::U16(info.c)]) +} + +pub fn eval_widemul( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U16(lhs), Value::U16(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + let result = (lhs as u32) * (rhs as u32); + + EvalAction::NormalBranch(0, smallvec![Value::U32(result)]) +} diff --git a/debug_utils/sierra-emu/src/vm/uint252.rs b/debug_utils/sierra-emu/src/vm/uint252.rs new file mode 100644 index 0000000000..b2e37359ae --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/uint252.rs @@ -0,0 +1,174 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + int::unsigned256::Uint256Concrete, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use num_bigint::BigUint; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Uint256Concrete, + args: Vec, +) -> EvalAction { + match selector { + Uint256Concrete::IsZero(info) => eval_is_zero(registry, info, args), + Uint256Concrete::Divmod(info) => eval_divmod(registry, info, args), + Uint256Concrete::SquareRoot(info) => eval_square_root(registry, info, args), + Uint256Concrete::InvModN(info) => eval_inv_mod_n(registry, info, args), + } +} + +fn eval_inv_mod_n( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Struct(x), Value::Struct(modulo)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let [Value::U128(x_lo), Value::U128(x_hi)]: [Value; 2] = x.clone().try_into().unwrap() else { + panic!() + }; + + let [Value::U128(mod_lo), Value::U128(mod_hi)]: [Value; 2] = modulo.clone().try_into().unwrap() + else { + panic!() + }; + + let x = u256_to_biguint(x_lo, x_hi); + let modulo = u256_to_biguint(mod_lo, mod_hi); + + match x.modinv(&modulo) { + Some(r) => EvalAction::NormalBranch( + 0, + smallvec![ + range_check, + u256_to_value(r), + Value::Unit, + Value::Unit, + Value::Unit, + Value::Unit, + Value::Unit, + Value::Unit, + Value::Unit, + Value::Unit + ], + ), + None => EvalAction::NormalBranch(1, smallvec![range_check, Value::Unit, Value::Unit]), + } +} + +pub fn eval_is_zero( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::Struct(fields)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + let [Value::U128(lo), Value::U128(hi)]: [Value; 2] = fields.clone().try_into().unwrap() else { + panic!() + }; + + if lo == 0 && hi == 0 { + EvalAction::NormalBranch(0, smallvec![]) + } else { + EvalAction::NormalBranch(1, smallvec![Value::Struct(fields)]) + } +} + +#[inline] +pub fn u256_to_biguint(lo: u128, hi: u128) -> BigUint { + BigUint::from(lo) + (BigUint::from(hi) << 128) +} + +#[inline] +pub fn u256_to_value(value: BigUint) -> Value { + let hi: u128 = (&value >> 128u32).try_into().unwrap(); + let lo: u128 = (value & BigUint::from(u128::MAX)).try_into().unwrap(); + Value::Struct(vec![Value::U128(lo), Value::U128(hi)]) +} + +#[inline] +pub fn u516_to_value(value: BigUint) -> Value { + let upper_u256: BigUint = &value >> 256u32; + let hi1: u128 = (&upper_u256 >> 128u32).try_into().unwrap(); + let lo1: u128 = (upper_u256 & BigUint::from(u128::MAX)).try_into().unwrap(); + let hi: u128 = (&value >> 128u32).try_into().unwrap(); + let lo: u128 = (value & BigUint::from(u128::MAX)).try_into().unwrap(); + Value::Struct(vec![ + Value::U128(lo), + Value::U128(hi), + Value::U128(lo1), + Value::U128(hi1), + ]) +} + +pub fn eval_divmod( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Struct(lhs), Value::Struct(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let [Value::U128(lhs_lo), Value::U128(lhs_hi)]: [Value; 2] = lhs.try_into().unwrap() else { + panic!() + }; + + let lhs = u256_to_biguint(lhs_lo, lhs_hi); + + let [Value::U128(rhs_lo), Value::U128(rhs_hi)]: [Value; 2] = rhs.try_into().unwrap() else { + panic!() + }; + + let rhs = u256_to_biguint(rhs_lo, rhs_hi); + + let div = &lhs / &rhs; + let modulo = lhs % rhs; + + EvalAction::NormalBranch( + 0, + smallvec![ + range_check, + u256_to_value(div), + u256_to_value(modulo), + Value::Unit + ], + ) +} + +pub fn eval_square_root( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Struct(lhs)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + let [Value::U128(lhs_lo), Value::U128(lhs_hi)]: [Value; 2] = lhs.try_into().unwrap() else { + panic!() + }; + + let lhs = u256_to_biguint(lhs_lo, lhs_hi); + let sqrt = lhs.sqrt(); + + let sqrt_lo: u128 = sqrt.clone().try_into().unwrap(); + + EvalAction::NormalBranch(0, smallvec![range_check, Value::U128(sqrt_lo)]) +} diff --git a/debug_utils/sierra-emu/src/vm/uint32.rs b/debug_utils/sierra-emu/src/vm/uint32.rs new file mode 100644 index 0000000000..6c9e2c3eb9 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/uint32.rs @@ -0,0 +1,176 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + int::{ + unsigned::{Uint32Concrete, Uint32Traits}, + IntConstConcreteLibfunc, IntOperationConcreteLibfunc, IntOperator, + }, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; +use starknet_crypto::Felt; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Uint32Concrete, + args: Vec, +) -> EvalAction { + match selector { + Uint32Concrete::Const(info) => eval_const(registry, info, args), + Uint32Concrete::Operation(info) => eval_operation(registry, info, args), + Uint32Concrete::SquareRoot(_) => todo!(), + Uint32Concrete::Equal(info) => eval_equal(registry, info, args), + Uint32Concrete::ToFelt252(info) => eval_to_felt252(registry, info, args), + Uint32Concrete::FromFelt252(info) => eval_from_felt(registry, info, args), + Uint32Concrete::IsZero(info) => eval_is_zero(registry, info, args), + Uint32Concrete::Divmod(info) => eval_divmod(registry, info, args), + Uint32Concrete::WideMul(info) => eval_widemul(registry, info, args), + Uint32Concrete::Bitwise(info) => eval_bitwise(registry, info, args), + } +} + +pub fn eval_divmod( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U32(x), Value::U32(y)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let val = Value::U32(x / y); + let rem = Value::U32(x % y); + + EvalAction::NormalBranch(0, smallvec![range_check, val, rem]) +} + +pub fn eval_operation( + _registry: &ProgramRegistry, + info: &IntOperationConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U32(lhs), Value::U32(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let (result, has_overflow) = match info.operator { + IntOperator::OverflowingAdd => lhs.overflowing_add(rhs), + IntOperator::OverflowingSub => lhs.overflowing_sub(rhs), + }; + + EvalAction::NormalBranch( + has_overflow as usize, + smallvec![range_check, Value::U32(result)], + ) +} + +pub fn eval_from_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Felt(value)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + let max = Felt::from(u32::MAX); + + if value <= max { + let value: u32 = value.to_biguint().try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![range_check, Value::U32(value)]) + } else { + EvalAction::NormalBranch(1, smallvec![range_check]) + } +} + +pub fn eval_equal( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U32(lhs), Value::U32(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch((lhs == rhs) as usize, smallvec![]) +} + +pub fn eval_bitwise( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [bitwise @ Value::Unit, Value::U32(lhs), Value::U32(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let and = lhs & rhs; + let or = lhs | rhs; + let xor = lhs ^ rhs; + + EvalAction::NormalBranch( + 0, + smallvec![bitwise, Value::U32(and), Value::U32(xor), Value::U32(or)], + ) +} + +pub fn eval_is_zero( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [vm_value @ Value::U32(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + if value == 0 { + EvalAction::NormalBranch(0, smallvec![]) + } else { + EvalAction::NormalBranch(1, smallvec![vm_value]) + } +} + +pub fn eval_to_felt252( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U32(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch(0, smallvec![Value::Felt(value.into())]) +} + +pub fn eval_const( + _registry: &ProgramRegistry, + info: &IntConstConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::U32(info.c)]) +} + +pub fn eval_widemul( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U32(lhs), Value::U32(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + let result = (lhs as u64) * (rhs as u64); + + EvalAction::NormalBranch(0, smallvec![Value::U64(result)]) +} diff --git a/debug_utils/sierra-emu/src/vm/uint512.rs b/debug_utils/sierra-emu/src/vm/uint512.rs new file mode 100644 index 0000000000..03a6268742 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/uint512.rs @@ -0,0 +1,68 @@ +use super::EvalAction; +use crate::{ + vm::uint252::{u256_to_biguint, u256_to_value, u516_to_value}, + Value, +}; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + int::unsigned512::Uint512Concrete, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Uint512Concrete, + args: Vec, +) -> EvalAction { + match selector { + Uint512Concrete::DivModU256(info) => eval_divmod(registry, info, args), + } +} + +pub fn eval_divmod( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Struct(lhs), Value::Struct(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let [Value::U128(div_0), Value::U128(div_1), Value::U128(div_2), Value::U128(div_3)]: [Value; + 4] = lhs.try_into().unwrap() + else { + panic!() + }; + + let lhs = u256_to_biguint(div_0, div_1) | (u256_to_biguint(div_2, div_3) << 256); + + let [Value::U128(divisor_0), Value::U128(divisor_1)]: [Value; 2] = rhs.try_into().unwrap() + else { + panic!() + }; + + let rhs = u256_to_biguint(divisor_0, divisor_1); + + let div = &lhs / &rhs; + let modulo = lhs % rhs; + + EvalAction::NormalBranch( + 0, + smallvec![ + range_check, + u516_to_value(div), + u256_to_value(modulo), + Value::Unit, + Value::Unit, + Value::Unit, + Value::Unit, + Value::Unit, + ], + ) +} diff --git a/debug_utils/sierra-emu/src/vm/uint64.rs b/debug_utils/sierra-emu/src/vm/uint64.rs new file mode 100644 index 0000000000..81622690cc --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/uint64.rs @@ -0,0 +1,176 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + int::{ + unsigned::{Uint64Concrete, Uint64Traits}, + IntConstConcreteLibfunc, IntOperationConcreteLibfunc, IntOperator, + }, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; +use starknet_crypto::Felt; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Uint64Concrete, + args: Vec, +) -> EvalAction { + match selector { + Uint64Concrete::Const(info) => eval_const(registry, info, args), + Uint64Concrete::Operation(info) => eval_operation(registry, info, args), + Uint64Concrete::SquareRoot(_) => todo!(), + Uint64Concrete::Equal(info) => eval_equal(registry, info, args), + Uint64Concrete::ToFelt252(info) => eval_to_felt252(registry, info, args), + Uint64Concrete::FromFelt252(info) => eval_from_felt(registry, info, args), + Uint64Concrete::IsZero(info) => eval_is_zero(registry, info, args), + Uint64Concrete::Divmod(info) => eval_divmod(registry, info, args), + Uint64Concrete::WideMul(info) => eval_widemul(registry, info, args), + Uint64Concrete::Bitwise(info) => eval_bitwise(registry, info, args), + } +} + +pub fn eval_divmod( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U64(x), Value::U64(y)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let val = Value::U64(x / y); + let rem = Value::U64(x % y); + + EvalAction::NormalBranch(0, smallvec![range_check, val, rem]) +} + +pub fn eval_from_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Felt(value)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + let max = Felt::from(u64::MAX); + + if value <= max { + let value: u64 = value.to_biguint().try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![range_check, Value::U64(value)]) + } else { + EvalAction::NormalBranch(1, smallvec![range_check]) + } +} + +pub fn eval_operation( + _registry: &ProgramRegistry, + info: &IntOperationConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U64(lhs), Value::U64(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let (result, has_overflow) = match info.operator { + IntOperator::OverflowingAdd => lhs.overflowing_add(rhs), + IntOperator::OverflowingSub => lhs.overflowing_sub(rhs), + }; + + EvalAction::NormalBranch( + has_overflow as usize, + smallvec![range_check, Value::U64(result)], + ) +} + +pub fn eval_equal( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U64(lhs), Value::U64(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch((lhs == rhs) as usize, smallvec![]) +} + +pub fn eval_bitwise( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [bitwise @ Value::Unit, Value::U64(lhs), Value::U64(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let and = lhs & rhs; + let or = lhs | rhs; + let xor = lhs ^ rhs; + + EvalAction::NormalBranch( + 0, + smallvec![bitwise, Value::U64(and), Value::U64(xor), Value::U64(or)], + ) +} + +pub fn eval_is_zero( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [vm_value @ Value::U64(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + if value == 0 { + EvalAction::NormalBranch(0, smallvec![]) + } else { + EvalAction::NormalBranch(1, smallvec![vm_value]) + } +} + +pub fn eval_to_felt252( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U64(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch(0, smallvec![Value::Felt(value.into())]) +} + +pub fn eval_const( + _registry: &ProgramRegistry, + info: &IntConstConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::U64(info.c)]) +} + +pub fn eval_widemul( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U64(lhs), Value::U64(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + let result = (lhs as u128) * (rhs as u128); + + EvalAction::NormalBranch(0, smallvec![Value::U128(result)]) +} diff --git a/debug_utils/sierra-emu/src/vm/uint8.rs b/debug_utils/sierra-emu/src/vm/uint8.rs new file mode 100644 index 0000000000..b4c06fb770 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/uint8.rs @@ -0,0 +1,176 @@ +use super::EvalAction; +use crate::Value; +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + int::{ + unsigned::{Uint8Concrete, Uint8Traits}, + IntConstConcreteLibfunc, IntOperationConcreteLibfunc, IntOperator, + }, + lib_func::SignatureOnlyConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; +use starknet_crypto::Felt; + +pub fn eval( + registry: &ProgramRegistry, + selector: &Uint8Concrete, + args: Vec, +) -> EvalAction { + match selector { + Uint8Concrete::Const(info) => eval_const(registry, info, args), + Uint8Concrete::Operation(info) => eval_operation(registry, info, args), + Uint8Concrete::SquareRoot(_) => todo!(), + Uint8Concrete::Equal(info) => eval_equal(registry, info, args), + Uint8Concrete::ToFelt252(info) => eval_to_felt252(registry, info, args), + Uint8Concrete::FromFelt252(info) => eval_from_felt(registry, info, args), + Uint8Concrete::IsZero(info) => eval_is_zero(registry, info, args), + Uint8Concrete::Divmod(info) => eval_divmod(registry, info, args), + Uint8Concrete::WideMul(info) => eval_widemul(registry, info, args), + Uint8Concrete::Bitwise(info) => eval_bitwise(registry, info, args), + } +} + +pub fn eval_divmod( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U8(x), Value::U8(y)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let val = Value::U8(x / y); + let rem = Value::U8(x % y); + + EvalAction::NormalBranch(0, smallvec![range_check, val, rem]) +} + +pub fn eval_to_felt252( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U8(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch(0, smallvec![Value::Felt(value.into())]) +} + +pub fn eval_operation( + _registry: &ProgramRegistry, + info: &IntOperationConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::U8(lhs), Value::U8(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let (result, has_overflow) = match info.operator { + IntOperator::OverflowingAdd => lhs.overflowing_add(rhs), + IntOperator::OverflowingSub => lhs.overflowing_sub(rhs), + }; + + EvalAction::NormalBranch( + has_overflow as usize, + smallvec![range_check, Value::U8(result)], + ) +} + +pub fn eval_from_felt( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [range_check @ Value::Unit, Value::Felt(value)]: [Value; 2] = args.try_into().unwrap() + else { + panic!() + }; + + let max = Felt::from(u8::MAX); + + if value <= max { + let value: u8 = value.to_biguint().try_into().unwrap(); + EvalAction::NormalBranch(0, smallvec![range_check, Value::U8(value)]) + } else { + EvalAction::NormalBranch(1, smallvec![range_check]) + } +} + +pub fn eval_equal( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U8(lhs), Value::U8(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + EvalAction::NormalBranch((lhs == rhs) as usize, smallvec![]) +} + +pub fn eval_bitwise( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [bitwise @ Value::Unit, Value::U8(lhs), Value::U8(rhs)]: [Value; 3] = + args.try_into().unwrap() + else { + panic!() + }; + + let and = lhs & rhs; + let or = lhs | rhs; + let xor = lhs ^ rhs; + + EvalAction::NormalBranch( + 0, + smallvec![bitwise, Value::U8(and), Value::U8(xor), Value::U8(or)], + ) +} + +pub fn eval_is_zero( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [vm_value @ Value::U8(value)]: [Value; 1] = args.try_into().unwrap() else { + panic!() + }; + + if value == 0 { + EvalAction::NormalBranch(0, smallvec![]) + } else { + EvalAction::NormalBranch(1, smallvec![vm_value]) + } +} + +pub fn eval_const( + _registry: &ProgramRegistry, + info: &IntConstConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::U8(info.c)]) +} + +pub fn eval_widemul( + _registry: &ProgramRegistry, + _info: &SignatureOnlyConcreteLibfunc, + args: Vec, +) -> EvalAction { + let [Value::U8(lhs), Value::U8(rhs)]: [Value; 2] = args.try_into().unwrap() else { + panic!() + }; + + let result = (lhs as u16) * (rhs as u16); + + EvalAction::NormalBranch(0, smallvec![Value::U16(result)]) +} diff --git a/debug_utils/sierra-emu/tests/libfuncs.rs b/debug_utils/sierra-emu/tests/libfuncs.rs new file mode 100644 index 0000000000..e7181885aa --- /dev/null +++ b/debug_utils/sierra-emu/tests/libfuncs.rs @@ -0,0 +1,157 @@ +use std::{path::Path, sync::Arc}; + +use cairo_lang_compiler::{compile_cairo_project_at_path, CompilerConfig}; +use cairo_lang_sierra::program::{GenFunction, Program, StatementIdx}; +use num_bigint::BigInt; +use sierra_emu::{starknet::StubSyscallHandler, Value, VirtualMachine}; + +fn run_program(path: &str, func_name: &str, args: &[Value]) -> Vec { + let path = Path::new(path); + + let sierra_program = Arc::new( + compile_cairo_project_at_path( + path, + CompilerConfig { + replace_ids: true, + ..Default::default() + }, + ) + .unwrap(), + ); + + let function = find_entry_point_by_name(&sierra_program, func_name).unwrap(); + + let mut vm = VirtualMachine::new(sierra_program.clone()); + + let args = args.iter().cloned(); + let initial_gas = 1000000; + + vm.call_program(function, initial_gas, args); + + let syscall_handler = &mut StubSyscallHandler::default(); + let trace = vm.run_with_trace(syscall_handler); + + trace + .states + .last() + .unwrap() + .items + .values() + .cloned() + .collect() +} + +#[test] +fn test_u32_overflow() { + let r = run_program( + "tests/tests/test_u32.cairo", + "test_u32::test_u32::run_test", + &[Value::U32(2), Value::U32(2)], + ); + assert!(matches!( + r[1], + Value::Enum { + self_ty: _, + index: 0, + payload: _ + } + )); + + let r = run_program( + "tests/tests/test_u32.cairo", + "test_u32::test_u32::run_test", + &[Value::U32(2), Value::U32(3)], + ); + assert!(matches!( + r[1], + Value::Enum { + self_ty: _, + index: 1, + payload: _ + } + )); + + let r = run_program( + "tests/tests/test_u32.cairo", + "test_u32::test_u32::run_test", + &[Value::U32(0), Value::U32(0)], + ); + assert!(matches!( + r[1], + Value::Enum { + self_ty: _, + index: 0, + payload: _ + } + )); +} + +pub fn find_entry_point_by_idx( + program: &Program, + entry_point_idx: usize, +) -> Option<&GenFunction> { + program + .funcs + .iter() + .find(|x| x.id.id == entry_point_idx as u64) +} + +pub fn find_entry_point_by_name<'a>( + program: &'a Program, + name: &str, +) -> Option<&'a GenFunction> { + program + .funcs + .iter() + .find(|x| x.id.debug_name.as_ref().map(|x| x.as_str()) == Some(name)) +} + +// CIRCUITS + +#[test] +fn test_run_full_circuit() { + let range96 = BigInt::ZERO..(BigInt::from(1) << 96); + let limb0 = Value::BoundedInt { + range: range96.clone(), + value: 36699840570117848377038274035_u128.into(), + }; + let limb1 = Value::BoundedInt { + range: range96.clone(), + value: 72042528776886984408017100026_u128.into(), + }; + let limb2 = Value::BoundedInt { + range: range96.clone(), + value: 54251667697617050795983757117_u128.into(), + }; + let limb3 = Value::BoundedInt { + range: range96, + value: 7.into(), + }; + + let output = run_program( + "tests/tests/circuits.cairo", + "circuits::circuits::main", + &[], + ); + let expected_output = Value::Struct(vec![Value::Struct(vec![limb0, limb1, limb2, limb3])]); + let Value::Enum { + self_ty: _, + index: _, + payload, + } = output.last().unwrap() + else { + panic!("No output"); + }; + + assert_eq!(**payload, expected_output); +} + +#[test] +#[should_panic(expected = "attempt to divide by 0")] +fn test_circuit_failure() { + run_program( + "tests/tests/circuits_failure.cairo", + "circuits_failure::circuits_failure::main", + &[], + ); +} diff --git a/debug_utils/sierra-emu/tests/syscalls.rs b/debug_utils/sierra-emu/tests/syscalls.rs new file mode 100644 index 0000000000..95bc49d0ae --- /dev/null +++ b/debug_utils/sierra-emu/tests/syscalls.rs @@ -0,0 +1,58 @@ +use std::{path::Path, sync::Arc}; + +use cairo_lang_compiler::{compile_cairo_project_at_path, CompilerConfig}; +use cairo_lang_sierra::program::{GenFunction, Program, StatementIdx}; +use sierra_emu::{starknet::StubSyscallHandler, ProgramTrace, VirtualMachine}; + +fn run_syscall(func_name: &str) -> ProgramTrace { + let path = Path::new("programs/syscalls.cairo"); + + let sierra_program = Arc::new( + compile_cairo_project_at_path( + path, + CompilerConfig { + replace_ids: true, + ..Default::default() + }, + ) + .unwrap(), + ); + + let function = find_entry_point_by_name(&sierra_program, func_name).unwrap(); + + let mut vm = VirtualMachine::new(sierra_program.clone()); + + let calldata = []; + let initial_gas = 1000000; + + vm.call_program(function, initial_gas, calldata); + + let syscall_handler = &mut StubSyscallHandler::default(); + + vm.run_with_trace(syscall_handler) +} + +#[test] +fn test_contract_constructor() { + run_syscall("syscalls::syscalls::get_execution_info_v2"); +} + +pub fn find_entry_point_by_idx( + program: &Program, + entry_point_idx: usize, +) -> Option<&GenFunction> { + program + .funcs + .iter() + .find(|x| x.id.id == entry_point_idx as u64) +} + +pub fn find_entry_point_by_name<'a>( + program: &'a Program, + name: &str, +) -> Option<&'a GenFunction> { + program + .funcs + .iter() + .find(|x| x.id.debug_name.as_ref().map(|x| x.as_str()) == Some(name)) +} diff --git a/debug_utils/sierra-emu/tests/tests/circuits.cairo b/debug_utils/sierra-emu/tests/tests/circuits.cairo new file mode 100644 index 0000000000..5e9acd179f --- /dev/null +++ b/debug_utils/sierra-emu/tests/tests/circuits.cairo @@ -0,0 +1,32 @@ +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/debug_utils/sierra-emu/tests/tests/circuits_failure.cairo b/debug_utils/sierra-emu/tests/tests/circuits_failure.cairo new file mode 100644 index 0000000000..fcf2684d02 --- /dev/null +++ b/debug_utils/sierra-emu/tests/tests/circuits_failure.cairo @@ -0,0 +1,20 @@ +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() { + let in1 = CircuitElement::> {}; + let inv = circuit_inverse(in1); + + let modulus = TryInto::<_, CircuitModulus>::try_into([7, 0, 0, 0]).unwrap(); + let outputs = (inv,) + .new_inputs() + .next([0, 0, 0, 0]) + .done() + .eval(modulus) + .unwrap(); + + outputs.get_output(inv); +} diff --git a/debug_utils/sierra-emu/tests/tests/test_u32.cairo b/debug_utils/sierra-emu/tests/tests/test_u32.cairo new file mode 100644 index 0000000000..1aa78c3890 --- /dev/null +++ b/debug_utils/sierra-emu/tests/tests/test_u32.cairo @@ -0,0 +1,3 @@ +fn run_test(lhs: u32, rhs: u32) -> u32 { + lhs - rhs +} From baf9b00d6adf0b1906b5042047bf425272efd335 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Fri, 11 Apr 2025 19:18:10 -0300 Subject: [PATCH 02/64] Change working dir in sierra-emu job --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82d17b316c..34abcf71dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -351,8 +351,11 @@ jobs: - name: Retreive cached dependecies uses: Swatinem/rust-cache@v2 - name: Deps + working-directory: ./debug_utils/sierra-emu run: make deps - name: Build + working-directory: ./debug_utils/sierra-emu run: cargo build --all-features --verbose - name: Run tests + working-directory: ./debug_utils/sierra-emu run: make test From 8caf29264bc792ec1900b327ba7fd5f138ca870c Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Mon, 14 Apr 2025 10:50:45 -0300 Subject: [PATCH 03/64] Remove repeated lines in sierra-emu gitignore --- debug_utils/sierra-emu/.gitignore | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/debug_utils/sierra-emu/.gitignore b/debug_utils/sierra-emu/.gitignore index 17dccf957d..50ab30c060 100644 --- a/debug_utils/sierra-emu/.gitignore +++ b/debug_utils/sierra-emu/.gitignore @@ -1,24 +1,2 @@ -# Created by https://www.toptal.com/developers/gitignore/api/rust -# Edit at https://www.toptal.com/developers/gitignore?templates=rust - -### Rust ### -# Generated by Cargo -# will have compiled files and executables -debug/ -target/ - -# These are backup files generated by rustfmt -**/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information -*.pdb - -# End of https://www.toptal.com/developers/gitignore/api/rust - -cairo2/ - -corelib - *.tar - *.json From 758c981d81e2e9e86cf5dd1485bb2eb16463d8f0 Mon Sep 17 00:00:00 2001 From: gabrielbosio Date: Mon, 14 Apr 2025 17:12:55 -0300 Subject: [PATCH 04/64] Exclude debug_utils from coverage job --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34abcf71dd..bb11f392ba 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -250,7 +250,7 @@ jobs: run: make check-llvm && make needs-cairo2 && make build-alexandria - name: Run tests and generate coverage partition ${{ matrix.partition }} - run: cargo llvm-cov nextest --verbose --features=scarb --workspace --lcov --output-path ${{ matrix.output }} --partition count:${{ matrix.partition }}/4 + run: cargo llvm-cov nextest --verbose --features=scarb --lcov --output-path ${{ matrix.output }} --partition count:${{ matrix.partition }}/4 - name: test and generate coverage corelib if: ${{ matrix.partition == '1' }} From 53343db32848321d25b444ff56395619b16473fb Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 21 Apr 2025 17:29:43 -0300 Subject: [PATCH 05/64] remove some todos from value and implement Coupon libfunc --- debug_utils/sierra-emu/src/value.rs | 13 +++++---- debug_utils/sierra-emu/src/vm.rs | 3 +- debug_utils/sierra-emu/src/vm/coupon.rs | 39 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 6 deletions(-) create mode 100644 debug_utils/sierra-emu/src/vm/coupon.rs diff --git a/debug_utils/sierra-emu/src/value.rs b/debug_utils/sierra-emu/src/value.rs index 8f6e540f12..3463dc6c85 100644 --- a/debug_utils/sierra-emu/src/value.rs +++ b/debug_utils/sierra-emu/src/value.rs @@ -66,6 +66,10 @@ pub enum Value { U32(u32), U64(u64), U8(u8), + IntRange { + x: Box, + y: Box, + }, Uninitialized { ty: ConcreteTypeId, }, @@ -107,7 +111,7 @@ impl Value { ) -> bool { let ty = registry.get_type(type_id).unwrap(); let res = match ty { - CoreTypeConcrete::Array(info) => { + CoreTypeConcrete::Array(info) | CoreTypeConcrete::Span(info) => { matches!(self, Self::Array { ty, .. } if *ty == info.ty) } CoreTypeConcrete::BoundedInt(info) => { @@ -157,7 +161,7 @@ impl Value { } // To do: - CoreTypeConcrete::Coupon(_) => todo!(), + CoreTypeConcrete::Coupon(_) => matches!(self, Self::Unit), CoreTypeConcrete::Bitwise(_) => matches!(self, Self::Unit), CoreTypeConcrete::Box(info) => self.is(registry, &info.ty), @@ -183,7 +187,7 @@ impl Value { matches!(self, Self::Unit) } }, - CoreTypeConcrete::Const(_) => todo!(), + CoreTypeConcrete::Const(info) => self.is(registry, &info.inner_ty), CoreTypeConcrete::EcOp(_) => matches!(self, Self::Unit), CoreTypeConcrete::EcPoint(_) => matches!(self, Self::EcPoint { .. }), CoreTypeConcrete::EcState(_) => matches!(self, Self::EcState { .. }), @@ -203,7 +207,6 @@ impl Value { } CoreTypeConcrete::Pedersen(_) => matches!(self, Self::Unit), CoreTypeConcrete::Poseidon(_) => matches!(self, Self::Unit), - CoreTypeConcrete::Span(_) => todo!(), CoreTypeConcrete::Starknet(inner) => match inner { StarknetTypeConcrete::ClassHash(_) | StarknetTypeConcrete::ContractAddress(_) @@ -213,7 +216,7 @@ impl Value { StarknetTypeConcrete::Secp256Point(_) => matches!(self, Self::Struct(_)), StarknetTypeConcrete::Sha256StateHandle(_) => matches!(self, Self::Struct { .. }), }, - CoreTypeConcrete::IntRange(_) => todo!(), + CoreTypeConcrete::IntRange(_) => matches!(self, Value::IntRange { .. }), CoreTypeConcrete::Blake(_) => todo!(), CoreTypeConcrete::QM31(_) => todo!(), }; diff --git a/debug_utils/sierra-emu/src/vm.rs b/debug_utils/sierra-emu/src/vm.rs index 86b754b47b..e59c3ec94a 100644 --- a/debug_utils/sierra-emu/src/vm.rs +++ b/debug_utils/sierra-emu/src/vm.rs @@ -37,6 +37,7 @@ mod bytes31; mod cast; mod circuit; mod r#const; +mod coupon; mod drop; mod dup; mod ec; @@ -466,7 +467,7 @@ fn eval<'a>( CoreConcreteLibfunc::Cast(selector) => self::cast::eval(registry, selector, args), CoreConcreteLibfunc::Circuit(selector) => self::circuit::eval(registry, selector, args), CoreConcreteLibfunc::Const(selector) => self::r#const::eval(registry, selector, args), - CoreConcreteLibfunc::Coupon(_) => todo!(), + CoreConcreteLibfunc::Coupon(selector) => self::coupon::eval(registry, selector, args), CoreConcreteLibfunc::CouponCall(_) => todo!(), CoreConcreteLibfunc::Debug(_) => todo!(), CoreConcreteLibfunc::Drop(info) => self::drop::eval(registry, info, args), diff --git a/debug_utils/sierra-emu/src/vm/coupon.rs b/debug_utils/sierra-emu/src/vm/coupon.rs new file mode 100644 index 0000000000..f701eb7af9 --- /dev/null +++ b/debug_utils/sierra-emu/src/vm/coupon.rs @@ -0,0 +1,39 @@ +use cairo_lang_sierra::{ + extensions::{ + core::{CoreLibfunc, CoreType}, + coupon::CouponConcreteLibfunc, + function_call::SignatureAndFunctionConcreteLibfunc, + }, + program_registry::ProgramRegistry, +}; +use smallvec::smallvec; + +use super::EvalAction; +use crate::Value; + +pub fn eval( + registry: &ProgramRegistry, + selector: &CouponConcreteLibfunc, + args: Vec, +) -> EvalAction { + match selector { + CouponConcreteLibfunc::Buy(info) => eval_buy(registry, info, args), + CouponConcreteLibfunc::Refund(info) => eval_refund(registry, info, args), + } +} + +fn eval_buy( + _registry: &ProgramRegistry, + _info: &SignatureAndFunctionConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![Value::Unit]) +} + +fn eval_refund( + _registry: &ProgramRegistry, + _info: &SignatureAndFunctionConcreteLibfunc, + _args: Vec, +) -> EvalAction { + EvalAction::NormalBranch(0, smallvec![]) +} From 37494f2b36cba445d13deb4aa274f13b5e9fda49 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 21 Apr 2025 18:01:48 -0300 Subject: [PATCH 06/64] start adding corelib test --- Cargo.lock | 1 + debug_utils/sierra-emu/Cargo.toml | 1 + debug_utils/sierra-emu/tests/corelib.rs | 62 +++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 debug_utils/sierra-emu/tests/corelib.rs diff --git a/Cargo.lock b/Cargo.lock index fa7e503ba4..24ed0801e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3303,6 +3303,7 @@ dependencies = [ "cairo-lang-sierra-gas", "cairo-lang-starknet", "cairo-lang-starknet-classes", + "cairo-lang-test-plugin", "cairo-lang-utils", "clap", "k256", diff --git a/debug_utils/sierra-emu/Cargo.toml b/debug_utils/sierra-emu/Cargo.toml index 1a69e7fbe7..fbfc7f926a 100644 --- a/debug_utils/sierra-emu/Cargo.toml +++ b/debug_utils/sierra-emu/Cargo.toml @@ -16,6 +16,7 @@ cairo-lang-sierra = "=2.12.0-dev.0" cairo-lang-sierra-ap-change = "=2.12.0-dev.0" cairo-lang-sierra-gas = "=2.12.0-dev.0" cairo-lang-starknet-classes = "=2.12.0-dev.0" +cairo-lang-test-plugin = "=2.12.0-dev.0" cairo-lang-utils = "=2.12.0-dev.0" clap = { version = "4.5.26", features = ["derive"] } k256 = "0.13.4" diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs new file mode 100644 index 0000000000..1942732a92 --- /dev/null +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -0,0 +1,62 @@ +use std::path::Path; + +use cairo_lang_compiler::{db::RootDatabase, diagnostics::DiagnosticsReporter, project::{check_compiler_path, setup_project}}; +use cairo_lang_filesystem::cfg::{Cfg, CfgSet}; +use cairo_lang_starknet::starknet_plugin_suite; +use cairo_lang_test_plugin::{compile_test_prepared_db, test_plugin_suite, TestsCompilationConfig}; + +#[test] +fn test_corelib() { + let compiler_path = Path::new("corelib"); + + check_compiler_path(false, &compiler_path).unwrap(); + + let db = &mut { + let mut b = RootDatabase::builder(); + b.detect_corelib(); + b.with_cfg(CfgSet::from_iter([Cfg::name("test")])); + b.with_default_plugin_suite(test_plugin_suite()); + b.with_default_plugin_suite(starknet_plugin_suite()); + b.build().unwrap() + }; + + let main_crate_ids = setup_project(db, &compiler_path).unwrap(); + + let db = db.snapshot(); + let test_crate_ids = main_crate_ids.clone(); + let test_config = TestsCompilationConfig { + starknet: true, + add_statements_functions: false, + add_statements_code_locations: false, + contract_declarations: None, + contract_crate_ids: None, + executable_crate_ids: None, + }; + + let mut diag_reporter = DiagnosticsReporter::stderr().with_crates(&main_crate_ids); + + let build_test_compilation = + compile_test_prepared_db(&db, test_config, test_crate_ids.clone(), diag_reporter).unwrap(); + + let (compiled, filtered_out) = filter_test_cases( + build_test_compilation, + args.include_ignored, + args.ignored, + args.filter.clone(), + ); + + let compiled = filter_test_case_compilation(compiled, &args.skip_compilation); + + let summary = + + display_tests_summary(&summary, filtered_out); + if !summary.failed.is_empty() { + bail!( + "test result: {}. {} passed; {} failed; {} ignored", + "FAILED".bright_red(), + summary.passed.len(), + summary.failed.len(), + summary.ignored.len() + ); + } +} From c7f6e0c7a10f519a561133fe22dadddf8856fcd5 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 22 Apr 2025 18:00:00 -0300 Subject: [PATCH 07/64] run tests from corelib --- Cargo.lock | 176 +++++++++------ debug_utils/sierra-emu/Cargo.toml | 7 + debug_utils/sierra-emu/src/args.rs | 20 +- debug_utils/sierra-emu/src/dump.rs | 1 + debug_utils/sierra-emu/src/lib.rs | 79 ++++++- debug_utils/sierra-emu/src/main.rs | 67 +----- debug_utils/sierra-emu/src/value.rs | 9 +- debug_utils/sierra-emu/src/vm/bool.rs | 28 ++- debug_utils/sierra-emu/src/vm/enum.rs | 7 +- debug_utils/sierra-emu/src/vm/int128.rs | 2 + debug_utils/sierra-emu/src/vm/starknet.rs | 5 + debug_utils/sierra-emu/tests/corelib.rs | 258 +++++++++++++++++++--- debug_utils/sierra-emu/tests/libfuncs.rs | 10 +- src/arch.rs | 3 +- 14 files changed, 482 insertions(+), 190 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24ed0801e1..7f29474068 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,9 +93,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "aquamarine" @@ -441,9 +441,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "serde", @@ -729,7 +729,7 @@ dependencies = [ "num-bigint", "num-integer", "num-traits", - "rand 0.9.0", + "rand 0.9.1", "sha2", "smol_str", "starknet-types-core", @@ -1005,7 +1005,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6cec425aef45ab28a7ee880481ad9ac22daeed811f325e9e135d71412338dacb" dependencies = [ "hashbrown 0.15.2", - "indexmap 2.8.0", + "indexmap 2.9.0", "itertools 0.14.0", "num-bigint", "num-traits", @@ -1058,7 +1058,7 @@ dependencies = [ "num-traits", "pretty_assertions_sorted", "proptest", - "rand 0.9.0", + "rand 0.9.1", "rayon", "rstest", "scarb-metadata", @@ -1172,9 +1172,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.17" +version = "1.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" dependencies = [ "shlex", ] @@ -1240,9 +1240,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.34" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e958897981290da2a852763fe9cdb89cd36977a5d729023127095fa94d95e2ff" +checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" dependencies = [ "clap_builder", "clap_derive", @@ -1250,9 +1250,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.34" +version = "4.5.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83b0f35019843db2160b5bb19ae09b4e6411ac33fc6a712003c33e03090e2489" +checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" dependencies = [ "anstream", "anstyle", @@ -1501,9 +1501,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", "pem-rfc7468", @@ -1673,9 +1673,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", "windows-sys 0.59.0", @@ -1705,9 +1705,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" [[package]] name = "flate2" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", @@ -1809,9 +1809,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -1872,9 +1872,9 @@ dependencies = [ [[package]] name = "half" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -2020,9 +2020,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -2100,10 +2100,11 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2206,9 +2207,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libloading" @@ -2222,9 +2223,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" [[package]] name = "llvm-sys" @@ -2334,9 +2335,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.8.5" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", ] @@ -2590,7 +2591,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" dependencies = [ "fixedbitset", - "indexmap 2.8.0", + "indexmap 2.9.0", ] [[package]] @@ -2710,9 +2711,9 @@ dependencies = [ [[package]] name = "prettyplease" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", "syn 2.0.100", @@ -2759,9 +2760,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -2826,13 +2827,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", - "zerocopy 0.8.24", ] [[package]] @@ -2861,7 +2861,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.2.16", ] [[package]] @@ -2910,9 +2910,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +checksum = "d2f103c6d277498fbceb16e84d317e2a400f160f46904d5f5410848c829511a3" dependencies = [ "bitflags", ] @@ -3019,7 +3019,7 @@ version = "0.17.0-pre.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719825638c59fd26a55412a24561c7c5bcf54364c88b9a7a04ba08a6eafaba8d" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.9.0", "lock_api", "oorandom", "parking_lot", @@ -3075,9 +3075,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +checksum = "d97817398dd4bb2e6da002002db259209759911da105da92bec29ccb12cf58bf" dependencies = [ "bitflags", "errno", @@ -3306,12 +3306,14 @@ dependencies = [ "cairo-lang-test-plugin", "cairo-lang-utils", "clap", + "educe 0.5.11", "k256", "keccak", "num-bigint", "num-traits", "p256", "rand 0.8.5", + "rayon", "sec1", "serde", "serde_json", @@ -3324,6 +3326,7 @@ dependencies = [ "thiserror 2.0.12", "tracing", "tracing-subscriber", + "tracing-test", ] [[package]] @@ -3342,6 +3345,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +[[package]] +name = "size-of" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4e36eca171fddeda53901b0a436573b3f2391eaa9189d439b2bd8ea8cebd7e3" + [[package]] name = "slab" version = "0.4.9" @@ -3363,9 +3372,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "smol_str" @@ -3441,9 +3450,9 @@ dependencies = [ [[package]] name = "starknet-types-core" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa1b9e01ccb217ab6d475c5cda05dbb22c30029f7bb52b192a010a00d77a3d74" +checksum = "4037bcb26ce7c508448d221e570d075196fd4f6912ae6380981098937af9522a" dependencies = [ "lambdaworks-crypto", "lambdaworks-math 0.10.0", @@ -3452,6 +3461,8 @@ dependencies = [ "num-integer", "num-traits", "serde", + "size-of", + "zeroize", ] [[package]] @@ -3539,12 +3550,12 @@ dependencies = [ [[package]] name = "term" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3bb6001afcea98122260987f8b7b5da969ecad46dbf0b5453702f776b491a41" +checksum = "8a984c8d058c627faaf5e8e2ed493fa3c51771889196de1016cf9c1c6e90d750" dependencies = [ "home", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3702,7 +3713,7 @@ version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ - "indexmap 2.8.0", + "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", @@ -3783,6 +3794,27 @@ dependencies = [ "tracing-serde", ] +[[package]] +name = "tracing-test" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68" +dependencies = [ + "tracing-core", + "tracing-subscriber", + "tracing-test-macro", +] + +[[package]] +name = "tracing-test-macro" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" +dependencies = [ + "quote", + "syn 2.0.100", +] + [[package]] name = "triomphe" version = "0.1.14" @@ -3937,23 +3969,24 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.100", @@ -3962,9 +3995,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3972,9 +4005,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -3985,15 +4018,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -4124,9 +4160,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" dependencies = [ "memchr", ] diff --git a/debug_utils/sierra-emu/Cargo.toml b/debug_utils/sierra-emu/Cargo.toml index fbfc7f926a..f22cc1fe89 100644 --- a/debug_utils/sierra-emu/Cargo.toml +++ b/debug_utils/sierra-emu/Cargo.toml @@ -9,6 +9,7 @@ readme = "README.md" keywords = ["starknet", "cairo", "compiler", "mlir"] [dependencies] +educe = "0.5.11" cairo-lang-compiler = "=2.12.0-dev.0" cairo-lang-filesystem = "=2.12.0-dev.0" cairo-lang-runner = "=2.12.0-dev.0" @@ -37,6 +38,8 @@ tempfile = "3.14.0" thiserror = "2.0.3" tracing = "0.1.41" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +tracing-test = { version = "0.2.5" } +rayon = "1.10.0" [dev-dependencies] cairo-lang-compiler = "=2.12.0-dev.0" @@ -45,3 +48,7 @@ cairo-lang-starknet = "=2.12.0-dev.0" # On dev optimize dependencies a bit so it's not as slow. [profile.dev.package."*"] opt-level = 1 + +[[test]] +name = "test_corelib" +path = "tests/corelib.rs" diff --git a/debug_utils/sierra-emu/src/args.rs b/debug_utils/sierra-emu/src/args.rs index 2108748de4..35be7edf02 100644 --- a/debug_utils/sierra-emu/src/args.rs +++ b/debug_utils/sierra-emu/src/args.rs @@ -1,5 +1,6 @@ use clap::Parser; -use std::{num::ParseIntError, path::PathBuf, str::FromStr}; +use sierra_emu::EntryPoint; +use std::path::PathBuf; #[derive(Debug, Parser)] pub struct CmdArgs { @@ -13,20 +14,3 @@ pub struct CmdArgs { #[clap(long, short)] pub output: Option, } - -#[derive(Clone, Debug)] -pub enum EntryPoint { - Number(u64), - String(String), -} - -impl FromStr for EntryPoint { - type Err = ParseIntError; - - fn from_str(s: &str) -> Result { - Ok(match s.chars().next() { - Some(x) if x.is_numeric() => Self::Number(s.parse()?), - _ => Self::String(s.to_string()), - }) - } -} diff --git a/debug_utils/sierra-emu/src/dump.rs b/debug_utils/sierra-emu/src/dump.rs index f8e5dbba75..4171fc5099 100644 --- a/debug_utils/sierra-emu/src/dump.rs +++ b/debug_utils/sierra-emu/src/dump.rs @@ -80,6 +80,7 @@ impl ContractExecutionResult { self_ty: _, index, payload, + .. } => { failure_flag = (*index) != 0; diff --git a/debug_utils/sierra-emu/src/lib.rs b/debug_utils/sierra-emu/src/lib.rs index 7c5049bdbd..c0579403c6 100644 --- a/debug_utils/sierra-emu/src/lib.rs +++ b/debug_utils/sierra-emu/src/lib.rs @@ -1,9 +1,16 @@ +use std::{num::ParseIntError, str::FromStr, sync::Arc}; + use cairo_lang_sierra::{ - extensions::core::{CoreLibfunc, CoreType}, + extensions::{ + circuit::CircuitTypeConcrete, + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + starknet::StarknetTypeConcrete, + }, ids::ConcreteTypeId, program::{GenFunction, Program, StatementIdx}, program_registry::ProgramRegistry, }; +use starknet::StubSyscallHandler; pub use self::{dump::*, gas::BuiltinCosts, value::*, vm::VirtualMachine}; @@ -15,6 +22,23 @@ mod test_utils; mod value; mod vm; +#[derive(Clone, Debug)] +pub enum EntryPoint { + Number(u64), + String(String), +} + +impl FromStr for EntryPoint { + type Err = ParseIntError; + + fn from_str(s: &str) -> Result { + Ok(match s.chars().next() { + Some(x) if x.is_numeric() => Self::Number(s.parse()?), + _ => Self::String(s.to_string()), + }) + } +} + pub fn find_entry_point_by_idx( program: &Program, entry_point_idx: usize, @@ -58,3 +82,56 @@ pub fn find_real_type( _ => ty.clone(), } } + +pub fn run_program( + program: Arc, + entry_point: EntryPoint, + args: Vec, + available_gas: u64, +) -> ProgramTrace { + let mut vm = VirtualMachine::new(program.clone()); + + let function = program + .funcs + .iter() + .find(|f| match &entry_point { + EntryPoint::Number(x) => f.id.id == *x, + EntryPoint::String(x) => f.id.debug_name.as_deref() == Some(x.as_str()), + }) + .unwrap(); + + let mut iter = args.into_iter(); + vm.push_frame( + function.id.clone(), + function + .signature + .param_types + .iter() + .map(|type_id| { + let type_info = vm.registry().get_type(type_id).unwrap(); + match type_info { + CoreTypeConcrete::Felt252(_) => Value::parse_felt(&iter.next().unwrap()), + CoreTypeConcrete::GasBuiltin(_) => Value::U64(available_gas), + CoreTypeConcrete::RangeCheck(_) + | CoreTypeConcrete::RangeCheck96(_) + | CoreTypeConcrete::Bitwise(_) + | CoreTypeConcrete::Pedersen(_) + | CoreTypeConcrete::Poseidon(_) + | CoreTypeConcrete::SegmentArena(_) + | CoreTypeConcrete::Circuit( + CircuitTypeConcrete::AddMod(_) | CircuitTypeConcrete::MulMod(_), + ) => Value::Unit, + CoreTypeConcrete::Starknet(inner) => match inner { + StarknetTypeConcrete::System(_) => Value::Unit, + _ => todo!(), + }, + _ => todo!(), + } + }) + .collect::>(), + ); + + let syscall_handler = &mut StubSyscallHandler::default(); + + vm.run_with_trace(syscall_handler) +} diff --git a/debug_utils/sierra-emu/src/main.rs b/debug_utils/sierra-emu/src/main.rs index d8508928d8..99be5c2aca 100644 --- a/debug_utils/sierra-emu/src/main.rs +++ b/debug_utils/sierra-emu/src/main.rs @@ -1,18 +1,13 @@ use self::args::CmdArgs; -use cairo_lang_sierra::{ - extensions::{ - circuit::CircuitTypeConcrete, core::CoreTypeConcrete, starknet::StarknetTypeConcrete, - }, - ProgramParser, -}; +use cairo_lang_sierra::ProgramParser; use clap::Parser; -use sierra_emu::{starknet::StubSyscallHandler, Value, VirtualMachine}; +use sierra_emu::run_program; use std::{ fs::{self, File}, io::stdout, sync::Arc, }; -use tracing::{debug, info, Level}; +use tracing::{info, Level}; use tracing_subscriber::{EnvFilter, FmtSubscriber}; mod args; @@ -37,57 +32,13 @@ fn main() -> Result<(), Box> { .map_err(|e| e.to_string())?, ); - info!("Preparing the virtual machine."); - let mut vm = VirtualMachine::new(program.clone()); - - debug!("Pushing the entry point's frame."); - let function = program - .funcs - .iter() - .find(|f| match &args.entry_point { - args::EntryPoint::Number(x) => f.id.id == *x, - args::EntryPoint::String(x) => f.id.debug_name.as_deref() == Some(x.as_str()), - }) - .unwrap(); - - debug!( - "Entry point argument types: {:?}", - function.signature.param_types - ); - let mut iter = args.args.into_iter(); - vm.push_frame( - function.id.clone(), - function - .signature - .param_types - .iter() - .map(|type_id| { - let type_info = vm.registry().get_type(type_id).unwrap(); - match type_info { - CoreTypeConcrete::Felt252(_) => Value::parse_felt(&iter.next().unwrap()), - CoreTypeConcrete::GasBuiltin(_) => Value::U64(args.available_gas.unwrap()), - CoreTypeConcrete::RangeCheck(_) - | CoreTypeConcrete::RangeCheck96(_) - | CoreTypeConcrete::Bitwise(_) - | CoreTypeConcrete::Pedersen(_) - | CoreTypeConcrete::Poseidon(_) - | CoreTypeConcrete::SegmentArena(_) - | CoreTypeConcrete::Circuit( - CircuitTypeConcrete::AddMod(_) | CircuitTypeConcrete::MulMod(_), - ) => Value::Unit, - CoreTypeConcrete::Starknet(inner) => match inner { - StarknetTypeConcrete::System(_) => Value::Unit, - _ => todo!(), - }, - _ => todo!(), - } - }) - .collect::>(), - ); - info!("Running the program."); - let syscall_handler = &mut StubSyscallHandler::default(); - let trace = vm.run_with_trace(syscall_handler); + let trace = run_program( + program, + args.entry_point, + args.args, + args.available_gas.unwrap_or_default(), + ); match args.output { Some(path) => serde_json::to_writer(File::create(path)?, &trace)?, diff --git a/debug_utils/sierra-emu/src/value.rs b/debug_utils/sierra-emu/src/value.rs index 3463dc6c85..d7b4b32da2 100644 --- a/debug_utils/sierra-emu/src/value.rs +++ b/debug_utils/sierra-emu/src/value.rs @@ -8,14 +8,16 @@ use cairo_lang_sierra::{ ids::ConcreteTypeId, program_registry::ProgramRegistry, }; +use educe::Educe; use num_bigint::{BigInt, BigUint}; use serde::Serialize; use starknet_types_core::felt::Felt; -use std::{collections::HashMap, fmt::Debug, ops::Range}; +use std::{collections::HashMap, ops::Range}; use crate::{debug::type_to_name, gas::BuiltinCosts}; -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[derive(Clone, Educe, Serialize)] +#[educe(Debug, Eq, PartialEq)] pub enum Value { Array { ty: ConcreteTypeId, @@ -32,6 +34,8 @@ pub enum Value { self_ty: ConcreteTypeId, index: usize, payload: Box, + #[educe(PartialEq(ignore))] + debug_name: Option, }, Felt(Felt), Bytes31(Felt), @@ -93,6 +97,7 @@ impl Value { self_ty: type_id.clone(), index: 0, payload: Box::new(Value::default_for_type(registry, &info.variants[0])), + debug_name: type_id.debug_name.as_ref().map(|n| n.to_string()), }, CoreTypeConcrete::Struct(info) => Value::Struct( info.members diff --git a/debug_utils/sierra-emu/src/vm/bool.rs b/debug_utils/sierra-emu/src/vm/bool.rs index 8d632953a7..ec22f98ddc 100644 --- a/debug_utils/sierra-emu/src/vm/bool.rs +++ b/debug_utils/sierra-emu/src/vm/bool.rs @@ -33,10 +33,12 @@ pub fn eval_and( self_ty, index: lhs, payload, + .. }, Value::Enum { self_ty: _, index: rhs, payload: _, + .. }]: [Value; 2] = args.try_into().unwrap() else { panic!() @@ -48,9 +50,10 @@ pub fn eval_and( EvalAction::NormalBranch( 0, smallvec![Value::Enum { - self_ty, + self_ty: self_ty.clone(), index: (lhs && rhs) as usize, - payload + payload, + debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()) }], ) } @@ -64,6 +67,7 @@ pub fn eval_not( self_ty, index: lhs, payload, + .. }]: [Value; 1] = args.try_into().unwrap() else { panic!() @@ -72,9 +76,10 @@ pub fn eval_not( EvalAction::NormalBranch( 0, smallvec![Value::Enum { - self_ty, + self_ty: self_ty.clone(), index: (lhs == 0) as usize, - payload + payload, + debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()) }], ) } @@ -88,10 +93,12 @@ pub fn eval_xor( self_ty, index: lhs, payload, + .. }, Value::Enum { self_ty: _, index: rhs, payload: _, + .. }]: [Value; 2] = args.try_into().unwrap() else { panic!() @@ -103,9 +110,10 @@ pub fn eval_xor( EvalAction::NormalBranch( 0, smallvec![Value::Enum { - self_ty, + self_ty: self_ty.clone(), index: (lhs ^ rhs) as usize, - payload + payload, + debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()) }], ) } @@ -119,10 +127,12 @@ pub fn eval_or( self_ty, index: lhs, payload, + .. }, Value::Enum { self_ty: _, index: rhs, payload: _, + .. }]: [Value; 2] = args.try_into().unwrap() else { panic!() @@ -134,9 +144,10 @@ pub fn eval_or( EvalAction::NormalBranch( 0, smallvec![Value::Enum { - self_ty, + self_ty: self_ty.clone(), index: (lhs || rhs) as usize, - payload + payload, + debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()) }], ) } @@ -150,6 +161,7 @@ pub fn eval_to_felt252( self_ty: _, index: lhs, payload: _, + .. }]: [Value; 1] = args.try_into().unwrap() else { panic!() diff --git a/debug_utils/sierra-emu/src/vm/enum.rs b/debug_utils/sierra-emu/src/vm/enum.rs index 5fce56b98f..dba38e97f6 100644 --- a/debug_utils/sierra-emu/src/vm/enum.rs +++ b/debug_utils/sierra-emu/src/vm/enum.rs @@ -50,6 +50,7 @@ pub fn eval_init( self_ty: self_ty.clone(), index: info.index, payload: Box::new(value), + debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()) }], ) } @@ -62,11 +63,13 @@ pub fn eval_from_bounded_int( let [Value::BoundedInt { range: _, value }]: [Value; 1] = args.try_into().unwrap() else { panic!() }; + let self_ty = &info.branch_signatures()[0].vars[0].ty; let enm = Value::Enum { - self_ty: info.branch_signatures()[0].vars[0].ty.clone(), + self_ty: self_ty.clone(), index: value.try_into().unwrap(), payload: Box::new(Value::Struct(vec![])), + debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()), }; EvalAction::NormalBranch(0, smallvec![enm]) @@ -81,6 +84,7 @@ pub fn eval_match( self_ty, index, payload, + .. }]: [Value; 1] = args.try_into().unwrap() else { panic!() @@ -103,6 +107,7 @@ pub fn eval_snapshot_match( self_ty, index, payload, + .. }]: [Value; 1] = args.try_into().unwrap() else { panic!() diff --git a/debug_utils/sierra-emu/src/vm/int128.rs b/debug_utils/sierra-emu/src/vm/int128.rs index 09f6a91e5b..ad34e5fb4f 100644 --- a/debug_utils/sierra-emu/src/vm/int128.rs +++ b/debug_utils/sierra-emu/src/vm/int128.rs @@ -153,6 +153,7 @@ mod tests { self_ty: _, index: _, payload, + .. } = result.last().unwrap() else { panic!("No output"); @@ -181,6 +182,7 @@ mod tests { self_ty: _, index: _, payload, + .. } = result.last().unwrap() else { panic!("No output"); diff --git a/debug_utils/sierra-emu/src/vm/starknet.rs b/debug_utils/sierra-emu/src/vm/starknet.rs index 4d039ebd9f..fc548820e9 100644 --- a/debug_utils/sierra-emu/src/vm/starknet.rs +++ b/debug_utils/sierra-emu/src/vm/starknet.rs @@ -200,11 +200,13 @@ fn eval_secp_r_new( self_ty: enum_ty.clone(), index: 0, payload: Box::new(p.into_value()), + debug_name: enum_ty.debug_name.as_ref().map(|n| n.to_string()), }, None => Value::Enum { self_ty: enum_ty.clone(), index: 1, payload: Box::new(Value::Unit), + debug_name: enum_ty.debug_name.as_ref().map(|n| n.to_string()), }, }; @@ -264,11 +266,13 @@ fn eval_secp_r_get_point_from_x( self_ty: enum_ty.clone(), index: 0, payload: Box::new(p.into_value()), + debug_name: enum_ty.debug_name.as_ref().map(|n| n.to_string()), }, None => Value::Enum { self_ty: enum_ty.clone(), index: 1, payload: Box::new(Value::Unit), + debug_name: enum_ty.debug_name.as_ref().map(|n| n.to_string()), }, }; @@ -887,6 +891,7 @@ fn eval_deploy( self_ty: _, index: deploy_from_zero, payload: _, + .. }]: [Value; 6] = args.try_into().unwrap() else { panic!() diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 1942732a92..520e5991c5 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -1,15 +1,30 @@ -use std::path::Path; +use std::{path::Path, sync::Arc, u64}; -use cairo_lang_compiler::{db::RootDatabase, diagnostics::DiagnosticsReporter, project::{check_compiler_path, setup_project}}; +use cairo_lang_compiler::{ + db::RootDatabase, + diagnostics::DiagnosticsReporter, + project::{check_compiler_path, setup_project}, +}; use cairo_lang_filesystem::cfg::{Cfg, CfgSet}; +use cairo_lang_runner::{casm_run::format_for_panic, RunResultValue}; use cairo_lang_starknet::starknet_plugin_suite; -use cairo_lang_test_plugin::{compile_test_prepared_db, test_plugin_suite, TestsCompilationConfig}; +use cairo_lang_test_plugin::{ + compile_test_prepared_db, + test_config::{PanicExpectation, TestExpectation}, + test_plugin_suite, TestCompilation, TestsCompilationConfig, +}; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use sierra_emu::{run_program, EntryPoint, ProgramTrace, Value}; +use starknet_crypto::Felt; +use tracing::{error, info}; +use tracing_test::traced_test; #[test] +#[traced_test] fn test_corelib() { - let compiler_path = Path::new("corelib"); + let compiler_path = Path::new("../../corelib"); - check_compiler_path(false, &compiler_path).unwrap(); + check_compiler_path(false, &compiler_path).expect("Couldn't the corelib in the given path"); let db = &mut { let mut b = RootDatabase::builder(); @@ -17,6 +32,7 @@ fn test_corelib() { b.with_cfg(CfgSet::from_iter([Cfg::name("test")])); b.with_default_plugin_suite(test_plugin_suite()); b.with_default_plugin_suite(starknet_plugin_suite()); + b.build().unwrap() }; @@ -25,38 +41,224 @@ fn test_corelib() { let db = db.snapshot(); let test_crate_ids = main_crate_ids.clone(); let test_config = TestsCompilationConfig { - starknet: true, - add_statements_functions: false, - add_statements_code_locations: false, + starknet: false, + add_statements_functions: true, + add_statements_code_locations: true, contract_declarations: None, contract_crate_ids: None, executable_crate_ids: None, }; - let mut diag_reporter = DiagnosticsReporter::stderr().with_crates(&main_crate_ids); + let diag_reporter = DiagnosticsReporter::stderr().with_crates(&main_crate_ids); let build_test_compilation = compile_test_prepared_db(&db, test_config, test_crate_ids.clone(), diag_reporter).unwrap(); - let (compiled, filtered_out) = filter_test_cases( - build_test_compilation, - args.include_ignored, - args.ignored, - args.filter.clone(), - ); - - let compiled = filter_test_case_compilation(compiled, &args.skip_compilation); - - let summary = - - display_tests_summary(&summary, filtered_out); - if !summary.failed.is_empty() { - bail!( - "test result: {}. {} passed; {} failed; {} ignored", - "FAILED".bright_red(), - summary.passed.len(), - summary.failed.len(), - summary.ignored.len() + run_tests(build_test_compilation); +} + +/// Runs the tests and process the results for a summary. +pub fn run_tests(compiled: TestCompilation) { + let program = Arc::new(compiled.sierra_program.program); + let success = true; + + compiled.metadata.named_tests.into_par_iter().for_each_with(success, move |success, (name, test)| { + let trace = run_program( + program.clone(), + EntryPoint::String(name.clone()), + vec![], + u64::MAX, ); + let run_result = trace_to_run_result(trace); + + *success &= assert_test_expectation(name, test.expectation, run_result); + }); + + assert!(success); +} + +fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { + let result = trace + .states + .last() + .unwrap() + .items + .values() + .cloned() + .collect::>(); + let return_value = result.last().unwrap(); + let mut felts = Vec::new(); + + let is_success = match &return_value { + outer_value @ Value::Enum { + index, + payload, + debug_name: _, + .. + } => { + //let debug_name = debug_name.as_ref().expect("missing debug name"); + if *index == 0 + // debug_name.starts_with("core::panics::PanicResult::") + // || debug_name.starts_with("Enum { + for field in fields { + let felt = jitvalue_to_felt(&field); + felts.extend(felt); + } + } + _ => panic!("unsuported return value in cairo-native"), + } + } else { + felts.extend(jitvalue_to_felt(&*payload)); + } + + is_success + } else { + felts.extend(jitvalue_to_felt(&outer_value)); + true + } + } + x => { + felts.extend(jitvalue_to_felt(&x)); + true + } + }; + + let return_values = felts.into_iter().map(|x| x.to_bigint().into()).collect(); + + match is_success { + true => RunResultValue::Success(return_values), + false => RunResultValue::Panic(return_values), + } +} + +fn assert_test_expectation( + name: String, + expectation: TestExpectation, + result: RunResultValue, +) -> bool { + let mut success = true; + + match result { + RunResultValue::Success(r) => { + if let TestExpectation::Panics(_) = expectation { + let err_msg = format_for_panic(r.into_iter()); + error!("test {}: {}", name, err_msg); + success = false; + } + info!("test: {}", name); + } + RunResultValue::Panic(e) => match expectation { + TestExpectation::Success => { + let err_msg = format_for_panic(e.into_iter()); + error!("test {}: {}", name, err_msg); + success = false; + } + TestExpectation::Panics(panic_expect) => { + if let PanicExpectation::Exact(expected) = panic_expect { + if expected != e { + let err_msg = format_for_panic(e.into_iter()); + error!("test {}: {}", name, err_msg); + success = false; + } + info!("test {}", name); + } + } + }, + } + + success +} + +/// Convert a Value to a felt. +pub fn jitvalue_to_felt(value: &Value) -> Vec { + let mut felts = Vec::new(); + match value { + Value::Array { data, .. } | Value::Struct(data) => { + data.iter().flat_map(jitvalue_to_felt).collect() + } + Value::BoundedInt { value, .. } => vec![value.into()], + Value::Bytes31(bytes) => vec![*bytes], + Value::BuiltinCosts(costs) => vec![ + costs.r#const.into(), + costs.pedersen.into(), + costs.bitwise.into(), + costs.ecop.into(), + costs.poseidon.into(), + costs.add_mod.into(), + costs.mul_mod.into(), + ], + Value::CircuitModulus(value) => vec![value.into()], + Value::Circuit(data) | Value::CircuitOutputs(data) => data.iter().map(Felt::from).collect(), + Value::EcPoint { x, y } => { + vec![*x, *y] + } + Value::EcState { x0, y0, x1, y1 } => { + vec![*x0, *y0, *x1, *y1] + } + Value::Enum { + index, + payload, + debug_name, + .. + } => { + if let Some(debug_name) = debug_name { + if debug_name == "core::bool" { + vec![(*index == 1).into()] + } else { + let mut felts = vec![(*index).into()]; + felts.extend(jitvalue_to_felt(payload)); + felts + } + } else { + // Assume its a regular enum. + let mut felts = vec![(*index).into()]; + felts.extend(jitvalue_to_felt(payload)); + felts + } + } + Value::Felt(felt) => vec![*felt], + Value::FeltDict { data, .. } => { + for (key, value) in data { + felts.push(*key); + let felt = jitvalue_to_felt(value); + felts.extend(felt); + } + + felts + } + Value::FeltDictEntry { + key: data_key, + data, + .. + } => { + felts.push(*data_key); + + for (key, value) in data { + felts.push(*key); + let felt = jitvalue_to_felt(value); + felts.extend(felt); + } + + felts + } + Value::IntRange { x, y } => [jitvalue_to_felt(x), jitvalue_to_felt(y)].concat(), + Value::I8(x) => vec![(*x).into()], + Value::I16(x) => vec![(*x).into()], + Value::I32(x) => vec![(*x).into()], + Value::I64(x) => vec![(*x).into()], + Value::I128(x) => vec![(*x).into()], + Value::U8(x) => vec![(*x).into()], + Value::U16(x) => vec![(*x).into()], + Value::U32(x) => vec![(*x).into()], + Value::U64(x) => vec![(*x).into()], + Value::U128(x) => vec![(*x).into()], + Value::U256(x, y) => vec![(*x).into(), (*y).into()], + Value::Unit | Value::Uninitialized { .. } => vec![0.into()], } } diff --git a/debug_utils/sierra-emu/tests/libfuncs.rs b/debug_utils/sierra-emu/tests/libfuncs.rs index e7181885aa..5594baad51 100644 --- a/debug_utils/sierra-emu/tests/libfuncs.rs +++ b/debug_utils/sierra-emu/tests/libfuncs.rs @@ -53,7 +53,8 @@ fn test_u32_overflow() { Value::Enum { self_ty: _, index: 0, - payload: _ + payload: _, + debug_name: _ } )); @@ -67,7 +68,8 @@ fn test_u32_overflow() { Value::Enum { self_ty: _, index: 1, - payload: _ + payload: _, + debug_name: _ } )); @@ -81,7 +83,8 @@ fn test_u32_overflow() { Value::Enum { self_ty: _, index: 0, - payload: _ + payload: _, + debug_name: _ } )); } @@ -138,6 +141,7 @@ fn test_run_full_circuit() { self_ty: _, index: _, payload, + debug_name: _, } = output.last().unwrap() else { panic!("No output"); diff --git a/src/arch.rs b/src/arch.rs index 400a7b0059..9ff2f35d6f 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -157,7 +157,8 @@ impl AbiArgument for ValueWithInfoWrapper<'_> { let abi_ptr = unsafe { *abi_ptr.cast::>().as_ref() }; abi_ptr.as_ptr().to_bytes(buffer, find_dict_drop_override)?; } else { - match (info.variants.len().next_power_of_two().trailing_zeros() + 7) / 8 { + match (info.variants.len().next_power_of_two().trailing_zeros() + 7).div_ceil(8) + { 0 => {} _ => (*tag as u64).to_bytes(buffer, find_dict_drop_override)?, } From a664894e4d1e62543bd24323a670ca125b085233 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 22 Apr 2025 18:23:30 -0300 Subject: [PATCH 08/64] remove unwanted file --- debug_utils/sierra-emu/src/vm/coupon.rs | 39 ------------------------- 1 file changed, 39 deletions(-) delete mode 100644 debug_utils/sierra-emu/src/vm/coupon.rs diff --git a/debug_utils/sierra-emu/src/vm/coupon.rs b/debug_utils/sierra-emu/src/vm/coupon.rs deleted file mode 100644 index f701eb7af9..0000000000 --- a/debug_utils/sierra-emu/src/vm/coupon.rs +++ /dev/null @@ -1,39 +0,0 @@ -use cairo_lang_sierra::{ - extensions::{ - core::{CoreLibfunc, CoreType}, - coupon::CouponConcreteLibfunc, - function_call::SignatureAndFunctionConcreteLibfunc, - }, - program_registry::ProgramRegistry, -}; -use smallvec::smallvec; - -use super::EvalAction; -use crate::Value; - -pub fn eval( - registry: &ProgramRegistry, - selector: &CouponConcreteLibfunc, - args: Vec, -) -> EvalAction { - match selector { - CouponConcreteLibfunc::Buy(info) => eval_buy(registry, info, args), - CouponConcreteLibfunc::Refund(info) => eval_refund(registry, info, args), - } -} - -fn eval_buy( - _registry: &ProgramRegistry, - _info: &SignatureAndFunctionConcreteLibfunc, - _args: Vec, -) -> EvalAction { - EvalAction::NormalBranch(0, smallvec![Value::Unit]) -} - -fn eval_refund( - _registry: &ProgramRegistry, - _info: &SignatureAndFunctionConcreteLibfunc, - _args: Vec, -) -> EvalAction { - EvalAction::NormalBranch(0, smallvec![]) -} From 0464dfc6027ec2ce95e4e30bd728742b6a17186e Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 22 Apr 2025 18:27:16 -0300 Subject: [PATCH 09/64] remove not related things --- debug_utils/sierra-emu/src/value.rs | 4 ---- debug_utils/sierra-emu/tests/corelib.rs | 24 +++++++++++++----------- src/arch.rs | 3 +-- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/debug_utils/sierra-emu/src/value.rs b/debug_utils/sierra-emu/src/value.rs index d822d14671..30a2d296ab 100644 --- a/debug_utils/sierra-emu/src/value.rs +++ b/debug_utils/sierra-emu/src/value.rs @@ -72,10 +72,6 @@ pub enum Value { U32(u32), U64(u64), U8(u8), - IntRange { - x: Box, - y: Box, - }, Uninitialized { ty: ConcreteTypeId, }, diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 520e5991c5..aed05d251c 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -62,17 +62,20 @@ pub fn run_tests(compiled: TestCompilation) { let program = Arc::new(compiled.sierra_program.program); let success = true; - compiled.metadata.named_tests.into_par_iter().for_each_with(success, move |success, (name, test)| { - let trace = run_program( - program.clone(), - EntryPoint::String(name.clone()), - vec![], - u64::MAX, - ); - let run_result = trace_to_run_result(trace); + compiled.metadata.named_tests.into_par_iter().for_each_with( + success, + move |success, (name, test)| { + let trace = run_program( + program.clone(), + EntryPoint::String(name.clone()), + vec![], + u64::MAX, + ); + let run_result = trace_to_run_result(trace); - *success &= assert_test_expectation(name, test.expectation, run_result); - }); + *success &= assert_test_expectation(name, test.expectation, run_result); + }, + ); assert!(success); } @@ -247,7 +250,6 @@ pub fn jitvalue_to_felt(value: &Value) -> Vec { felts } - Value::IntRange { x, y } => [jitvalue_to_felt(x), jitvalue_to_felt(y)].concat(), Value::I8(x) => vec![(*x).into()], Value::I16(x) => vec![(*x).into()], Value::I32(x) => vec![(*x).into()], diff --git a/src/arch.rs b/src/arch.rs index 9ff2f35d6f..400a7b0059 100644 --- a/src/arch.rs +++ b/src/arch.rs @@ -157,8 +157,7 @@ impl AbiArgument for ValueWithInfoWrapper<'_> { let abi_ptr = unsafe { *abi_ptr.cast::>().as_ref() }; abi_ptr.as_ptr().to_bytes(buffer, find_dict_drop_override)?; } else { - match (info.variants.len().next_power_of_two().trailing_zeros() + 7).div_ceil(8) - { + match (info.variants.len().next_power_of_two().trailing_zeros() + 7) / 8 { 0 => {} _ => (*tag as u64).to_bytes(buffer, find_dict_drop_override)?, } From 30c6191a85e513a388c880cfe00518a32267430e Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 23 Apr 2025 14:59:17 -0300 Subject: [PATCH 10/64] get return_value from trace --- debug_utils/sierra-emu/src/dump.rs | 13 +++++++++++++ debug_utils/sierra-emu/tests/corelib.rs | 10 +--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/debug_utils/sierra-emu/src/dump.rs b/debug_utils/sierra-emu/src/dump.rs index 4171fc5099..be49a8b21f 100644 --- a/debug_utils/sierra-emu/src/dump.rs +++ b/debug_utils/sierra-emu/src/dump.rs @@ -19,6 +19,19 @@ impl ProgramTrace { pub fn push(&mut self, state: StateDump) { self.states.push(state); } + + pub fn return_value(&self) -> Option { + self + .states + .last() + .unwrap() + .items + .values() + .cloned() + .collect::>() + .last() + .cloned() + } } #[derive(Clone, Debug)] diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index aed05d251c..50448c8076 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -81,15 +81,7 @@ pub fn run_tests(compiled: TestCompilation) { } fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { - let result = trace - .states - .last() - .unwrap() - .items - .values() - .cloned() - .collect::>(); - let return_value = result.last().unwrap(); + let return_value = trace.return_value().unwrap(); let mut felts = Vec::new(); let is_success = match &return_value { From 40f0ed9fa4afcfa6fb8831be7623c21da56a060c Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 23 Apr 2025 15:11:46 -0300 Subject: [PATCH 11/64] replace ids for debug info --- Cargo.lock | 1 + debug_utils/sierra-emu/Cargo.toml | 1 + debug_utils/sierra-emu/src/dump.rs | 3 +- debug_utils/sierra-emu/tests/corelib.rs | 76 +++++++++++++++++++++++-- 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30baea55ba..7eeb7d57dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3302,6 +3302,7 @@ dependencies = [ "cairo-lang-sierra", "cairo-lang-sierra-ap-change", "cairo-lang-sierra-gas", + "cairo-lang-sierra-generator", "cairo-lang-sierra-to-casm", "cairo-lang-starknet", "cairo-lang-starknet-classes", diff --git a/debug_utils/sierra-emu/Cargo.toml b/debug_utils/sierra-emu/Cargo.toml index 75b7c232a8..9f621e368c 100644 --- a/debug_utils/sierra-emu/Cargo.toml +++ b/debug_utils/sierra-emu/Cargo.toml @@ -19,6 +19,7 @@ cairo-lang-sierra-ap-change = "=2.12.0-dev.0" cairo-lang-sierra-gas = "=2.12.0-dev.0" cairo-lang-starknet-classes = "=2.12.0-dev.0" cairo-lang-test-plugin = "=2.12.0-dev.0" +cairo-lang-sierra-generator = "=2.12.0-dev.0" cairo-lang-utils = "=2.12.0-dev.0" clap = { version = "4.5.26", features = ["derive"] } k256 = "0.13.4" diff --git a/debug_utils/sierra-emu/src/dump.rs b/debug_utils/sierra-emu/src/dump.rs index be49a8b21f..e855a89021 100644 --- a/debug_utils/sierra-emu/src/dump.rs +++ b/debug_utils/sierra-emu/src/dump.rs @@ -21,8 +21,7 @@ impl ProgramTrace { } pub fn return_value(&self) -> Option { - self - .states + self.states .last() .unwrap() .items diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 50448c8076..6277df751c 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -5,8 +5,12 @@ use cairo_lang_compiler::{ diagnostics::DiagnosticsReporter, project::{check_compiler_path, setup_project}, }; -use cairo_lang_filesystem::cfg::{Cfg, CfgSet}; +use cairo_lang_filesystem::{ + cfg::{Cfg, CfgSet}, + ids::CrateId, +}; use cairo_lang_runner::{casm_run::format_for_panic, RunResultValue}; +use cairo_lang_sierra_generator::replace_ids::replace_sierra_ids_in_program; use cairo_lang_starknet::starknet_plugin_suite; use cairo_lang_test_plugin::{ compile_test_prepared_db, @@ -51,10 +55,21 @@ fn test_corelib() { let diag_reporter = DiagnosticsReporter::stderr().with_crates(&main_crate_ids); - let build_test_compilation = - compile_test_prepared_db(&db, test_config, test_crate_ids.clone(), diag_reporter).unwrap(); + let filtered_tests = vec![ + "core::test::dict_test::test_array_from_squash_dict", + "core::test::hash_test::test_blake2s", + "core::test::testing_test::test_get_unspent_gas", + ]; + + let compiled = compile_tests( + &db, + test_config, + test_crate_ids, + diag_reporter, + Some(&filtered_tests), + ); - run_tests(build_test_compilation); + run_tests(compiled); } /// Runs the tests and process the results for a summary. @@ -256,3 +271,56 @@ pub fn jitvalue_to_felt(value: &Value) -> Vec { Value::Unit | Value::Uninitialized { .. } => vec![0.into()], } } + +fn compile_tests( + db: &RootDatabase, + test_config: TestsCompilationConfig, + test_crate_ids: Vec, + diag_reporter: DiagnosticsReporter<'_>, + with_filtered_tests: Option<&[&str]>, +) -> TestCompilation { + let mut compiled = + compile_test_prepared_db(db, test_config, test_crate_ids.clone(), diag_reporter).unwrap(); + compiled.sierra_program.program = + replace_sierra_ids_in_program(db, &compiled.sierra_program.program); + + if let Some(compilation_filter) = with_filtered_tests { + let should_skip_test = |name: &str| -> bool { + compilation_filter + .iter() + .any(|filter| name.contains(filter)) + }; + + // Remove matching function definitions. + compiled.sierra_program.program.funcs.retain(|f| { + let name = + f.id.debug_name + .as_ref() + .map(|s| s.as_str()) + .unwrap_or_default(); + + let skipped = should_skip_test(name); + + if skipped { + println!("skipping compilation of: {}", name); + } + + !skipped + }); + + // Ignore matching test cases. + compiled + .metadata + .named_tests + .iter_mut() + .for_each(|(test, case)| { + if should_skip_test(test) { + case.ignored = true + } + }); + + compiled + } else { + compiled + } +} From 144a7ace1ebc6011cb1b4ae17fd5cd8238539ae8 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 23 Apr 2025 15:28:41 -0300 Subject: [PATCH 12/64] add common folder for integration tests --- debug_utils/sierra-emu/tests/common/mod.rs | 89 ++++++++++++++++++ debug_utils/sierra-emu/tests/corelib.rs | 104 ++------------------- 2 files changed, 98 insertions(+), 95 deletions(-) create mode 100644 debug_utils/sierra-emu/tests/common/mod.rs diff --git a/debug_utils/sierra-emu/tests/common/mod.rs b/debug_utils/sierra-emu/tests/common/mod.rs new file mode 100644 index 0000000000..57156308e7 --- /dev/null +++ b/debug_utils/sierra-emu/tests/common/mod.rs @@ -0,0 +1,89 @@ +use sierra_emu::Value; +use starknet_crypto::Felt; + +/// Convert a Value to a felt. +pub fn jitvalue_to_felt(value: &Value) -> Vec { + let mut felts = Vec::new(); + match value { + Value::Array { data, .. } | Value::Struct(data) => { + data.iter().flat_map(jitvalue_to_felt).collect() + } + Value::BoundedInt { value, .. } => vec![value.into()], + Value::Bytes31(bytes) => vec![*bytes], + Value::BuiltinCosts(costs) => vec![ + costs.r#const.into(), + costs.pedersen.into(), + costs.bitwise.into(), + costs.ecop.into(), + costs.poseidon.into(), + costs.add_mod.into(), + costs.mul_mod.into(), + ], + Value::CircuitModulus(value) => vec![value.into()], + Value::Circuit(data) | Value::CircuitOutputs(data) => data.iter().map(Felt::from).collect(), + Value::EcPoint { x, y } => { + vec![*x, *y] + } + Value::EcState { x0, y0, x1, y1 } => { + vec![*x0, *y0, *x1, *y1] + } + Value::Enum { + index, + payload, + debug_name, + .. + } => { + if let Some(debug_name) = debug_name { + if debug_name == "core::bool" { + vec![(*index == 1).into()] + } else { + let mut felts = vec![(*index).into()]; + felts.extend(jitvalue_to_felt(payload)); + felts + } + } else { + // Assume its a regular enum. + let mut felts = vec![(*index).into()]; + felts.extend(jitvalue_to_felt(payload)); + felts + } + } + Value::Felt(felt) => vec![*felt], + Value::FeltDict { data, .. } => { + for (key, value) in data { + felts.push(*key); + let felt = jitvalue_to_felt(value); + felts.extend(felt); + } + + felts + } + Value::FeltDictEntry { + key: data_key, + data, + .. + } => { + felts.push(*data_key); + + for (key, value) in data { + felts.push(*key); + let felt = jitvalue_to_felt(value); + felts.extend(felt); + } + + felts + } + Value::I8(x) => vec![(*x).into()], + Value::I16(x) => vec![(*x).into()], + Value::I32(x) => vec![(*x).into()], + Value::I64(x) => vec![(*x).into()], + Value::I128(x) => vec![(*x).into()], + Value::U8(x) => vec![(*x).into()], + Value::U16(x) => vec![(*x).into()], + Value::U32(x) => vec![(*x).into()], + Value::U64(x) => vec![(*x).into()], + Value::U128(x) => vec![(*x).into()], + Value::U256(x, y) => vec![(*x).into(), (*y).into()], + Value::Unit | Value::Uninitialized { .. } => vec![0.into()], + } +} diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 6277df751c..4d817f60ca 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -17,12 +17,14 @@ use cairo_lang_test_plugin::{ test_config::{PanicExpectation, TestExpectation}, test_plugin_suite, TestCompilation, TestsCompilationConfig, }; +use common::jitvalue_to_felt; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use sierra_emu::{run_program, EntryPoint, ProgramTrace, Value}; -use starknet_crypto::Felt; use tracing::{error, info}; use tracing_test::traced_test; +mod common; + #[test] #[traced_test] fn test_corelib() { @@ -103,13 +105,12 @@ fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { outer_value @ Value::Enum { index, payload, - debug_name: _, + debug_name, .. } => { - //let debug_name = debug_name.as_ref().expect("missing debug name"); - if *index == 0 - // debug_name.starts_with("core::panics::PanicResult::") - // || debug_name.starts_with("Enum match expectation { TestExpectation::Success => { @@ -176,7 +177,7 @@ fn assert_test_expectation( error!("test {}: {}", name, err_msg); success = false; } - info!("test {}", name); + info!("test {} ... OK", name); } } }, @@ -185,93 +186,6 @@ fn assert_test_expectation( success } -/// Convert a Value to a felt. -pub fn jitvalue_to_felt(value: &Value) -> Vec { - let mut felts = Vec::new(); - match value { - Value::Array { data, .. } | Value::Struct(data) => { - data.iter().flat_map(jitvalue_to_felt).collect() - } - Value::BoundedInt { value, .. } => vec![value.into()], - Value::Bytes31(bytes) => vec![*bytes], - Value::BuiltinCosts(costs) => vec![ - costs.r#const.into(), - costs.pedersen.into(), - costs.bitwise.into(), - costs.ecop.into(), - costs.poseidon.into(), - costs.add_mod.into(), - costs.mul_mod.into(), - ], - Value::CircuitModulus(value) => vec![value.into()], - Value::Circuit(data) | Value::CircuitOutputs(data) => data.iter().map(Felt::from).collect(), - Value::EcPoint { x, y } => { - vec![*x, *y] - } - Value::EcState { x0, y0, x1, y1 } => { - vec![*x0, *y0, *x1, *y1] - } - Value::Enum { - index, - payload, - debug_name, - .. - } => { - if let Some(debug_name) = debug_name { - if debug_name == "core::bool" { - vec![(*index == 1).into()] - } else { - let mut felts = vec![(*index).into()]; - felts.extend(jitvalue_to_felt(payload)); - felts - } - } else { - // Assume its a regular enum. - let mut felts = vec![(*index).into()]; - felts.extend(jitvalue_to_felt(payload)); - felts - } - } - Value::Felt(felt) => vec![*felt], - Value::FeltDict { data, .. } => { - for (key, value) in data { - felts.push(*key); - let felt = jitvalue_to_felt(value); - felts.extend(felt); - } - - felts - } - Value::FeltDictEntry { - key: data_key, - data, - .. - } => { - felts.push(*data_key); - - for (key, value) in data { - felts.push(*key); - let felt = jitvalue_to_felt(value); - felts.extend(felt); - } - - felts - } - Value::I8(x) => vec![(*x).into()], - Value::I16(x) => vec![(*x).into()], - Value::I32(x) => vec![(*x).into()], - Value::I64(x) => vec![(*x).into()], - Value::I128(x) => vec![(*x).into()], - Value::U8(x) => vec![(*x).into()], - Value::U16(x) => vec![(*x).into()], - Value::U32(x) => vec![(*x).into()], - Value::U64(x) => vec![(*x).into()], - Value::U128(x) => vec![(*x).into()], - Value::U256(x, y) => vec![(*x).into(), (*y).into()], - Value::Unit | Value::Uninitialized { .. } => vec![0.into()], - } -} - fn compile_tests( db: &RootDatabase, test_config: TestsCompilationConfig, From 60ac2885884e680295a944204dd7b55099900b30 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 23 Apr 2025 16:12:35 -0300 Subject: [PATCH 13/64] fix tests --- debug_utils/sierra-emu/.gitignore | 1 + debug_utils/sierra-emu/Cargo.toml | 4 ---- debug_utils/sierra-emu/tests/corelib.rs | 13 +++++++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/debug_utils/sierra-emu/.gitignore b/debug_utils/sierra-emu/.gitignore index 50ab30c060..56a07f2db4 100644 --- a/debug_utils/sierra-emu/.gitignore +++ b/debug_utils/sierra-emu/.gitignore @@ -1,2 +1,3 @@ *.tar *.json +cairo2/ diff --git a/debug_utils/sierra-emu/Cargo.toml b/debug_utils/sierra-emu/Cargo.toml index 9f621e368c..e7d98cd581 100644 --- a/debug_utils/sierra-emu/Cargo.toml +++ b/debug_utils/sierra-emu/Cargo.toml @@ -50,7 +50,3 @@ cairo-lang-starknet = "=2.12.0-dev.0" # On dev optimize dependencies a bit so it's not as slow. [profile.dev.package."*"] opt-level = 1 - -[[test]] -name = "test_corelib" -path = "tests/corelib.rs" diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 4d817f60ca..59ccf6eefe 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -25,10 +25,10 @@ use tracing_test::traced_test; mod common; -#[test] #[traced_test] +#[test] fn test_corelib() { - let compiler_path = Path::new("../../corelib"); + let compiler_path = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("corelib"); check_compiler_path(false, &compiler_path).expect("Couldn't the corelib in the given path"); @@ -94,7 +94,8 @@ pub fn run_tests(compiled: TestCompilation) { }, ); - assert!(success); + // TODO: assert success, since there missing features (like libfuncs, types, etc) + // this test would fail. So this shouldn't be done until it's ready } fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { @@ -159,7 +160,7 @@ fn assert_test_expectation( RunResultValue::Success(r) => { if let TestExpectation::Panics(_) = expectation { let err_msg = format_for_panic(r.into_iter()); - error!("test {}: {}", name, err_msg); + info!("test {} ... FAILED: {}", name, err_msg); success = false; } info!("test {} ... OK", name); @@ -167,14 +168,14 @@ fn assert_test_expectation( RunResultValue::Panic(e) => match expectation { TestExpectation::Success => { let err_msg = format_for_panic(e.into_iter()); - error!("test {}: {}", name, err_msg); + info!("test {} ... FAILED: {}", name, err_msg); success = false; } TestExpectation::Panics(panic_expect) => { if let PanicExpectation::Exact(expected) = panic_expect { if expected != e { let err_msg = format_for_panic(e.into_iter()); - error!("test {}: {}", name, err_msg); + info!("test {} ... FAILED: {}", name, err_msg); success = false; } info!("test {} ... OK", name); From e18a55d225508e7b35b84f8ca36fe08b098dda38 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 23 Apr 2025 16:54:22 -0300 Subject: [PATCH 14/64] remove todo --- debug_utils/sierra-emu/tests/corelib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 59ccf6eefe..2a7312b214 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -20,7 +20,7 @@ use cairo_lang_test_plugin::{ use common::jitvalue_to_felt; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use sierra_emu::{run_program, EntryPoint, ProgramTrace, Value}; -use tracing::{error, info}; +use tracing::info; use tracing_test::traced_test; mod common; @@ -94,8 +94,7 @@ pub fn run_tests(compiled: TestCompilation) { }, ); - // TODO: assert success, since there missing features (like libfuncs, types, etc) - // this test would fail. So this shouldn't be done until it's ready + assert!(success); } fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { From 21b75aaad44753d5a5154072c7e64483dc0b5888 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 23 Apr 2025 17:22:32 -0300 Subject: [PATCH 15/64] ignore test for now --- debug_utils/sierra-emu/tests/corelib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 2a7312b214..8657ea607b 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -27,6 +27,7 @@ mod common; #[traced_test] #[test] +#[ignore = "sierra-emu is not fully implemented yet"] fn test_corelib() { let compiler_path = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("corelib"); From be2905848624fef663fff91dc31911ec0f5cc041 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 23 Apr 2025 18:11:41 -0300 Subject: [PATCH 16/64] add enum in const::inner --- debug_utils/sierra-emu/src/vm/cast.rs | 7 ++++++- debug_utils/sierra-emu/src/vm/const.rs | 22 ++++++++++++++++++++++ debug_utils/sierra-emu/tests/corelib.rs | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs index fd1f15f6fc..b7de3cd367 100644 --- a/debug_utils/sierra-emu/src/vm/cast.rs +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -100,7 +100,12 @@ pub fn eval_upcast( CoreTypeConcrete::Felt252(_) => Value::Felt(value.into()), CoreTypeConcrete::Sint16(_) => todo!("Sint16"), CoreTypeConcrete::Sint64(_) => todo!("Sint64"), - CoreTypeConcrete::BoundedInt(_) => todo!("BoundedInt"), + CoreTypeConcrete::BoundedInt(info) => { + Value::BoundedInt { + range: info.range.lower.clone()..info.range.upper.clone(), + value, + } + } _ => todo!(), }], ) diff --git a/debug_utils/sierra-emu/src/vm/const.rs b/debug_utils/sierra-emu/src/vm/const.rs index be6c24e785..30fabe6f65 100644 --- a/debug_utils/sierra-emu/src/vm/const.rs +++ b/debug_utils/sierra-emu/src/vm/const.rs @@ -11,6 +11,7 @@ use cairo_lang_sierra::{ program::GenericArg, program_registry::ProgramRegistry, }; +use num_traits::ToPrimitive; use smallvec::smallvec; pub fn eval( @@ -160,6 +161,27 @@ fn inner( Value::Struct(fields) } + CoreTypeConcrete::Enum(_) => match inner_data { + [GenericArg::Value(value_idx), GenericArg::Type(payload_ty)] => { + let payload_type = registry.get_type(payload_ty).unwrap(); + let const_payload_type = match payload_type { + CoreTypeConcrete::Const(inner) => inner, + _ => { + panic!("matched an unexpected CoreTypeConcrete that is not a Const") + } + }; + let payload = inner(registry, payload_ty, &const_payload_type.inner_data); + let index: usize = value_idx.to_usize().unwrap(); + + Value::Enum { + self_ty: type_id.clone(), + index, + payload: Box::new(payload), + debug_name: type_id.debug_name.as_ref().map(|s| s.to_string()), + } + } + _ => panic!("const data mismatch"), + }, _ => todo!("{}", type_id), } } diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 8657ea607b..383ac9ab61 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -27,7 +27,7 @@ mod common; #[traced_test] #[test] -#[ignore = "sierra-emu is not fully implemented yet"] +//#[ignore = "sierra-emu is not fully implemented yet"] fn test_corelib() { let compiler_path = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("corelib"); From c5e535571a26f065b5d44ed73971e6639a4db8a4 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 24 Apr 2025 12:58:30 -0300 Subject: [PATCH 17/64] remove todos and fix some failures --- debug_utils/sierra-emu/src/lib.rs | 1 + debug_utils/sierra-emu/src/utils.rs | 27 ++++++++++++++ debug_utils/sierra-emu/src/vm/bounded_int.rs | 24 +----------- debug_utils/sierra-emu/src/vm/cast.rs | 38 ++++--------------- debug_utils/sierra-emu/src/vm/const.rs | 39 ++++++++++++++------ 5 files changed, 65 insertions(+), 64 deletions(-) create mode 100644 debug_utils/sierra-emu/src/utils.rs diff --git a/debug_utils/sierra-emu/src/lib.rs b/debug_utils/sierra-emu/src/lib.rs index c0579403c6..392fe43a54 100644 --- a/debug_utils/sierra-emu/src/lib.rs +++ b/debug_utils/sierra-emu/src/lib.rs @@ -21,6 +21,7 @@ pub mod starknet; mod test_utils; mod value; mod vm; +mod utils; #[derive(Clone, Debug)] pub enum EntryPoint { diff --git a/debug_utils/sierra-emu/src/utils.rs b/debug_utils/sierra-emu/src/utils.rs new file mode 100644 index 0000000000..58234324aa --- /dev/null +++ b/debug_utils/sierra-emu/src/utils.rs @@ -0,0 +1,27 @@ +use num_bigint::BigInt; + +use crate::Value; + +/// Receives a vector of values, filters any which is non numeric and returns a `Vec` +/// Useful when a binary operation takes generic values (like with bounded ints). +pub fn get_numberic_args_as_bigints(args: Vec) -> Vec { + args.into_iter() + .filter(|v| !matches!(v, Value::Unit)) + .map(|v| match v { + Value::BoundedInt { value, .. } => value, + Value::I8(value) => BigInt::from(value), + Value::I16(value) => BigInt::from(value), + Value::I32(value) => BigInt::from(value), + Value::I64(value) => BigInt::from(value), + Value::I128(value) => BigInt::from(value), + Value::U8(value) => BigInt::from(value), + Value::U16(value) => BigInt::from(value), + Value::U32(value) => BigInt::from(value), + Value::U64(value) => BigInt::from(value), + Value::U128(value) => BigInt::from(value), + Value::Felt(value) => value.to_bigint(), + Value::Bytes31(value) => value.to_bigint(), + value => panic!("{:?}", value), + }) + .collect() +} diff --git a/debug_utils/sierra-emu/src/vm/bounded_int.rs b/debug_utils/sierra-emu/src/vm/bounded_int.rs index 585451e0ee..b1798c0ee4 100644 --- a/debug_utils/sierra-emu/src/vm/bounded_int.rs +++ b/debug_utils/sierra-emu/src/vm/bounded_int.rs @@ -1,5 +1,5 @@ use super::EvalAction; -use crate::Value; +use crate::{utils::get_numberic_args_as_bigints, Value}; use cairo_lang_sierra::{ extensions::{ bounded_int::{ @@ -15,28 +15,6 @@ use cairo_lang_sierra::{ use num_bigint::BigInt; use smallvec::smallvec; -// All binary operations have generic arguments, this function takes their values -// and builds bigints out of them (since Bigints are used to represent bounded ints' values) -fn get_numberic_args_as_bigints(args: Vec) -> Vec { - args.into_iter() - .filter(|v| !matches!(v, Value::Unit)) - .map(|v| match v { - Value::BoundedInt { value, .. } => value, - Value::I8(value) => BigInt::from(value), - Value::I16(value) => BigInt::from(value), - Value::I32(value) => BigInt::from(value), - Value::I64(value) => BigInt::from(value), - Value::I128(value) => BigInt::from(value), - Value::U8(value) => BigInt::from(value), - Value::U16(value) => BigInt::from(value), - Value::U32(value) => BigInt::from(value), - Value::U64(value) => BigInt::from(value), - Value::U128(value) => BigInt::from(value), - _ => panic!("Not a numeric value"), - }) - .collect() -} - pub fn eval( registry: &ProgramRegistry, selector: &BoundedIntConcreteLibfunc, diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs index b7de3cd367..f552b4c422 100644 --- a/debug_utils/sierra-emu/src/vm/cast.rs +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -1,5 +1,5 @@ use super::EvalAction; -use crate::Value; +use crate::{utils::get_numberic_args_as_bigints, Value}; use cairo_lang_sierra::{ extensions::{ casts::{CastConcreteLibfunc, DowncastConcreteLibfunc}, @@ -9,7 +9,6 @@ use cairo_lang_sierra::{ }, program_registry::ProgramRegistry, }; -use num_bigint::BigInt; use smallvec::smallvec; pub fn eval( @@ -28,26 +27,14 @@ pub fn eval_downcast( info: &DowncastConcreteLibfunc, args: Vec, ) -> EvalAction { - let [range_check @ Value::Unit, value]: [Value; 2] = args.try_into().unwrap() else { - panic!() - }; - - let value = match value { - Value::BoundedInt { value, .. } => value, - Value::U128(value) => BigInt::from(value), - Value::U64(value) => BigInt::from(value), - Value::U32(value) => BigInt::from(value), - Value::U16(value) => BigInt::from(value), - Value::U8(value) => BigInt::from(value), - _ => todo!(), - }; + let [value] = get_numberic_args_as_bigints(args).try_into().unwrap(); let range = info.to_range.lower.clone()..info.to_range.upper.clone(); if range.contains(&value) { EvalAction::NormalBranch( 0, smallvec![ - range_check, + Value::Unit, // range_check match registry.get_type(&info.to_ty).unwrap() { CoreTypeConcrete::Sint8(_) => Value::I8(value.try_into().unwrap()), CoreTypeConcrete::Sint128(_) => Value::I128(value.try_into().unwrap()), @@ -56,13 +43,14 @@ pub fn eval_downcast( CoreTypeConcrete::Uint32(_) => Value::U32(value.try_into().unwrap()), CoreTypeConcrete::Uint64(_) => Value::U64(value.try_into().unwrap()), CoreTypeConcrete::Uint128(_) => Value::U128(value.try_into().unwrap()), + CoreTypeConcrete::Felt252(_) => Value::Felt(value.into()), CoreTypeConcrete::BoundedInt(_) => Value::BoundedInt { range, value }, x => todo!("{:?}", x.info()), } ], ) } else { - EvalAction::NormalBranch(1, smallvec![range_check]) + EvalAction::NormalBranch(1, smallvec![Value::Unit]) } } @@ -71,17 +59,7 @@ pub fn eval_upcast( info: &SignatureOnlyConcreteLibfunc, args: Vec, ) -> EvalAction { - let [value] = args.try_into().unwrap(); - - let value = match value { - Value::BoundedInt { value, .. } => value, - Value::U128(value) => BigInt::from(value), - Value::U64(value) => BigInt::from(value), - Value::U32(value) => BigInt::from(value), - Value::U16(value) => BigInt::from(value), - Value::U8(value) => BigInt::from(value), - _ => todo!(), - }; + let [value] = get_numberic_args_as_bigints(args).try_into().unwrap(); EvalAction::NormalBranch( 0, @@ -90,7 +68,9 @@ pub fn eval_upcast( .unwrap() { CoreTypeConcrete::Sint8(_) => Value::I8(value.try_into().unwrap()), + CoreTypeConcrete::Sint16(_) => Value::U16(value.try_into().unwrap()), CoreTypeConcrete::Sint32(_) => Value::I32(value.try_into().unwrap()), + CoreTypeConcrete::Sint64(_) => Value::U64(value.try_into().unwrap()), CoreTypeConcrete::Sint128(_) => Value::I128(value.try_into().unwrap()), CoreTypeConcrete::Uint8(_) => Value::U8(value.try_into().unwrap()), CoreTypeConcrete::Uint16(_) => Value::U16(value.try_into().unwrap()), @@ -98,8 +78,6 @@ pub fn eval_upcast( CoreTypeConcrete::Uint64(_) => Value::U64(value.try_into().unwrap()), CoreTypeConcrete::Uint128(_) => Value::U128(value.try_into().unwrap()), CoreTypeConcrete::Felt252(_) => Value::Felt(value.into()), - CoreTypeConcrete::Sint16(_) => todo!("Sint16"), - CoreTypeConcrete::Sint64(_) => todo!("Sint64"), CoreTypeConcrete::BoundedInt(info) => { Value::BoundedInt { range: info.range.lower.clone()..info.range.upper.clone(), diff --git a/debug_utils/sierra-emu/src/vm/const.rs b/debug_utils/sierra-emu/src/vm/const.rs index 30fabe6f65..d75b8bd3be 100644 --- a/debug_utils/sierra-emu/src/vm/const.rs +++ b/debug_utils/sierra-emu/src/vm/const.rs @@ -5,7 +5,7 @@ use cairo_lang_sierra::{ const_type::{ ConstAsBoxConcreteLibfunc, ConstAsImmediateConcreteLibfunc, ConstConcreteLibfunc, }, - core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, starknet::StarknetTypeConcrete, }, ids::ConcreteTypeId, program::GenericArg, @@ -90,28 +90,32 @@ fn inner( }, _ => unreachable!(), }, + CoreTypeConcrete::Bytes31(_) => match inner_data { + [GenericArg::Value(value)] => Value::Bytes31(value.try_into().unwrap()), + _ => unreachable!(), + }, CoreTypeConcrete::Sint128(_) => match inner_data { - [GenericArg::Value(value)] => Value::I128(value.try_into().unwrap()), + [GenericArg::Value(value)] => Value::I128(value.to_i128().unwrap()), _ => unreachable!(), }, CoreTypeConcrete::Sint64(_) => match inner_data { - [GenericArg::Value(value)] => Value::U64(value.try_into().unwrap()), + [GenericArg::Value(value)] => Value::I64(value.to_i64().unwrap()), _ => unreachable!(), }, CoreTypeConcrete::Sint32(_) => match inner_data { - [GenericArg::Value(value)] => Value::I32(value.try_into().unwrap()), + [GenericArg::Value(value)] => Value::I32(value.to_i32().unwrap()), _ => unreachable!(), }, CoreTypeConcrete::Sint16(_) => match inner_data { - [GenericArg::Value(value)] => Value::I16(value.try_into().unwrap()), + [GenericArg::Value(value)] => Value::I16(value.to_i16().unwrap()), _ => unreachable!(), }, CoreTypeConcrete::Sint8(_) => match inner_data { - [GenericArg::Value(value)] => Value::I8(value.try_into().unwrap()), + [GenericArg::Value(value)] => Value::I8(value.to_i8().unwrap()), _ => unreachable!(), }, CoreTypeConcrete::Uint128(_) => match inner_data { - [GenericArg::Value(value)] => Value::U128(value.try_into().unwrap()), + [GenericArg::Value(value)] => Value::U128(value.to_u128().unwrap()), [GenericArg::Type(type_id)] => match registry.get_type(type_id).unwrap() { CoreTypeConcrete::Const(info) => inner(registry, &info.inner_ty, &info.inner_data), _ => unreachable!(), @@ -119,11 +123,11 @@ fn inner( _ => unreachable!(), }, CoreTypeConcrete::Uint64(_) => match inner_data { - [GenericArg::Value(value)] => Value::U64(value.try_into().unwrap()), + [GenericArg::Value(value)] => Value::U64(value.to_u64().unwrap()), _ => unreachable!(), }, CoreTypeConcrete::Uint32(_) => match inner_data { - [GenericArg::Value(value)] => Value::U32(value.try_into().unwrap()), + [GenericArg::Value(value)] => Value::U32(value.to_u32().unwrap()), [GenericArg::Type(type_id)] => match registry.get_type(type_id).unwrap() { CoreTypeConcrete::Const(info) => inner(registry, &info.inner_ty, &info.inner_data), _ => unreachable!(), @@ -131,11 +135,11 @@ fn inner( _ => unreachable!(), }, CoreTypeConcrete::Uint16(_) => match inner_data { - [GenericArg::Value(value)] => Value::U16(value.try_into().unwrap()), + [GenericArg::Value(value)] => Value::U16(value.to_u16().unwrap()), _ => unreachable!(), }, CoreTypeConcrete::Uint8(_) => match inner_data { - [GenericArg::Value(value)] => Value::U8(value.try_into().unwrap()), + [GenericArg::Value(value)] => Value::U8(value.to_u8().unwrap()), _ => unreachable!(), }, CoreTypeConcrete::Struct(_) => { @@ -182,6 +186,19 @@ fn inner( } _ => panic!("const data mismatch"), }, + CoreTypeConcrete::Const(info) => { + inner(registry, &info.inner_ty, &info.inner_data) + } + CoreTypeConcrete::Starknet(selector) => match selector { + StarknetTypeConcrete::ClassHash(_) + | StarknetTypeConcrete::ContractAddress(_) + | StarknetTypeConcrete::StorageAddress(_) + | StarknetTypeConcrete::StorageBaseAddress(_) => match inner_data { + [GenericArg::Value(value)] => Value::Felt(value.into()), + _ => unreachable!(), + } + _ => todo!(""), + } _ => todo!("{}", type_id), } } From 612b471a9a975513ce4442456270823b4b72955c Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 24 Apr 2025 15:03:13 -0300 Subject: [PATCH 18/64] filter ignored tests --- debug_utils/sierra-emu/src/dump.rs | 3 ++- debug_utils/sierra-emu/tests/corelib.rs | 35 +++++++------------------ 2 files changed, 12 insertions(+), 26 deletions(-) diff --git a/debug_utils/sierra-emu/src/dump.rs b/debug_utils/sierra-emu/src/dump.rs index e855a89021..af40df6bfa 100644 --- a/debug_utils/sierra-emu/src/dump.rs +++ b/debug_utils/sierra-emu/src/dump.rs @@ -20,7 +20,7 @@ impl ProgramTrace { self.states.push(state); } - pub fn return_value(&self) -> Option { + pub fn return_value(&self) -> Value { self.states .last() .unwrap() @@ -30,6 +30,7 @@ impl ProgramTrace { .collect::>() .last() .cloned() + .unwrap_or(Value::Unit) } } diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 8657ea607b..ead01afd6e 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -27,7 +27,7 @@ mod common; #[traced_test] #[test] -#[ignore = "sierra-emu is not fully implemented yet"] +//#[ignore = "sierra-emu is not fully implemented yet"] fn test_corelib() { let compiler_path = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("corelib"); @@ -80,9 +80,12 @@ pub fn run_tests(compiled: TestCompilation) { let program = Arc::new(compiled.sierra_program.program); let success = true; - compiled.metadata.named_tests.into_par_iter().for_each_with( - success, - move |success, (name, test)| { + compiled + .metadata + .named_tests + .into_par_iter() + .filter(|(_, test)| !test.ignored) + .for_each_with(success, |success, (name, test)| { let trace = run_program( program.clone(), EntryPoint::String(name.clone()), @@ -92,14 +95,13 @@ pub fn run_tests(compiled: TestCompilation) { let run_result = trace_to_run_result(trace); *success &= assert_test_expectation(name, test.expectation, run_result); - }, - ); + }); - assert!(success); + assert!(success, "Some tests from the corelib have failed"); } fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { - let return_value = trace.return_value().unwrap(); + let return_value = trace.return_value(); let mut felts = Vec::new(); let is_success = match &return_value { @@ -206,23 +208,6 @@ fn compile_tests( .any(|filter| name.contains(filter)) }; - // Remove matching function definitions. - compiled.sierra_program.program.funcs.retain(|f| { - let name = - f.id.debug_name - .as_ref() - .map(|s| s.as_str()) - .unwrap_or_default(); - - let skipped = should_skip_test(name); - - if skipped { - println!("skipping compilation of: {}", name); - } - - !skipped - }); - // Ignore matching test cases. compiled .metadata From 3b344553d6687d99537b42eb6ea59dfc103ce0d0 Mon Sep 17 00:00:00 2001 From: Franco Giachetta Date: Thu, 24 Apr 2025 15:09:05 -0300 Subject: [PATCH 19/64] Update debug_utils/sierra-emu/tests/common/mod.rs Co-authored-by: Julian Gonzalez Calderon --- debug_utils/sierra-emu/tests/common/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/tests/common/mod.rs b/debug_utils/sierra-emu/tests/common/mod.rs index 57156308e7..6acf54d63d 100644 --- a/debug_utils/sierra-emu/tests/common/mod.rs +++ b/debug_utils/sierra-emu/tests/common/mod.rs @@ -2,7 +2,7 @@ use sierra_emu::Value; use starknet_crypto::Felt; /// Convert a Value to a felt. -pub fn jitvalue_to_felt(value: &Value) -> Vec { +pub fn value_to_felt(value: &Value) -> Vec { let mut felts = Vec::new(); match value { Value::Array { data, .. } | Value::Struct(data) => { From 973a7db3536fddb829c18982871ab29cde27e267 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 24 Apr 2025 15:46:08 -0300 Subject: [PATCH 20/64] run ignored test from the ci --- .github/workflows/ci.yml | 5 ++++- debug_utils/sierra-emu/tests/common/mod.rs | 10 +++++----- debug_utils/sierra-emu/tests/corelib.rs | 12 ++++++------ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb11f392ba..ac61943921 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -341,7 +341,7 @@ jobs: df -h - name: build image run: docker build . - + build-sierra-emu: name: Build sierra-emu runs-on: ubuntu-latest @@ -359,3 +359,6 @@ jobs: - name: Run tests working-directory: ./debug_utils/sierra-emu run: make test + - name: Run corelib's tests + working-directory: ./debug_utils/sierra-emu + run: cargo test -- --ignored || true # ignore result for now. When sierra-emu is fully implemented this should be removed diff --git a/debug_utils/sierra-emu/tests/common/mod.rs b/debug_utils/sierra-emu/tests/common/mod.rs index 6acf54d63d..af8a320e4f 100644 --- a/debug_utils/sierra-emu/tests/common/mod.rs +++ b/debug_utils/sierra-emu/tests/common/mod.rs @@ -6,7 +6,7 @@ pub fn value_to_felt(value: &Value) -> Vec { let mut felts = Vec::new(); match value { Value::Array { data, .. } | Value::Struct(data) => { - data.iter().flat_map(jitvalue_to_felt).collect() + data.iter().flat_map(value_to_felt).collect() } Value::BoundedInt { value, .. } => vec![value.into()], Value::Bytes31(bytes) => vec![*bytes], @@ -38,13 +38,13 @@ pub fn value_to_felt(value: &Value) -> Vec { vec![(*index == 1).into()] } else { let mut felts = vec![(*index).into()]; - felts.extend(jitvalue_to_felt(payload)); + felts.extend(value_to_felt(payload)); felts } } else { // Assume its a regular enum. let mut felts = vec![(*index).into()]; - felts.extend(jitvalue_to_felt(payload)); + felts.extend(value_to_felt(payload)); felts } } @@ -52,7 +52,7 @@ pub fn value_to_felt(value: &Value) -> Vec { Value::FeltDict { data, .. } => { for (key, value) in data { felts.push(*key); - let felt = jitvalue_to_felt(value); + let felt = value_to_felt(value); felts.extend(felt); } @@ -67,7 +67,7 @@ pub fn value_to_felt(value: &Value) -> Vec { for (key, value) in data { felts.push(*key); - let felt = jitvalue_to_felt(value); + let felt = value_to_felt(value); felts.extend(felt); } diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index ead01afd6e..0fefc1c26a 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -17,7 +17,7 @@ use cairo_lang_test_plugin::{ test_config::{PanicExpectation, TestExpectation}, test_plugin_suite, TestCompilation, TestsCompilationConfig, }; -use common::jitvalue_to_felt; +use common::value_to_felt; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use sierra_emu::{run_program, EntryPoint, ProgramTrace, Value}; use tracing::info; @@ -27,7 +27,7 @@ mod common; #[traced_test] #[test] -//#[ignore = "sierra-emu is not fully implemented yet"] +#[ignore = "sierra-emu is not fully implemented yet"] fn test_corelib() { let compiler_path = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("corelib"); @@ -121,24 +121,24 @@ fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { match &**payload { Value::Struct(fields) => { for field in fields { - let felt = jitvalue_to_felt(&field); + let felt = value_to_felt(&field); felts.extend(felt); } } _ => panic!("unsuported return value in cairo-native"), } } else { - felts.extend(jitvalue_to_felt(&*payload)); + felts.extend(value_to_felt(&*payload)); } is_success } else { - felts.extend(jitvalue_to_felt(&outer_value)); + felts.extend(value_to_felt(&outer_value)); true } } x => { - felts.extend(jitvalue_to_felt(&x)); + felts.extend(value_to_felt(&x)); true } }; From 035ff56a80583e62474b5365bcbba1c057131fe4 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 24 Apr 2025 15:48:48 -0300 Subject: [PATCH 21/64] better ignoring --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac61943921..cebf7e9055 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -361,4 +361,5 @@ jobs: run: make test - name: Run corelib's tests working-directory: ./debug_utils/sierra-emu - run: cargo test -- --ignored || true # ignore result for now. When sierra-emu is fully implemented this should be removed + run: cargo test -- --ignored + continue-on-error: true # ignore result for now. When sierra-emu is fully implemented this should be removed From 6acc0c27cf326839d44c55c1a770b79847c7e879 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 24 Apr 2025 15:55:51 -0300 Subject: [PATCH 22/64] ignore test --- debug_utils/sierra-emu/tests/corelib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 87775d3012..0fefc1c26a 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -27,7 +27,7 @@ mod common; #[traced_test] #[test] -//#[ignore = "sierra-emu is not fully implemented yet"] +#[ignore = "sierra-emu is not fully implemented yet"] fn test_corelib() { let compiler_path = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("corelib"); From 1ab64793f1b37fc537139e247bc26537b27c3385 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 24 Apr 2025 16:03:30 -0300 Subject: [PATCH 23/64] fmt --- debug_utils/sierra-emu/src/lib.rs | 2 +- debug_utils/sierra-emu/src/vm/const.rs | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/debug_utils/sierra-emu/src/lib.rs b/debug_utils/sierra-emu/src/lib.rs index 392fe43a54..4ee5689f4b 100644 --- a/debug_utils/sierra-emu/src/lib.rs +++ b/debug_utils/sierra-emu/src/lib.rs @@ -19,9 +19,9 @@ mod dump; mod gas; pub mod starknet; mod test_utils; +mod utils; mod value; mod vm; -mod utils; #[derive(Clone, Debug)] pub enum EntryPoint { diff --git a/debug_utils/sierra-emu/src/vm/const.rs b/debug_utils/sierra-emu/src/vm/const.rs index d75b8bd3be..00f94f4d13 100644 --- a/debug_utils/sierra-emu/src/vm/const.rs +++ b/debug_utils/sierra-emu/src/vm/const.rs @@ -5,7 +5,8 @@ use cairo_lang_sierra::{ const_type::{ ConstAsBoxConcreteLibfunc, ConstAsImmediateConcreteLibfunc, ConstConcreteLibfunc, }, - core::{CoreLibfunc, CoreType, CoreTypeConcrete}, starknet::StarknetTypeConcrete, + core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + starknet::StarknetTypeConcrete, }, ids::ConcreteTypeId, program::GenericArg, @@ -186,19 +187,17 @@ fn inner( } _ => panic!("const data mismatch"), }, - CoreTypeConcrete::Const(info) => { - inner(registry, &info.inner_ty, &info.inner_data) - } + CoreTypeConcrete::Const(info) => inner(registry, &info.inner_ty, &info.inner_data), CoreTypeConcrete::Starknet(selector) => match selector { StarknetTypeConcrete::ClassHash(_) | StarknetTypeConcrete::ContractAddress(_) | StarknetTypeConcrete::StorageAddress(_) | StarknetTypeConcrete::StorageBaseAddress(_) => match inner_data { - [GenericArg::Value(value)] => Value::Felt(value.into()), - _ => unreachable!(), - } + [GenericArg::Value(value)] => Value::Felt(value.into()), + _ => unreachable!(), + }, _ => todo!(""), - } + }, _ => todo!("{}", type_id), } } From b32565509f4e3581753ebba5779d5217f897dae9 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 24 Apr 2025 16:15:31 -0300 Subject: [PATCH 24/64] remove debug_name in enums --- debug_utils/sierra-emu/src/value.rs | 7 +------ debug_utils/sierra-emu/src/vm/bool.rs | 4 ---- debug_utils/sierra-emu/src/vm/enum.rs | 2 -- debug_utils/sierra-emu/src/vm/starknet.rs | 4 ---- debug_utils/sierra-emu/tests/common/mod.rs | 5 ++--- debug_utils/sierra-emu/tests/corelib.rs | 4 ++-- debug_utils/sierra-emu/tests/libfuncs.rs | 4 ---- 7 files changed, 5 insertions(+), 25 deletions(-) diff --git a/debug_utils/sierra-emu/src/value.rs b/debug_utils/sierra-emu/src/value.rs index 30a2d296ab..cfdf6efbe7 100644 --- a/debug_utils/sierra-emu/src/value.rs +++ b/debug_utils/sierra-emu/src/value.rs @@ -8,7 +8,6 @@ use cairo_lang_sierra::{ ids::ConcreteTypeId, program_registry::ProgramRegistry, }; -use educe::Educe; use num_bigint::{BigInt, BigUint}; use serde::Serialize; use starknet_types_core::felt::Felt; @@ -16,8 +15,7 @@ use std::{collections::HashMap, ops::Range}; use crate::{debug::type_to_name, gas::BuiltinCosts}; -#[derive(Clone, Educe, Serialize)] -#[educe(Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] pub enum Value { Array { ty: ConcreteTypeId, @@ -34,8 +32,6 @@ pub enum Value { self_ty: ConcreteTypeId, index: usize, payload: Box, - #[educe(PartialEq(ignore))] - debug_name: Option, }, Felt(Felt), Bytes31(Felt), @@ -95,7 +91,6 @@ impl Value { self_ty: type_id.clone(), index: 0, payload: Box::new(Value::default_for_type(registry, &info.variants[0])), - debug_name: type_id.debug_name.as_ref().map(|n| n.to_string()), }, CoreTypeConcrete::Struct(info) => Value::Struct( info.members diff --git a/debug_utils/sierra-emu/src/vm/bool.rs b/debug_utils/sierra-emu/src/vm/bool.rs index ec22f98ddc..43da483de5 100644 --- a/debug_utils/sierra-emu/src/vm/bool.rs +++ b/debug_utils/sierra-emu/src/vm/bool.rs @@ -53,7 +53,6 @@ pub fn eval_and( self_ty: self_ty.clone(), index: (lhs && rhs) as usize, payload, - debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()) }], ) } @@ -79,7 +78,6 @@ pub fn eval_not( self_ty: self_ty.clone(), index: (lhs == 0) as usize, payload, - debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()) }], ) } @@ -113,7 +111,6 @@ pub fn eval_xor( self_ty: self_ty.clone(), index: (lhs ^ rhs) as usize, payload, - debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()) }], ) } @@ -147,7 +144,6 @@ pub fn eval_or( self_ty: self_ty.clone(), index: (lhs || rhs) as usize, payload, - debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()) }], ) } diff --git a/debug_utils/sierra-emu/src/vm/enum.rs b/debug_utils/sierra-emu/src/vm/enum.rs index dba38e97f6..f5f2421ef2 100644 --- a/debug_utils/sierra-emu/src/vm/enum.rs +++ b/debug_utils/sierra-emu/src/vm/enum.rs @@ -50,7 +50,6 @@ pub fn eval_init( self_ty: self_ty.clone(), index: info.index, payload: Box::new(value), - debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()) }], ) } @@ -69,7 +68,6 @@ pub fn eval_from_bounded_int( self_ty: self_ty.clone(), index: value.try_into().unwrap(), payload: Box::new(Value::Struct(vec![])), - debug_name: self_ty.debug_name.as_ref().map(|n| n.to_string()), }; EvalAction::NormalBranch(0, smallvec![enm]) diff --git a/debug_utils/sierra-emu/src/vm/starknet.rs b/debug_utils/sierra-emu/src/vm/starknet.rs index fc548820e9..e0fcca34e7 100644 --- a/debug_utils/sierra-emu/src/vm/starknet.rs +++ b/debug_utils/sierra-emu/src/vm/starknet.rs @@ -200,13 +200,11 @@ fn eval_secp_r_new( self_ty: enum_ty.clone(), index: 0, payload: Box::new(p.into_value()), - debug_name: enum_ty.debug_name.as_ref().map(|n| n.to_string()), }, None => Value::Enum { self_ty: enum_ty.clone(), index: 1, payload: Box::new(Value::Unit), - debug_name: enum_ty.debug_name.as_ref().map(|n| n.to_string()), }, }; @@ -266,13 +264,11 @@ fn eval_secp_r_get_point_from_x( self_ty: enum_ty.clone(), index: 0, payload: Box::new(p.into_value()), - debug_name: enum_ty.debug_name.as_ref().map(|n| n.to_string()), }, None => Value::Enum { self_ty: enum_ty.clone(), index: 1, payload: Box::new(Value::Unit), - debug_name: enum_ty.debug_name.as_ref().map(|n| n.to_string()), }, }; diff --git a/debug_utils/sierra-emu/tests/common/mod.rs b/debug_utils/sierra-emu/tests/common/mod.rs index af8a320e4f..fa77478ade 100644 --- a/debug_utils/sierra-emu/tests/common/mod.rs +++ b/debug_utils/sierra-emu/tests/common/mod.rs @@ -28,12 +28,11 @@ pub fn value_to_felt(value: &Value) -> Vec { vec![*x0, *y0, *x1, *y1] } Value::Enum { + self_ty, index, payload, - debug_name, - .. } => { - if let Some(debug_name) = debug_name { + if let Some(debug_name) = &self_ty.debug_name { if debug_name == "core::bool" { vec![(*index == 1).into()] } else { diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 0fefc1c26a..4fd6e9d586 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -106,12 +106,12 @@ fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { let is_success = match &return_value { outer_value @ Value::Enum { + self_ty, index, payload, - debug_name, .. } => { - let debug_name = debug_name.as_ref().expect("missing debug name"); + let debug_name = self_ty.debug_name.as_ref().expect("missing debug name"); if debug_name.starts_with("core::panics::PanicResult::") || debug_name.starts_with("Enum Date: Thu, 24 Apr 2025 16:20:09 -0300 Subject: [PATCH 25/64] refactor --- debug_utils/sierra-emu/src/vm/bool.rs | 8 -------- debug_utils/sierra-emu/src/vm/enum.rs | 2 -- debug_utils/sierra-emu/src/vm/int128.rs | 2 -- debug_utils/sierra-emu/src/vm/starknet.rs | 1 - debug_utils/sierra-emu/tests/libfuncs.rs | 6 +++--- 5 files changed, 3 insertions(+), 16 deletions(-) diff --git a/debug_utils/sierra-emu/src/vm/bool.rs b/debug_utils/sierra-emu/src/vm/bool.rs index 43da483de5..a23c839458 100644 --- a/debug_utils/sierra-emu/src/vm/bool.rs +++ b/debug_utils/sierra-emu/src/vm/bool.rs @@ -33,12 +33,10 @@ pub fn eval_and( self_ty, index: lhs, payload, - .. }, Value::Enum { self_ty: _, index: rhs, payload: _, - .. }]: [Value; 2] = args.try_into().unwrap() else { panic!() @@ -66,7 +64,6 @@ pub fn eval_not( self_ty, index: lhs, payload, - .. }]: [Value; 1] = args.try_into().unwrap() else { panic!() @@ -91,12 +88,10 @@ pub fn eval_xor( self_ty, index: lhs, payload, - .. }, Value::Enum { self_ty: _, index: rhs, payload: _, - .. }]: [Value; 2] = args.try_into().unwrap() else { panic!() @@ -124,12 +119,10 @@ pub fn eval_or( self_ty, index: lhs, payload, - .. }, Value::Enum { self_ty: _, index: rhs, payload: _, - .. }]: [Value; 2] = args.try_into().unwrap() else { panic!() @@ -157,7 +150,6 @@ pub fn eval_to_felt252( self_ty: _, index: lhs, payload: _, - .. }]: [Value; 1] = args.try_into().unwrap() else { panic!() diff --git a/debug_utils/sierra-emu/src/vm/enum.rs b/debug_utils/sierra-emu/src/vm/enum.rs index f5f2421ef2..67abf680a4 100644 --- a/debug_utils/sierra-emu/src/vm/enum.rs +++ b/debug_utils/sierra-emu/src/vm/enum.rs @@ -82,7 +82,6 @@ pub fn eval_match( self_ty, index, payload, - .. }]: [Value; 1] = args.try_into().unwrap() else { panic!() @@ -105,7 +104,6 @@ pub fn eval_snapshot_match( self_ty, index, payload, - .. }]: [Value; 1] = args.try_into().unwrap() else { panic!() diff --git a/debug_utils/sierra-emu/src/vm/int128.rs b/debug_utils/sierra-emu/src/vm/int128.rs index ad34e5fb4f..09f6a91e5b 100644 --- a/debug_utils/sierra-emu/src/vm/int128.rs +++ b/debug_utils/sierra-emu/src/vm/int128.rs @@ -153,7 +153,6 @@ mod tests { self_ty: _, index: _, payload, - .. } = result.last().unwrap() else { panic!("No output"); @@ -182,7 +181,6 @@ mod tests { self_ty: _, index: _, payload, - .. } = result.last().unwrap() else { panic!("No output"); diff --git a/debug_utils/sierra-emu/src/vm/starknet.rs b/debug_utils/sierra-emu/src/vm/starknet.rs index e0fcca34e7..4d039ebd9f 100644 --- a/debug_utils/sierra-emu/src/vm/starknet.rs +++ b/debug_utils/sierra-emu/src/vm/starknet.rs @@ -887,7 +887,6 @@ fn eval_deploy( self_ty: _, index: deploy_from_zero, payload: _, - .. }]: [Value; 6] = args.try_into().unwrap() else { panic!() diff --git a/debug_utils/sierra-emu/tests/libfuncs.rs b/debug_utils/sierra-emu/tests/libfuncs.rs index f9ee5fcd9a..e7181885aa 100644 --- a/debug_utils/sierra-emu/tests/libfuncs.rs +++ b/debug_utils/sierra-emu/tests/libfuncs.rs @@ -53,7 +53,7 @@ fn test_u32_overflow() { Value::Enum { self_ty: _, index: 0, - payload: _, + payload: _ } )); @@ -67,7 +67,7 @@ fn test_u32_overflow() { Value::Enum { self_ty: _, index: 1, - payload: _, + payload: _ } )); @@ -81,7 +81,7 @@ fn test_u32_overflow() { Value::Enum { self_ty: _, index: 0, - payload: _, + payload: _ } )); } From 8e8c03fea8eee54af4999a4969205d5b6b6d44d3 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Thu, 24 Apr 2025 16:24:53 -0300 Subject: [PATCH 26/64] remove unused dependency --- Cargo.lock | 1 - debug_utils/sierra-emu/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7eeb7d57dd..3ae3e6e001 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3309,7 +3309,6 @@ dependencies = [ "cairo-lang-test-plugin", "cairo-lang-utils", "clap", - "educe 0.5.11", "k256", "keccak", "num-bigint", diff --git a/debug_utils/sierra-emu/Cargo.toml b/debug_utils/sierra-emu/Cargo.toml index e7d98cd581..c58f1bd41c 100644 --- a/debug_utils/sierra-emu/Cargo.toml +++ b/debug_utils/sierra-emu/Cargo.toml @@ -9,7 +9,6 @@ readme = "README.md" keywords = ["starknet", "cairo", "compiler", "mlir"] [dependencies] -educe = "0.5.11" cairo-lang-compiler = "=2.12.0-dev.0" cairo-lang-filesystem = "=2.12.0-dev.0" cairo-lang-runner = "=2.12.0-dev.0" From 0b5f319a22597c2acc4e08af864c79dc30cc3b9d Mon Sep 17 00:00:00 2001 From: Franco Giachetta Date: Thu, 24 Apr 2025 17:41:47 -0300 Subject: [PATCH 27/64] More expressive naming for the workflow Co-authored-by: Gabriel Bosio <38794644+gabrielbosio@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cebf7e9055..bb28ad03d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -359,7 +359,7 @@ jobs: - name: Run tests working-directory: ./debug_utils/sierra-emu run: make test - - name: Run corelib's tests + - name: Run corelib's tests (WIP) working-directory: ./debug_utils/sierra-emu run: cargo test -- --ignored continue-on-error: true # ignore result for now. When sierra-emu is fully implemented this should be removed From 6164b398c368194d1af48e28e4f4db8bc87a43f0 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Apr 2025 09:47:28 -0300 Subject: [PATCH 28/64] reviews --- debug_utils/sierra-emu/src/dump.rs | 1 - debug_utils/sierra-emu/src/vm/bool.rs | 8 ++++---- debug_utils/sierra-emu/tests/corelib.rs | 20 +++++++++----------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/debug_utils/sierra-emu/src/dump.rs b/debug_utils/sierra-emu/src/dump.rs index af40df6bfa..a7e2b668b5 100644 --- a/debug_utils/sierra-emu/src/dump.rs +++ b/debug_utils/sierra-emu/src/dump.rs @@ -93,7 +93,6 @@ impl ContractExecutionResult { self_ty: _, index, payload, - .. } => { failure_flag = (*index) != 0; diff --git a/debug_utils/sierra-emu/src/vm/bool.rs b/debug_utils/sierra-emu/src/vm/bool.rs index a23c839458..745cc50f0c 100644 --- a/debug_utils/sierra-emu/src/vm/bool.rs +++ b/debug_utils/sierra-emu/src/vm/bool.rs @@ -48,7 +48,7 @@ pub fn eval_and( EvalAction::NormalBranch( 0, smallvec![Value::Enum { - self_ty: self_ty.clone(), + self_ty, index: (lhs && rhs) as usize, payload, }], @@ -72,7 +72,7 @@ pub fn eval_not( EvalAction::NormalBranch( 0, smallvec![Value::Enum { - self_ty: self_ty.clone(), + self_ty, index: (lhs == 0) as usize, payload, }], @@ -103,7 +103,7 @@ pub fn eval_xor( EvalAction::NormalBranch( 0, smallvec![Value::Enum { - self_ty: self_ty.clone(), + self_ty, index: (lhs ^ rhs) as usize, payload, }], @@ -134,7 +134,7 @@ pub fn eval_or( EvalAction::NormalBranch( 0, smallvec![Value::Enum { - self_ty: self_ty.clone(), + self_ty, index: (lhs || rhs) as usize, payload, }], diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 4fd6e9d586..5bceabbd3e 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -18,7 +18,7 @@ use cairo_lang_test_plugin::{ test_plugin_suite, TestCompilation, TestsCompilationConfig, }; use common::value_to_felt; -use rayon::iter::{IntoParallelIterator, ParallelIterator}; +use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; use sierra_emu::{run_program, EntryPoint, ProgramTrace, Value}; use tracing::info; use tracing_test::traced_test; @@ -78,14 +78,13 @@ fn test_corelib() { /// Runs the tests and process the results for a summary. pub fn run_tests(compiled: TestCompilation) { let program = Arc::new(compiled.sierra_program.program); - let success = true; - compiled + let success = compiled .metadata .named_tests - .into_par_iter() + .into_iter() .filter(|(_, test)| !test.ignored) - .for_each_with(success, |success, (name, test)| { + .map(|(name, test)| { let trace = run_program( program.clone(), EntryPoint::String(name.clone()), @@ -94,8 +93,9 @@ pub fn run_tests(compiled: TestCompilation) { ); let run_result = trace_to_run_result(trace); - *success &= assert_test_expectation(name, test.expectation, run_result); - }); + assert_test_expectation(name, test.expectation, run_result) + }) + .all(|r| r); assert!(success, "Some tests from the corelib have failed"); } @@ -218,9 +218,7 @@ fn compile_tests( case.ignored = true } }); - - compiled - } else { - compiled } + + compiled } From 1cd02e99064bc34bc3d017aaaccf5fbd6e1601d6 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Apr 2025 09:49:11 -0300 Subject: [PATCH 29/64] reviews --- debug_utils/sierra-emu/tests/corelib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 5bceabbd3e..3de7bcf719 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -18,7 +18,6 @@ use cairo_lang_test_plugin::{ test_plugin_suite, TestCompilation, TestsCompilationConfig, }; use common::value_to_felt; -use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; use sierra_emu::{run_program, EntryPoint, ProgramTrace, Value}; use tracing::info; use tracing_test::traced_test; @@ -31,7 +30,7 @@ mod common; fn test_corelib() { let compiler_path = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("corelib"); - check_compiler_path(false, &compiler_path).expect("Couldn't the corelib in the given path"); + check_compiler_path(false, &compiler_path).expect("Couldn't find the corelib in the given path"); let db = &mut { let mut b = RootDatabase::builder(); From f1b40d022908f8a4ee0154bc3939729284cdfcc3 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Apr 2025 09:51:22 -0300 Subject: [PATCH 30/64] fmt --- debug_utils/sierra-emu/tests/corelib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 3de7bcf719..4919c79cd9 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -30,7 +30,8 @@ mod common; fn test_corelib() { let compiler_path = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("corelib"); - check_compiler_path(false, &compiler_path).expect("Couldn't find the corelib in the given path"); + check_compiler_path(false, &compiler_path) + .expect("Couldn't find the corelib in the given path"); let db = &mut { let mut b = RootDatabase::builder(); From 48fb545ea0021f6235f669811173037568d7ad55 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Apr 2025 09:53:15 -0300 Subject: [PATCH 31/64] fmt ci --- .github/workflows/ci.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb28ad03d5..ab9cd21de9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -341,7 +341,6 @@ jobs: df -h - name: build image run: docker build . - build-sierra-emu: name: Build sierra-emu runs-on: ubuntu-latest @@ -359,7 +358,7 @@ jobs: - name: Run tests working-directory: ./debug_utils/sierra-emu run: make test - - name: Run corelib's tests (WIP) + - name: Run corelib's tests working-directory: ./debug_utils/sierra-emu run: cargo test -- --ignored continue-on-error: true # ignore result for now. When sierra-emu is fully implemented this should be removed From 7b9c99c2b25430f3da91c68613147b8ac6daa765 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Apr 2025 09:54:12 -0300 Subject: [PATCH 32/64] revert last commit --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab9cd21de9..cebf7e9055 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -341,6 +341,7 @@ jobs: df -h - name: build image run: docker build . + build-sierra-emu: name: Build sierra-emu runs-on: ubuntu-latest From 986dafad675ed8f75e00852d50130fef583e5682 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Apr 2025 09:59:19 -0300 Subject: [PATCH 33/64] fmt --- debug_utils/sierra-emu/src/vm/bool.rs | 8 ++++---- debug_utils/sierra-emu/src/vm/enum.rs | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/debug_utils/sierra-emu/src/vm/bool.rs b/debug_utils/sierra-emu/src/vm/bool.rs index 745cc50f0c..8d632953a7 100644 --- a/debug_utils/sierra-emu/src/vm/bool.rs +++ b/debug_utils/sierra-emu/src/vm/bool.rs @@ -50,7 +50,7 @@ pub fn eval_and( smallvec![Value::Enum { self_ty, index: (lhs && rhs) as usize, - payload, + payload }], ) } @@ -74,7 +74,7 @@ pub fn eval_not( smallvec![Value::Enum { self_ty, index: (lhs == 0) as usize, - payload, + payload }], ) } @@ -105,7 +105,7 @@ pub fn eval_xor( smallvec![Value::Enum { self_ty, index: (lhs ^ rhs) as usize, - payload, + payload }], ) } @@ -136,7 +136,7 @@ pub fn eval_or( smallvec![Value::Enum { self_ty, index: (lhs || rhs) as usize, - payload, + payload }], ) } diff --git a/debug_utils/sierra-emu/src/vm/enum.rs b/debug_utils/sierra-emu/src/vm/enum.rs index 67abf680a4..5fce56b98f 100644 --- a/debug_utils/sierra-emu/src/vm/enum.rs +++ b/debug_utils/sierra-emu/src/vm/enum.rs @@ -62,10 +62,9 @@ pub fn eval_from_bounded_int( let [Value::BoundedInt { range: _, value }]: [Value; 1] = args.try_into().unwrap() else { panic!() }; - let self_ty = &info.branch_signatures()[0].vars[0].ty; let enm = Value::Enum { - self_ty: self_ty.clone(), + self_ty: info.branch_signatures()[0].vars[0].ty.clone(), index: value.try_into().unwrap(), payload: Box::new(Value::Struct(vec![])), }; From f0e90c75762bac9473a77c50047dd74d034c8b28 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Apr 2025 11:06:57 -0300 Subject: [PATCH 34/64] accidentally removed rayon usage --- debug_utils/sierra-emu/tests/corelib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 4919c79cd9..10fca6bcd4 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -18,6 +18,7 @@ use cairo_lang_test_plugin::{ test_plugin_suite, TestCompilation, TestsCompilationConfig, }; use common::value_to_felt; +use rayon::iter::{IntoParallelIterator, ParallelIterator}; use sierra_emu::{run_program, EntryPoint, ProgramTrace, Value}; use tracing::info; use tracing_test::traced_test; @@ -82,7 +83,7 @@ pub fn run_tests(compiled: TestCompilation) { let success = compiled .metadata .named_tests - .into_iter() + .into_par_iter() .filter(|(_, test)| !test.ignored) .map(|(name, test)| { let trace = run_program( From 51ff8eaff9f5b3d0094022a7f9a276df31bba19c Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Apr 2025 12:15:46 -0300 Subject: [PATCH 35/64] avoid map's short-circuiting --- debug_utils/sierra-emu/tests/corelib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 10fca6bcd4..9f7ecc2806 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -96,6 +96,8 @@ pub fn run_tests(compiled: TestCompilation) { assert_test_expectation(name, test.expectation, run_result) }) + .collect::>() // avoid map's short-circuiting + .into_iter() .all(|r| r); assert!(success, "Some tests from the corelib have failed"); From 6083538a7d7427acdc78253e111ba6c59e621167 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Apr 2025 15:31:09 -0300 Subject: [PATCH 36/64] catch panics the avoid loosing the test name --- debug_utils/sierra-emu/tests/corelib.rs | 135 +++++++++++++++++------- 1 file changed, 95 insertions(+), 40 deletions(-) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 9f7ecc2806..30dd7145e8 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -1,4 +1,4 @@ -use std::{path::Path, sync::Arc, u64}; +use std::{panic::PanicHookInfo, path::Path, sync::Arc, u64}; use cairo_lang_compiler::{ db::RootDatabase, @@ -25,6 +25,17 @@ use tracing_test::traced_test; mod common; +enum TestStatus { + Passed, + Failed(String), + Ignored, +} + +struct TestResult { + pub test: String, + pub status: TestStatus, +} + #[traced_test] #[test] #[ignore = "sierra-emu is not fully implemented yet"] @@ -50,8 +61,8 @@ fn test_corelib() { let test_crate_ids = main_crate_ids.clone(); let test_config = TestsCompilationConfig { starknet: false, - add_statements_functions: true, - add_statements_code_locations: true, + add_statements_functions: false, + add_statements_code_locations: false, contract_declarations: None, contract_crate_ids: None, executable_crate_ids: None, @@ -73,34 +84,55 @@ fn test_corelib() { Some(&filtered_tests), ); - run_tests(compiled); + let results = run_tests(compiled); + + display_results(&results); + + assert!(results + .iter() + .all(|t| matches!(t.status, TestStatus::Passed | TestStatus::Ignored)),); } /// Runs the tests and process the results for a summary. -pub fn run_tests(compiled: TestCompilation) { +fn run_tests(compiled: TestCompilation) -> Vec { let program = Arc::new(compiled.sierra_program.program); - let success = compiled + compiled .metadata .named_tests .into_par_iter() - .filter(|(_, test)| !test.ignored) .map(|(name, test)| { - let trace = run_program( - program.clone(), - EntryPoint::String(name.clone()), - vec![], - u64::MAX, - ); - let run_result = trace_to_run_result(trace); - - assert_test_expectation(name, test.expectation, run_result) - }) - .collect::>() // avoid map's short-circuiting - .into_iter() - .all(|r| r); + if test.ignored { + return TestResult { + test: name, + status: TestStatus::Ignored, + }; + } + + // catch any panic during the test run + let res = std::panic::catch_unwind(|| { + run_program( + program.clone(), + EntryPoint::String(name.clone()), + vec![], + u64::MAX, + ) + }); + + let status = match res { + Ok(trace) => { + let run_result = trace_to_run_result(trace); + + assert_test_expectation(test.expectation, run_result) + } + Err(panic) => { + TestStatus::Failed(format!("PANIC: {:?}", panic.downcast_ref::())) + } + }; - assert!(success, "Some tests from the corelib have failed"); + TestResult { test: name, status } + }) + .collect::>() } fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { @@ -154,42 +186,64 @@ fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { } } -fn assert_test_expectation( - name: String, - expectation: TestExpectation, - result: RunResultValue, -) -> bool { - let mut success = true; - +fn assert_test_expectation(expectation: TestExpectation, result: RunResultValue) -> TestStatus { match result { RunResultValue::Success(r) => { if let TestExpectation::Panics(_) = expectation { let err_msg = format_for_panic(r.into_iter()); - info!("test {} ... FAILED: {}", name, err_msg); - success = false; + return TestStatus::Failed(err_msg); } - info!("test {} ... OK", name); + TestStatus::Passed } RunResultValue::Panic(e) => match expectation { TestExpectation::Success => { let err_msg = format_for_panic(e.into_iter()); - info!("test {} ... FAILED: {}", name, err_msg); - success = false; + TestStatus::Failed(err_msg) } - TestExpectation::Panics(panic_expect) => { - if let PanicExpectation::Exact(expected) = panic_expect { + TestExpectation::Panics(panic_expect) => match panic_expect { + PanicExpectation::Exact(expected) => { if expected != e { let err_msg = format_for_panic(e.into_iter()); - info!("test {} ... FAILED: {}", name, err_msg); - success = false; + return TestStatus::Failed(err_msg); } - info!("test {} ... OK", name); + TestStatus::Passed } - } + PanicExpectation::Any => TestStatus::Passed, + }, }, } +} + +fn display_results(results: &[TestResult]) { + let mut passed = 0; + let mut failed = 0; + let mut ignored = 0; - success + for r in results { + match &r.status { + TestStatus::Passed => { + info!("test {} ... OK", r.test); + passed += 1 + } + TestStatus::Failed(err) => { + info!("test {} ... FAILED: {}", r.test, err); + failed += 1 + } + _ => ignored += 1, + } + } + + if failed > 0 { + info!( + "\n\ntest result: FAILED. {} passed; {} failed; {} filtered out;", + passed, failed, ignored + ); + } else { + info!( + "\n\ntest result: OK. {} passed; {} failed; {} filtered out;", + passed, failed, ignored + ); + } } fn compile_tests( @@ -201,6 +255,7 @@ fn compile_tests( ) -> TestCompilation { let mut compiled = compile_test_prepared_db(db, test_config, test_crate_ids.clone(), diag_reporter).unwrap(); + // replace ids to have debug_names compiled.sierra_program.program = replace_sierra_ids_in_program(db, &compiled.sierra_program.program); From 49123094278071648ea71c6d2b5550d48ab54bf0 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Apr 2025 15:35:33 -0300 Subject: [PATCH 37/64] log ignored tests --- debug_utils/sierra-emu/tests/corelib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 30dd7145e8..8dc32ea74b 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -229,7 +229,10 @@ fn display_results(results: &[TestResult]) { info!("test {} ... FAILED: {}", r.test, err); failed += 1 } - _ => ignored += 1, + _ => { + info!("test {} ... IGNORED", r.test); + ignored += 1 + } } } From eb3749b41f985d9eccd0b8d748551d2d26341a70 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Fri, 25 Apr 2025 16:58:34 -0300 Subject: [PATCH 38/64] clippy --- debug_utils/sierra-emu/tests/corelib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 8dc32ea74b..a35e759914 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -1,4 +1,4 @@ -use std::{panic::PanicHookInfo, path::Path, sync::Arc, u64}; +use std::{path::Path, sync::Arc, u64}; use cairo_lang_compiler::{ db::RootDatabase, From 50a4dde270db947576a37f427ca182d8770715d7 Mon Sep 17 00:00:00 2001 From: Franco Giachetta Date: Mon, 28 Apr 2025 09:20:08 -0300 Subject: [PATCH 39/64] Add "(WIP)" to corelib's test job Co-authored-by: Gabriel Bosio <38794644+gabrielbosio@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cebf7e9055..bb28ad03d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -359,7 +359,7 @@ jobs: - name: Run tests working-directory: ./debug_utils/sierra-emu run: make test - - name: Run corelib's tests + - name: Run corelib's tests (WIP) working-directory: ./debug_utils/sierra-emu run: cargo test -- --ignored continue-on-error: true # ignore result for now. When sierra-emu is fully implemented this should be removed From ee6b5f7784912f0172f1584560f0105a4edd0747 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 28 Apr 2025 12:33:23 -0300 Subject: [PATCH 40/64] print progress as the test runs --- .github/workflows/ci.yml | 4 +-- Cargo.lock | 22 ------------ debug_utils/sierra-emu/Cargo.toml | 1 - debug_utils/sierra-emu/Makefile | 3 ++ debug_utils/sierra-emu/tests/corelib.rs | 46 ++++++++++--------------- 5 files changed, 24 insertions(+), 52 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cebf7e9055..bbe7541e0b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -358,8 +358,8 @@ jobs: run: cargo build --all-features --verbose - name: Run tests working-directory: ./debug_utils/sierra-emu - run: make test + run: make test-no-corelib - name: Run corelib's tests working-directory: ./debug_utils/sierra-emu - run: cargo test -- --ignored + run: cargo test test-corelib -- --nocapture continue-on-error: true # ignore result for now. When sierra-emu is fully implemented this should be removed diff --git a/Cargo.lock b/Cargo.lock index 3ae3e6e001..949c17c585 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3328,7 +3328,6 @@ dependencies = [ "thiserror 2.0.12", "tracing", "tracing-subscriber", - "tracing-test", ] [[package]] @@ -3796,27 +3795,6 @@ dependencies = [ "tracing-serde", ] -[[package]] -name = "tracing-test" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "557b891436fe0d5e0e363427fc7f217abf9ccd510d5136549847bdcbcd011d68" -dependencies = [ - "tracing-core", - "tracing-subscriber", - "tracing-test-macro", -] - -[[package]] -name = "tracing-test-macro" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04659ddb06c87d233c566112c1c9c5b9e98256d9af50ec3bc9c8327f873a7568" -dependencies = [ - "quote", - "syn 2.0.100", -] - [[package]] name = "triomphe" version = "0.1.14" diff --git a/debug_utils/sierra-emu/Cargo.toml b/debug_utils/sierra-emu/Cargo.toml index c58f1bd41c..0be96f762f 100644 --- a/debug_utils/sierra-emu/Cargo.toml +++ b/debug_utils/sierra-emu/Cargo.toml @@ -39,7 +39,6 @@ tempfile = "3.14.0" thiserror = "2.0.3" tracing = "0.1.41" tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } -tracing-test = { version = "0.2.5" } rayon = "1.10.0" [dev-dependencies] diff --git a/debug_utils/sierra-emu/Makefile b/debug_utils/sierra-emu/Makefile index 06470973cd..1d6183ea72 100644 --- a/debug_utils/sierra-emu/Makefile +++ b/debug_utils/sierra-emu/Makefile @@ -29,6 +29,9 @@ check: test: needs-cairo2 cargo test --all-features +test-no-corelib: needs-cairo2 + cargo test --all-features -- --skip test_corelib + clean: cargo clean diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index a35e759914..b3433ac50f 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -20,8 +20,6 @@ use cairo_lang_test_plugin::{ use common::value_to_felt; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use sierra_emu::{run_program, EntryPoint, ProgramTrace, Value}; -use tracing::info; -use tracing_test::traced_test; mod common; @@ -31,14 +29,7 @@ enum TestStatus { Ignored, } -struct TestResult { - pub test: String, - pub status: TestStatus, -} - -#[traced_test] #[test] -#[ignore = "sierra-emu is not fully implemented yet"] fn test_corelib() { let compiler_path = Path::new(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).join("corelib"); @@ -90,11 +81,11 @@ fn test_corelib() { assert!(results .iter() - .all(|t| matches!(t.status, TestStatus::Passed | TestStatus::Ignored)),); + .all(|s| matches!(s, TestStatus::Passed | TestStatus::Ignored)),); } /// Runs the tests and process the results for a summary. -fn run_tests(compiled: TestCompilation) -> Vec { +fn run_tests(compiled: TestCompilation) -> Vec { let program = Arc::new(compiled.sierra_program.program); compiled @@ -103,10 +94,8 @@ fn run_tests(compiled: TestCompilation) -> Vec { .into_par_iter() .map(|(name, test)| { if test.ignored { - return TestResult { - test: name, - status: TestStatus::Ignored, - }; + println!("test {} ... Ignored", name); + return TestStatus::Ignored; } // catch any panic during the test run @@ -130,9 +119,15 @@ fn run_tests(compiled: TestCompilation) -> Vec { } }; - TestResult { test: name, status } + match &status { + TestStatus::Passed => println!("test {} ... OK", name), + TestStatus::Failed(err) => println!("test {} ... FAILED: {}", name, err), + TestStatus::Ignored => {} // already handled before + }; + + status }) - .collect::>() + .collect::>() } fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { @@ -214,35 +209,32 @@ fn assert_test_expectation(expectation: TestExpectation, result: RunResultValue) } } -fn display_results(results: &[TestResult]) { +fn display_results(results: &[TestStatus]) { let mut passed = 0; let mut failed = 0; let mut ignored = 0; - for r in results { - match &r.status { + for status in results { + match &status { TestStatus::Passed => { - info!("test {} ... OK", r.test); passed += 1 } - TestStatus::Failed(err) => { - info!("test {} ... FAILED: {}", r.test, err); + TestStatus::Failed(_) => { failed += 1 } - _ => { - info!("test {} ... IGNORED", r.test); + TestStatus::Ignored => { ignored += 1 } } } if failed > 0 { - info!( + println!( "\n\ntest result: FAILED. {} passed; {} failed; {} filtered out;", passed, failed, ignored ); } else { - info!( + println!( "\n\ntest result: OK. {} passed; {} failed; {} filtered out;", passed, failed, ignored ); From 1f3bf94c0bc1626a853c341aeb9a822b0eb4ac1e Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 28 Apr 2025 13:20:13 -0300 Subject: [PATCH 41/64] fmt --- debug_utils/sierra-emu/tests/corelib.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index b3433ac50f..42226e55e6 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -216,15 +216,9 @@ fn display_results(results: &[TestStatus]) { for status in results { match &status { - TestStatus::Passed => { - passed += 1 - } - TestStatus::Failed(_) => { - failed += 1 - } - TestStatus::Ignored => { - ignored += 1 - } + TestStatus::Passed => passed += 1, + TestStatus::Failed(_) => failed += 1, + TestStatus::Ignored => ignored += 1, } } From d1ec743185313963443ba6f6b70ee172610947a2 Mon Sep 17 00:00:00 2001 From: Franco Giachetta Date: Tue, 29 Apr 2025 09:26:00 -0300 Subject: [PATCH 42/64] format makefile Co-authored-by: Julian Gonzalez Calderon --- debug_utils/sierra-emu/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/Makefile b/debug_utils/sierra-emu/Makefile index 71ac675b07..601804eb77 100644 --- a/debug_utils/sierra-emu/Makefile +++ b/debug_utils/sierra-emu/Makefile @@ -13,7 +13,7 @@ endif usage: @echo "Usage:" - @echo " deps: Installs the necesarry dependencies." + @echo " deps: Installs the necesarry dependencies." @echo " build: Builds the cairo-native library and binaries." @echo " check: Checks format and lints." @echo " test: Runs all tests." From d38d14dff2345261bd9ea0a7201f7b6cf96a3d89 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 29 Apr 2025 09:30:38 -0300 Subject: [PATCH 43/64] fix name test in ci --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 07a7e1b26a..abcda79f2a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -361,5 +361,5 @@ jobs: run: make test-no-corelib - name: Run corelib's tests (WIP) working-directory: ./debug_utils/sierra-emu - run: cargo test test-corelib -- --nocapture + run: cargo test test_corelib -- --nocapture continue-on-error: true # ignore result for now. When sierra-emu is fully implemented this should be removed From 7a1fdfc19fdf3504c2023cce5fdc2a4a128b3a62 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 29 Apr 2025 09:36:30 -0300 Subject: [PATCH 44/64] add dependencies removed in merge --- Cargo.lock | 2 -- debug_utils/sierra-emu/Cargo.toml | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c7f82323c..b708ce4c74 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3325,11 +3325,9 @@ dependencies = [ "cairo-lang-sierra", "cairo-lang-sierra-ap-change", "cairo-lang-sierra-gas", - "cairo-lang-sierra-generator", "cairo-lang-sierra-to-casm", "cairo-lang-starknet", "cairo-lang-starknet-classes", - "cairo-lang-test-plugin", "cairo-lang-utils", "clap", "k256", diff --git a/debug_utils/sierra-emu/Cargo.toml b/debug_utils/sierra-emu/Cargo.toml index f95adb2ada..de649e0925 100644 --- a/debug_utils/sierra-emu/Cargo.toml +++ b/debug_utils/sierra-emu/Cargo.toml @@ -14,11 +14,13 @@ cairo-lang-compiler = "=2.12.0-dev.1" cairo-lang-filesystem = "=2.12.0-dev.1" cairo-lang-runner = "=2.12.0-dev.1" cairo-lang-sierra = "=2.12.0-dev.1" +cairo_lang_sierra_generator = "=2.12.0-dev.1" cairo-lang-sierra-to-casm = "=2.12.0-dev.1" cairo-lang-sierra-ap-change = "=2.12.0-dev.1" cairo-lang-sierra-gas = "=2.12.0-dev.1" cairo-lang-starknet-classes = "=2.12.0-dev.1" cairo-lang-utils = "=2.12.0-dev.1" +cairo_lang_test_plugin = "=2.12.0-dev.1" clap = { version = "4.5.26", features = ["derive"] } k256 = "0.13.4" keccak = "0.1.5" From f89b02f4a7ee21192ddc76709717074a374fd56d Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 29 Apr 2025 09:38:35 -0300 Subject: [PATCH 45/64] dependencies --- Cargo.lock | 2 ++ debug_utils/sierra-emu/Cargo.toml | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b708ce4c74..4c7f82323c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3325,9 +3325,11 @@ dependencies = [ "cairo-lang-sierra", "cairo-lang-sierra-ap-change", "cairo-lang-sierra-gas", + "cairo-lang-sierra-generator", "cairo-lang-sierra-to-casm", "cairo-lang-starknet", "cairo-lang-starknet-classes", + "cairo-lang-test-plugin", "cairo-lang-utils", "clap", "k256", diff --git a/debug_utils/sierra-emu/Cargo.toml b/debug_utils/sierra-emu/Cargo.toml index de649e0925..d0f808afe5 100644 --- a/debug_utils/sierra-emu/Cargo.toml +++ b/debug_utils/sierra-emu/Cargo.toml @@ -14,13 +14,13 @@ cairo-lang-compiler = "=2.12.0-dev.1" cairo-lang-filesystem = "=2.12.0-dev.1" cairo-lang-runner = "=2.12.0-dev.1" cairo-lang-sierra = "=2.12.0-dev.1" -cairo_lang_sierra_generator = "=2.12.0-dev.1" +cairo-lang-sierra-generator = "=2.12.0-dev.1" cairo-lang-sierra-to-casm = "=2.12.0-dev.1" cairo-lang-sierra-ap-change = "=2.12.0-dev.1" cairo-lang-sierra-gas = "=2.12.0-dev.1" cairo-lang-starknet-classes = "=2.12.0-dev.1" cairo-lang-utils = "=2.12.0-dev.1" -cairo_lang_test_plugin = "=2.12.0-dev.1" +cairo-lang-test-plugin = "=2.12.0-dev.1" clap = { version = "4.5.26", features = ["derive"] } k256 = "0.13.4" keccak = "0.1.5" From f278fcc516db26124ff63b54f89fd51ea066ab37 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 29 Apr 2025 09:47:27 -0300 Subject: [PATCH 46/64] clippy --- debug_utils/sierra-emu/tests/corelib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 42226e55e6..dc0549efba 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -151,24 +151,24 @@ fn trace_to_run_result(trace: ProgramTrace) -> RunResultValue { match &**payload { Value::Struct(fields) => { for field in fields { - let felt = value_to_felt(&field); + let felt = value_to_felt(field); felts.extend(felt); } } _ => panic!("unsuported return value in cairo-native"), } } else { - felts.extend(value_to_felt(&*payload)); + felts.extend(value_to_felt(payload)); } is_success } else { - felts.extend(value_to_felt(&outer_value)); + felts.extend(value_to_felt(outer_value)); true } } x => { - felts.extend(value_to_felt(&x)); + felts.extend(value_to_felt(x)); true } }; From d248128da11abf8679a6f1147727680241795805 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 29 Apr 2025 09:54:11 -0300 Subject: [PATCH 47/64] remove unused import --- debug_utils/sierra-emu/Cargo.toml | 1 - debug_utils/sierra-emu/tests/corelib.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/debug_utils/sierra-emu/Cargo.toml b/debug_utils/sierra-emu/Cargo.toml index d0f808afe5..71face6bea 100644 --- a/debug_utils/sierra-emu/Cargo.toml +++ b/debug_utils/sierra-emu/Cargo.toml @@ -9,7 +9,6 @@ readme = "README.md" keywords = ["starknet", "cairo", "compiler", "mlir"] [dependencies] - cairo-lang-compiler = "=2.12.0-dev.1" cairo-lang-filesystem = "=2.12.0-dev.1" cairo-lang-runner = "=2.12.0-dev.1" diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index dc0549efba..9119bf9abe 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -1,4 +1,4 @@ -use std::{path::Path, sync::Arc, u64}; +use std::{path::Path, sync::Arc}; use cairo_lang_compiler::{ db::RootDatabase, From 72761ce3c148078529951df434714ca329acbd82 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 29 Apr 2025 09:56:24 -0300 Subject: [PATCH 48/64] better indent in makefile --- debug_utils/sierra-emu/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/Makefile b/debug_utils/sierra-emu/Makefile index 44b339f183..436fbeeb98 100644 --- a/debug_utils/sierra-emu/Makefile +++ b/debug_utils/sierra-emu/Makefile @@ -13,7 +13,7 @@ endif usage: @echo "Usage:" - @echo " deps: Installs the necesarry dependencies." + @echo " deps: Installs the necesarry dependencies." @echo " build: Builds the cairo-native library and binaries." @echo " check: Checks format and lints." @echo " test: Runs all tests." From 1e43ad3bdc3b1acbc931589956124f289c1460f2 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 29 Apr 2025 10:12:16 -0300 Subject: [PATCH 49/64] uppercase tests summary comment for better sight --- debug_utils/sierra-emu/tests/corelib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debug_utils/sierra-emu/tests/corelib.rs b/debug_utils/sierra-emu/tests/corelib.rs index 9119bf9abe..50bd5d5ea8 100644 --- a/debug_utils/sierra-emu/tests/corelib.rs +++ b/debug_utils/sierra-emu/tests/corelib.rs @@ -224,12 +224,12 @@ fn display_results(results: &[TestStatus]) { if failed > 0 { println!( - "\n\ntest result: FAILED. {} passed; {} failed; {} filtered out;", + "\n\ntest result: FAILED. {} PASSED; {} FAILED; {} FILTERED OUT;", passed, failed, ignored ); } else { println!( - "\n\ntest result: OK. {} passed; {} failed; {} filtered out;", + "\n\ntest result: OK. {} PASSED; {} FAILED; {} FILTERED OUT;", passed, failed, ignored ); } From 83416f1131be53b135aca407c1a66e7923c5133d Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 30 Apr 2025 10:52:57 -0300 Subject: [PATCH 50/64] fix clippy --- Cargo.lock | 113 +++++++++++++------------ debug_utils/sierra-emu/src/vm/const.rs | 1 - 2 files changed, 60 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4c7f82323c..512b714441 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,7 +108,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -189,7 +189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -215,7 +215,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -287,7 +287,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -385,7 +385,7 @@ dependencies = [ "regex", "rustc-hash 2.1.1", "shlex", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -675,7 +675,7 @@ checksum = "b1e4872352761cf6d7f47eeb1626e3b1d84a514017fb4251173148d8c04f36d5" dependencies = [ "cairo-lang-debug", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1020,7 +1020,7 @@ dependencies = [ [[package]] name = "cairo-native" -version = "0.5.0-rc.3" +version = "0.5.0-rc.4" dependencies = [ "anyhow", "aquamarine", @@ -1177,9 +1177,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.19" +version = "1.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362" +checksum = "04da6a0d40b948dfc4fa8f5bbf402b0fc1a64a28dbf7d12ffd683550f2c1b63a" dependencies = [ "shlex", ] @@ -1274,7 +1274,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1528,9 +1528,9 @@ dependencies = [ [[package]] name = "deunicode" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc55fe0d1f6c107595572ec8b107c0999bb1a2e0b75e37429a4fb0d6474a0e7d" +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" [[package]] name = "diff" @@ -1588,7 +1588,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1600,7 +1600,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1667,7 +1667,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1760,7 +1760,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1808,7 +1808,7 @@ checksum = "43eaff6bbc0b3a878361aced5ec6a2818ee7c541c5b33b5880dfa9a86c23e9e7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -1994,7 +1994,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2330,7 +2330,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.100", + "syn 2.0.101", "tblgen", "unindent", ] @@ -2561,7 +2561,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -2704,7 +2704,7 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" dependencies = [ - "zerocopy 0.8.24", + "zerocopy 0.8.25", ] [[package]] @@ -2740,7 +2740,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3033,7 +3033,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.100", + "syn 2.0.101", "unicode-ident", ] @@ -3063,7 +3063,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3195,7 +3195,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3244,7 +3244,7 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3255,7 +3255,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3532,9 +3532,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -3600,7 +3600,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3611,7 +3611,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "test-case-core", ] @@ -3641,7 +3641,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3652,7 +3652,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3712,9 +3712,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.8.20" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", @@ -3724,26 +3724,33 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap 2.9.0", "serde", "serde_spanned", "toml_datetime", + "toml_write", "winnow", ] +[[package]] +name = "toml_write" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + [[package]] name = "tracing" version = "0.1.41" @@ -3763,7 +3770,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -3867,7 +3874,7 @@ checksum = "35f5380909ffc31b4de4f4bdf96b877175a016aa2ca98cee39fcfd8c4d53d952" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4022,7 +4029,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -4044,7 +4051,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -4193,9 +4200,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10" +checksum = "6cb8234a863ea0e8cd7284fcdd4f145233eb00fee02bbdd9861aec44e6477bc5" dependencies = [ "memchr", ] @@ -4250,11 +4257,11 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ - "zerocopy-derive 0.8.24", + "zerocopy-derive 0.8.25", ] [[package]] @@ -4265,18 +4272,18 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] name = "zerocopy-derive" -version = "0.8.24" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] @@ -4296,7 +4303,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.100", + "syn 2.0.101", ] [[package]] diff --git a/debug_utils/sierra-emu/src/vm/const.rs b/debug_utils/sierra-emu/src/vm/const.rs index 00f94f4d13..07064f0b13 100644 --- a/debug_utils/sierra-emu/src/vm/const.rs +++ b/debug_utils/sierra-emu/src/vm/const.rs @@ -182,7 +182,6 @@ fn inner( self_ty: type_id.clone(), index, payload: Box::new(payload), - debug_name: type_id.debug_name.as_ref().map(|s| s.to_string()), } } _ => panic!("const data mismatch"), From 6e02e1b235476eacce87c7421d41cf03bc12cf52 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 30 Apr 2025 15:03:15 -0300 Subject: [PATCH 51/64] clippy --- debug_utils/sierra-emu/src/vm/const.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/src/vm/const.rs b/debug_utils/sierra-emu/src/vm/const.rs index 07064f0b13..9b862b2ba6 100644 --- a/debug_utils/sierra-emu/src/vm/const.rs +++ b/debug_utils/sierra-emu/src/vm/const.rs @@ -92,7 +92,7 @@ fn inner( _ => unreachable!(), }, CoreTypeConcrete::Bytes31(_) => match inner_data { - [GenericArg::Value(value)] => Value::Bytes31(value.try_into().unwrap()), + [GenericArg::Value(value)] => Value::Bytes31(value.into()), _ => unreachable!(), }, CoreTypeConcrete::Sint128(_) => match inner_data { From 0d2a76023049ddf4b4758fd44b4e606a7ab73b63 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 30 Apr 2025 16:06:16 -0300 Subject: [PATCH 52/64] remove unnecesary pubs --- debug_utils/sierra-emu/src/vm/cast.rs | 4 ++-- debug_utils/sierra-emu/src/vm/const.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs index f552b4c422..d04bc8fff6 100644 --- a/debug_utils/sierra-emu/src/vm/cast.rs +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -22,7 +22,7 @@ pub fn eval( } } -pub fn eval_downcast( +fn eval_downcast( registry: &ProgramRegistry, info: &DowncastConcreteLibfunc, args: Vec, @@ -54,7 +54,7 @@ pub fn eval_downcast( } } -pub fn eval_upcast( +fn eval_upcast( registry: &ProgramRegistry, info: &SignatureOnlyConcreteLibfunc, args: Vec, diff --git a/debug_utils/sierra-emu/src/vm/const.rs b/debug_utils/sierra-emu/src/vm/const.rs index 9b862b2ba6..17aac2b970 100644 --- a/debug_utils/sierra-emu/src/vm/const.rs +++ b/debug_utils/sierra-emu/src/vm/const.rs @@ -26,7 +26,7 @@ pub fn eval( } } -pub fn eval_as_immediate( +fn eval_as_immediate( registry: &ProgramRegistry, info: &ConstAsImmediateConcreteLibfunc, args: Vec, @@ -43,7 +43,7 @@ pub fn eval_as_immediate( ) } -pub fn eval_as_box( +fn eval_as_box( registry: &ProgramRegistry, info: &ConstAsBoxConcreteLibfunc, args: Vec, From d9d38283ab39d5b90a2f5ac2a9f8d376d6c0443f Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 30 Apr 2025 18:00:36 -0300 Subject: [PATCH 53/64] better docs --- debug_utils/sierra-emu/src/utils.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/debug_utils/sierra-emu/src/utils.rs b/debug_utils/sierra-emu/src/utils.rs index 58234324aa..f3ef5e16d8 100644 --- a/debug_utils/sierra-emu/src/utils.rs +++ b/debug_utils/sierra-emu/src/utils.rs @@ -2,8 +2,8 @@ use num_bigint::BigInt; use crate::Value; -/// Receives a vector of values, filters any which is non numeric and returns a `Vec` -/// Useful when a binary operation takes generic values (like with bounded ints). +/// Receives a vector of values, filters builtins and returns a `Vec` +/// Useful, for example, in binary operations which take generic values (like with bounded ints). pub fn get_numberic_args_as_bigints(args: Vec) -> Vec { args.into_iter() .filter(|v| !matches!(v, Value::Unit)) From b8de7bec311744c4a2c8d76e676dd9c1dbfdc9cd Mon Sep 17 00:00:00 2001 From: Franco Giachetta Date: Mon, 5 May 2025 09:57:27 -0300 Subject: [PATCH 54/64] better panic message in get_numberic_args_as_bigints() Co-authored-by: Julian Gonzalez Calderon --- debug_utils/sierra-emu/src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/src/utils.rs b/debug_utils/sierra-emu/src/utils.rs index f3ef5e16d8..83114e0bdd 100644 --- a/debug_utils/sierra-emu/src/utils.rs +++ b/debug_utils/sierra-emu/src/utils.rs @@ -21,7 +21,7 @@ pub fn get_numberic_args_as_bigints(args: Vec) -> Vec { Value::U128(value) => BigInt::from(value), Value::Felt(value) => value.to_bigint(), Value::Bytes31(value) => value.to_bigint(), - value => panic!("{:?}", value), + value => panic!("expected numeric value: {:?}", value), }) .collect() } From 38a25b81af23acfd0e0ef881743392e368a61d14 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 5 May 2025 16:22:16 -0300 Subject: [PATCH 55/64] reviews --- debug_utils/sierra-emu/src/utils.rs | 33 +++++++++++++- debug_utils/sierra-emu/src/vm/cast.rs | 63 ++++++++++----------------- 2 files changed, 53 insertions(+), 43 deletions(-) diff --git a/debug_utils/sierra-emu/src/utils.rs b/debug_utils/sierra-emu/src/utils.rs index f3ef5e16d8..b040e8f642 100644 --- a/debug_utils/sierra-emu/src/utils.rs +++ b/debug_utils/sierra-emu/src/utils.rs @@ -1,9 +1,14 @@ +use cairo_lang_sierra::{ + extensions::core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + program_registry::ProgramRegistry, +}; use num_bigint::BigInt; +use num_traits::ToPrimitive; use crate::Value; -/// Receives a vector of values, filters builtins and returns a `Vec` -/// Useful, for example, in binary operations which take generic values (like with bounded ints). +/// Receives a vector of values, filters any which is non numeric and returns a `Vec` +/// Useful when a binary operation takes generic values (like with bounded ints). pub fn get_numberic_args_as_bigints(args: Vec) -> Vec { args.into_iter() .filter(|v| !matches!(v, Value::Unit)) @@ -25,3 +30,27 @@ pub fn get_numberic_args_as_bigints(args: Vec) -> Vec { }) .collect() } + +pub fn get_value_from_integer( + registry: &ProgramRegistry, + ty: &CoreTypeConcrete, + value: BigInt, +) -> Value { + match ty { + CoreTypeConcrete::NonZero(info) => { + let ty = registry.get_type(&info.ty).unwrap(); + get_value_from_integer(registry, ty, value) + } + CoreTypeConcrete::Sint8(_) => Value::I8(value.to_i8().unwrap()), + CoreTypeConcrete::Sint16(_) => Value::I16(value.to_i16().unwrap()), + CoreTypeConcrete::Sint32(_) => Value::I32(value.to_i32().unwrap()), + CoreTypeConcrete::Sint64(_) => Value::I64(value.to_i64().unwrap()), + CoreTypeConcrete::Sint128(_) => Value::I128(value.to_i128().unwrap()), + CoreTypeConcrete::Uint8(_) => Value::U8(value.to_u8().unwrap()), + CoreTypeConcrete::Uint16(_) => Value::U16(value.to_u16().unwrap()), + CoreTypeConcrete::Uint32(_) => Value::U32(value.to_u32().unwrap()), + CoreTypeConcrete::Uint64(_) => Value::U64(value.to_u64().unwrap()), + CoreTypeConcrete::Uint128(_) => Value::U128(value.to_u128().unwrap()), + _ => panic!("Found a non-numeric type"), + } +} diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs index d04bc8fff6..90b273de41 100644 --- a/debug_utils/sierra-emu/src/vm/cast.rs +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -1,11 +1,14 @@ use super::EvalAction; -use crate::{utils::get_numberic_args_as_bigints, Value}; +use crate::{ + utils::{get_numberic_args_as_bigints, get_value_from_integer}, + Value, +}; use cairo_lang_sierra::{ extensions::{ casts::{CastConcreteLibfunc, DowncastConcreteLibfunc}, - core::{CoreLibfunc, CoreType, CoreTypeConcrete}, + core::{CoreLibfunc, CoreType}, lib_func::SignatureOnlyConcreteLibfunc, - ConcreteType, + ConcreteLibfunc, }, program_registry::ProgramRegistry, }; @@ -27,26 +30,21 @@ fn eval_downcast( info: &DowncastConcreteLibfunc, args: Vec, ) -> EvalAction { - let [value] = get_numberic_args_as_bigints(args).try_into().unwrap(); + let range_check @ Value::Unit: Value = args[0].clone() else { + panic!() + }; + let [value] = get_numberic_args_as_bigints(args[1..].to_vec()) + .try_into() + .unwrap(); + let int_ty = registry.get_type(&info.to_ty).unwrap(); let range = info.to_range.lower.clone()..info.to_range.upper.clone(); if range.contains(&value) { EvalAction::NormalBranch( 0, smallvec![ - Value::Unit, // range_check - match registry.get_type(&info.to_ty).unwrap() { - CoreTypeConcrete::Sint8(_) => Value::I8(value.try_into().unwrap()), - CoreTypeConcrete::Sint128(_) => Value::I128(value.try_into().unwrap()), - CoreTypeConcrete::Uint8(_) => Value::U8(value.try_into().unwrap()), - CoreTypeConcrete::Uint16(_) => Value::U16(value.try_into().unwrap()), - CoreTypeConcrete::Uint32(_) => Value::U32(value.try_into().unwrap()), - CoreTypeConcrete::Uint64(_) => Value::U64(value.try_into().unwrap()), - CoreTypeConcrete::Uint128(_) => Value::U128(value.try_into().unwrap()), - CoreTypeConcrete::Felt252(_) => Value::Felt(value.into()), - CoreTypeConcrete::BoundedInt(_) => Value::BoundedInt { range, value }, - x => todo!("{:?}", x.info()), - } + range_check, // range_check + get_value_from_integer(registry, int_ty, value) ], ) } else { @@ -59,32 +57,15 @@ fn eval_upcast( info: &SignatureOnlyConcreteLibfunc, args: Vec, ) -> EvalAction { - let [value] = get_numberic_args_as_bigints(args).try_into().unwrap(); + let [value] = get_numberic_args_as_bigints(args[1..].to_vec()) + .try_into() + .unwrap(); + let int_ty = registry + .get_type(&info.branch_signatures()[0].vars[0].ty) + .unwrap(); EvalAction::NormalBranch( 0, - smallvec![match registry - .get_type(&info.signature.branch_signatures[0].vars[0].ty) - .unwrap() - { - CoreTypeConcrete::Sint8(_) => Value::I8(value.try_into().unwrap()), - CoreTypeConcrete::Sint16(_) => Value::U16(value.try_into().unwrap()), - CoreTypeConcrete::Sint32(_) => Value::I32(value.try_into().unwrap()), - CoreTypeConcrete::Sint64(_) => Value::U64(value.try_into().unwrap()), - CoreTypeConcrete::Sint128(_) => Value::I128(value.try_into().unwrap()), - CoreTypeConcrete::Uint8(_) => Value::U8(value.try_into().unwrap()), - CoreTypeConcrete::Uint16(_) => Value::U16(value.try_into().unwrap()), - CoreTypeConcrete::Uint32(_) => Value::U32(value.try_into().unwrap()), - CoreTypeConcrete::Uint64(_) => Value::U64(value.try_into().unwrap()), - CoreTypeConcrete::Uint128(_) => Value::U128(value.try_into().unwrap()), - CoreTypeConcrete::Felt252(_) => Value::Felt(value.into()), - CoreTypeConcrete::BoundedInt(info) => { - Value::BoundedInt { - range: info.range.lower.clone()..info.range.upper.clone(), - value, - } - } - _ => todo!(), - }], + smallvec![get_value_from_integer(registry, int_ty, value)], ) } From c148d2929c47e6d5d47c0d309204c2692ecd645a Mon Sep 17 00:00:00 2001 From: Franco Giachetta Date: Mon, 5 May 2025 17:24:29 -0300 Subject: [PATCH 56/64] remove unnecessary comment Co-authored-by: Julian Gonzalez Calderon --- debug_utils/sierra-emu/src/vm/cast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs index 90b273de41..808f7dd416 100644 --- a/debug_utils/sierra-emu/src/vm/cast.rs +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -43,7 +43,7 @@ fn eval_downcast( EvalAction::NormalBranch( 0, smallvec![ - range_check, // range_check + range_check, get_value_from_integer(registry, int_ty, value) ], ) From fc4e8817df43407388d627fec78eb203b817890b Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 5 May 2025 17:36:11 -0300 Subject: [PATCH 57/64] use slices in get_numeric_args_as_bigints --- debug_utils/sierra-emu/src/utils.rs | 25 ++++++++++---------- debug_utils/sierra-emu/src/vm/bounded_int.rs | 8 +++---- debug_utils/sierra-emu/src/vm/cast.rs | 4 ++-- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/debug_utils/sierra-emu/src/utils.rs b/debug_utils/sierra-emu/src/utils.rs index bc2d4c3a8d..3ab1ea41db 100644 --- a/debug_utils/sierra-emu/src/utils.rs +++ b/debug_utils/sierra-emu/src/utils.rs @@ -9,21 +9,20 @@ use crate::Value; /// Receives a vector of values, filters any which is non numeric and returns a `Vec` /// Useful when a binary operation takes generic values (like with bounded ints). -pub fn get_numberic_args_as_bigints(args: Vec) -> Vec { +pub fn get_numberic_args_as_bigints(args: &[Value]) -> Vec { args.into_iter() - .filter(|v| !matches!(v, Value::Unit)) .map(|v| match v { - Value::BoundedInt { value, .. } => value, - Value::I8(value) => BigInt::from(value), - Value::I16(value) => BigInt::from(value), - Value::I32(value) => BigInt::from(value), - Value::I64(value) => BigInt::from(value), - Value::I128(value) => BigInt::from(value), - Value::U8(value) => BigInt::from(value), - Value::U16(value) => BigInt::from(value), - Value::U32(value) => BigInt::from(value), - Value::U64(value) => BigInt::from(value), - Value::U128(value) => BigInt::from(value), + Value::BoundedInt { value, .. } => value.to_owned(), + Value::I8(value) => BigInt::from(*value), + Value::I16(value) => BigInt::from(*value), + Value::I32(value) => BigInt::from(*value), + Value::I64(value) => BigInt::from(*value), + Value::I128(value) => BigInt::from(*value), + Value::U8(value) => BigInt::from(*value), + Value::U16(value) => BigInt::from(*value), + Value::U32(value) => BigInt::from(*value), + Value::U64(value) => BigInt::from(*value), + Value::U128(value) => BigInt::from(*value), Value::Felt(value) => value.to_bigint(), Value::Bytes31(value) => value.to_bigint(), value => panic!("expected numeric value: {:?}", value), diff --git a/debug_utils/sierra-emu/src/vm/bounded_int.rs b/debug_utils/sierra-emu/src/vm/bounded_int.rs index d576ca17ea..24c77a42c7 100644 --- a/debug_utils/sierra-emu/src/vm/bounded_int.rs +++ b/debug_utils/sierra-emu/src/vm/bounded_int.rs @@ -39,7 +39,7 @@ pub fn eval_add( info: &SignatureOnlyConcreteLibfunc, args: Vec, ) -> EvalAction { - let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(args).try_into().unwrap(); + let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(&args).try_into().unwrap(); let range = match registry .get_type(&info.signature.branch_signatures[0].vars[0].ty) @@ -68,7 +68,7 @@ pub fn eval_sub( info: &SignatureOnlyConcreteLibfunc, args: Vec, ) -> EvalAction { - let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(args).try_into().unwrap(); + let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(&args).try_into().unwrap(); let range = match registry .get_type(&info.signature.branch_signatures[0].vars[0].ty) @@ -97,7 +97,7 @@ pub fn eval_mul( info: &SignatureOnlyConcreteLibfunc, args: Vec, ) -> EvalAction { - let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(args).try_into().unwrap(); + let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(&args).try_into().unwrap(); let range = match registry .get_type(&info.signature.branch_signatures[0].vars[0].ty) @@ -126,7 +126,7 @@ pub fn eval_div_rem( info: &BoundedIntDivRemConcreteLibfunc, args: Vec, ) -> EvalAction { - let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(args).try_into().unwrap(); + let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(&args).try_into().unwrap(); let quo = &lhs / &rhs; let rem = lhs % rhs; diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs index 90b273de41..4bd0b7ce96 100644 --- a/debug_utils/sierra-emu/src/vm/cast.rs +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -33,7 +33,7 @@ fn eval_downcast( let range_check @ Value::Unit: Value = args[0].clone() else { panic!() }; - let [value] = get_numberic_args_as_bigints(args[1..].to_vec()) + let [value] = get_numberic_args_as_bigints(&args[1..].to_vec()) .try_into() .unwrap(); @@ -57,7 +57,7 @@ fn eval_upcast( info: &SignatureOnlyConcreteLibfunc, args: Vec, ) -> EvalAction { - let [value] = get_numberic_args_as_bigints(args[1..].to_vec()) + let [value] = get_numberic_args_as_bigints(&args) .try_into() .unwrap(); let int_ty = registry From 5c254486cc5f882aaf9d98721c8d4cefd6fd7659 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 5 May 2025 17:37:36 -0300 Subject: [PATCH 58/64] fmt --- debug_utils/sierra-emu/src/vm/cast.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs index d44a52d37e..d257ae353a 100644 --- a/debug_utils/sierra-emu/src/vm/cast.rs +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -42,10 +42,7 @@ fn eval_downcast( if range.contains(&value) { EvalAction::NormalBranch( 0, - smallvec![ - range_check, - get_value_from_integer(registry, int_ty, value) - ], + smallvec![range_check, get_value_from_integer(registry, int_ty, value)], ) } else { EvalAction::NormalBranch(1, smallvec![Value::Unit]) @@ -57,9 +54,7 @@ fn eval_upcast( info: &SignatureOnlyConcreteLibfunc, args: Vec, ) -> EvalAction { - let [value] = get_numberic_args_as_bigints(&args) - .try_into() - .unwrap(); + let [value] = get_numberic_args_as_bigints(&args).try_into().unwrap(); let int_ty = registry .get_type(&info.branch_signatures()[0].vars[0].ty) .unwrap(); From 7f0393dce2e945ddb2f3772fdc676166517bc519 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 5 May 2025 17:46:54 -0300 Subject: [PATCH 59/64] remove unnecesary to_vec --- debug_utils/sierra-emu/src/vm/cast.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs index d257ae353a..42da096800 100644 --- a/debug_utils/sierra-emu/src/vm/cast.rs +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -33,7 +33,7 @@ fn eval_downcast( let range_check @ Value::Unit: Value = args[0].clone() else { panic!() }; - let [value] = get_numberic_args_as_bigints(&args[1..].to_vec()) + let [value] = get_numberic_args_as_bigints(&args[1..]) .try_into() .unwrap(); From 82b8da3960652821ed21c2f7ede86b5a03752f5e Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 5 May 2025 17:51:15 -0300 Subject: [PATCH 60/64] fmt --- debug_utils/sierra-emu/src/vm/cast.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs index 42da096800..ea0b8e21ee 100644 --- a/debug_utils/sierra-emu/src/vm/cast.rs +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -33,9 +33,7 @@ fn eval_downcast( let range_check @ Value::Unit: Value = args[0].clone() else { panic!() }; - let [value] = get_numberic_args_as_bigints(&args[1..]) - .try_into() - .unwrap(); + let [value] = get_numberic_args_as_bigints(&args[1..]).try_into().unwrap(); let int_ty = registry.get_type(&info.to_ty).unwrap(); let range = info.to_range.lower.clone()..info.to_range.upper.clone(); From ea6a75bbca6b574ee286eb783e3e0ef98946660c Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Mon, 5 May 2025 18:01:41 -0300 Subject: [PATCH 61/64] use iter instead of into_iter --- debug_utils/sierra-emu/src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/src/utils.rs b/debug_utils/sierra-emu/src/utils.rs index 3ab1ea41db..fed3f11a4b 100644 --- a/debug_utils/sierra-emu/src/utils.rs +++ b/debug_utils/sierra-emu/src/utils.rs @@ -10,7 +10,7 @@ use crate::Value; /// Receives a vector of values, filters any which is non numeric and returns a `Vec` /// Useful when a binary operation takes generic values (like with bounded ints). pub fn get_numberic_args_as_bigints(args: &[Value]) -> Vec { - args.into_iter() + args.iter() .map(|v| match v { Value::BoundedInt { value, .. } => value.to_owned(), Value::I8(value) => BigInt::from(*value), From d3c5b0a81fcff0b147ae930eefd21aec44c3fec5 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 6 May 2025 11:16:39 -0300 Subject: [PATCH 62/64] reviews changes --- debug_utils/sierra-emu/src/utils.rs | 2 +- debug_utils/sierra-emu/src/vm/cast.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/debug_utils/sierra-emu/src/utils.rs b/debug_utils/sierra-emu/src/utils.rs index fed3f11a4b..3f20dfde9d 100644 --- a/debug_utils/sierra-emu/src/utils.rs +++ b/debug_utils/sierra-emu/src/utils.rs @@ -25,7 +25,7 @@ pub fn get_numberic_args_as_bigints(args: &[Value]) -> Vec { Value::U128(value) => BigInt::from(*value), Value::Felt(value) => value.to_bigint(), Value::Bytes31(value) => value.to_bigint(), - value => panic!("expected numeric value: {:?}", value), + value => panic!("argument should be an integer: {:?}", value), }) .collect() } diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs index ea0b8e21ee..fe2d8b3599 100644 --- a/debug_utils/sierra-emu/src/vm/cast.rs +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -43,7 +43,7 @@ fn eval_downcast( smallvec![range_check, get_value_from_integer(registry, int_ty, value)], ) } else { - EvalAction::NormalBranch(1, smallvec![Value::Unit]) + EvalAction::NormalBranch(1, smallvec![range_check]) } } From 05c92ebdb8e0156e00efe71670bb7629d3dc6c8a Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Tue, 6 May 2025 11:21:48 -0300 Subject: [PATCH 63/64] better error message --- debug_utils/sierra-emu/src/utils.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debug_utils/sierra-emu/src/utils.rs b/debug_utils/sierra-emu/src/utils.rs index 3f20dfde9d..fce214f229 100644 --- a/debug_utils/sierra-emu/src/utils.rs +++ b/debug_utils/sierra-emu/src/utils.rs @@ -50,6 +50,6 @@ pub fn get_value_from_integer( CoreTypeConcrete::Uint32(_) => Value::U32(value.to_u32().unwrap()), CoreTypeConcrete::Uint64(_) => Value::U64(value.to_u64().unwrap()), CoreTypeConcrete::Uint128(_) => Value::U128(value.to_u128().unwrap()), - _ => panic!("Found a non-numeric type"), + _ => panic!("cannot get integer value for a non-integer type"), } } From 545bbe5288c503b9f27d2d0879fe7532ce6cf1b9 Mon Sep 17 00:00:00 2001 From: FrancoGiachetta Date: Wed, 7 May 2025 15:53:55 -0300 Subject: [PATCH 64/64] fix typo --- debug_utils/sierra-emu/src/utils.rs | 2 +- debug_utils/sierra-emu/src/vm/bounded_int.rs | 10 +++++----- debug_utils/sierra-emu/src/vm/cast.rs | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/debug_utils/sierra-emu/src/utils.rs b/debug_utils/sierra-emu/src/utils.rs index fce214f229..fe618ed905 100644 --- a/debug_utils/sierra-emu/src/utils.rs +++ b/debug_utils/sierra-emu/src/utils.rs @@ -9,7 +9,7 @@ use crate::Value; /// Receives a vector of values, filters any which is non numeric and returns a `Vec` /// Useful when a binary operation takes generic values (like with bounded ints). -pub fn get_numberic_args_as_bigints(args: &[Value]) -> Vec { +pub fn get_numeric_args_as_bigints(args: &[Value]) -> Vec { args.iter() .map(|v| match v { Value::BoundedInt { value, .. } => value.to_owned(), diff --git a/debug_utils/sierra-emu/src/vm/bounded_int.rs b/debug_utils/sierra-emu/src/vm/bounded_int.rs index 24c77a42c7..55d4955d59 100644 --- a/debug_utils/sierra-emu/src/vm/bounded_int.rs +++ b/debug_utils/sierra-emu/src/vm/bounded_int.rs @@ -1,5 +1,5 @@ use super::EvalAction; -use crate::{utils::get_numberic_args_as_bigints, Value}; +use crate::{utils::get_numeric_args_as_bigints, Value}; use cairo_lang_sierra::{ extensions::{ bounded_int::{ @@ -39,7 +39,7 @@ pub fn eval_add( info: &SignatureOnlyConcreteLibfunc, args: Vec, ) -> EvalAction { - let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(&args).try_into().unwrap(); + let [lhs, rhs]: [BigInt; 2] = get_numeric_args_as_bigints(&args).try_into().unwrap(); let range = match registry .get_type(&info.signature.branch_signatures[0].vars[0].ty) @@ -68,7 +68,7 @@ pub fn eval_sub( info: &SignatureOnlyConcreteLibfunc, args: Vec, ) -> EvalAction { - let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(&args).try_into().unwrap(); + let [lhs, rhs]: [BigInt; 2] = get_numeric_args_as_bigints(&args).try_into().unwrap(); let range = match registry .get_type(&info.signature.branch_signatures[0].vars[0].ty) @@ -97,7 +97,7 @@ pub fn eval_mul( info: &SignatureOnlyConcreteLibfunc, args: Vec, ) -> EvalAction { - let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(&args).try_into().unwrap(); + let [lhs, rhs]: [BigInt; 2] = get_numeric_args_as_bigints(&args).try_into().unwrap(); let range = match registry .get_type(&info.signature.branch_signatures[0].vars[0].ty) @@ -126,7 +126,7 @@ pub fn eval_div_rem( info: &BoundedIntDivRemConcreteLibfunc, args: Vec, ) -> EvalAction { - let [lhs, rhs]: [BigInt; 2] = get_numberic_args_as_bigints(&args).try_into().unwrap(); + let [lhs, rhs]: [BigInt; 2] = get_numeric_args_as_bigints(&args).try_into().unwrap(); let quo = &lhs / &rhs; let rem = lhs % rhs; diff --git a/debug_utils/sierra-emu/src/vm/cast.rs b/debug_utils/sierra-emu/src/vm/cast.rs index fe2d8b3599..78330a5fce 100644 --- a/debug_utils/sierra-emu/src/vm/cast.rs +++ b/debug_utils/sierra-emu/src/vm/cast.rs @@ -1,6 +1,6 @@ use super::EvalAction; use crate::{ - utils::{get_numberic_args_as_bigints, get_value_from_integer}, + utils::{get_numeric_args_as_bigints, get_value_from_integer}, Value, }; use cairo_lang_sierra::{ @@ -33,7 +33,7 @@ fn eval_downcast( let range_check @ Value::Unit: Value = args[0].clone() else { panic!() }; - let [value] = get_numberic_args_as_bigints(&args[1..]).try_into().unwrap(); + let [value] = get_numeric_args_as_bigints(&args[1..]).try_into().unwrap(); let int_ty = registry.get_type(&info.to_ty).unwrap(); let range = info.to_range.lower.clone()..info.to_range.upper.clone(); @@ -52,7 +52,7 @@ fn eval_upcast( info: &SignatureOnlyConcreteLibfunc, args: Vec, ) -> EvalAction { - let [value] = get_numberic_args_as_bigints(&args).try_into().unwrap(); + let [value] = get_numeric_args_as_bigints(&args).try_into().unwrap(); let int_ty = registry .get_type(&info.branch_signatures()[0].vars[0].ty) .unwrap();