From 80b725200edff7197cacce202fb852d7837e778c Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Fri, 22 Aug 2025 11:19:44 +0200 Subject: [PATCH 01/45] Bump blockifier, change deps, make compile --- Cargo.lock | 543 +++++++++++++++--- Cargo.toml | 4 +- .../execution/entry_point.rs | 1 + 3 files changed, 457 insertions(+), 91 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cd4ef1488d..91384bea52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -126,11 +126,41 @@ version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" +[[package]] +name = "apollo_compilation_utils" +version = "0.15.0-rc.3" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" +dependencies = [ + "apollo_infra_utils", + "cairo-lang-sierra", + "cairo-lang-starknet-classes", + "cairo-lang-utils", + "rlimit", + "serde", + "serde_json", + "starknet-types-core", + "starknet_api", + "tempfile", + "thiserror 1.0.69", +] + +[[package]] +name = "apollo_compile_to_native" +version = "0.15.0-rc.3" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" +dependencies = [ + "apollo_compilation_utils", + "apollo_compile_to_native_types", + "apollo_infra_utils", + "cairo-lang-starknet-classes", + "cairo-native", + "tempfile", +] + [[package]] name = "apollo_compile_to_native_types" -version = "0.15.0-rc.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389c8e9ea0c68822b179c75ac3aae646b101c390d01095d993e1ae41287d41d5" +version = "0.15.0-rc.3" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" dependencies = [ "apollo_config", "serde", @@ -139,9 +169,8 @@ dependencies = [ [[package]] name = "apollo_config" -version = "0.15.0-rc.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63c97b0be7573c6e0191c8b34776c64ed1bb621148f7d14d14fef4422d630a4" +version = "0.15.0-rc.3" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" dependencies = [ "apollo_infra_utils", "clap", @@ -158,13 +187,12 @@ dependencies = [ [[package]] name = "apollo_infra_utils" -version = "0.15.0-rc.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171220821a924e7c170341fc564553163488a17496194ea5f7d2039f294bc2e4" +version = "0.15.0-rc.3" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" dependencies = [ "apollo_proc_macros", "assert-json-diff", - "colored", + "colored 3.0.0", "num_enum", "serde", "serde_json", @@ -178,8 +206,7 @@ dependencies = [ [[package]] name = "apollo_metrics" version = "0.15.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00d819befc97f0ad6b7ab85546f2dc3546fb4e0faab4b330c3c46824309257c8" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" dependencies = [ "indexmap 2.11.0", "metrics", @@ -190,9 +217,8 @@ dependencies = [ [[package]] name = "apollo_proc_macros" -version = "0.15.0-rc.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7989de6d0cf43a44d6c69c20f183e23af6959fc2be53f65554e4efaf8e6cb5b" +version = "0.15.0-rc.3" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" dependencies = [ "lazy_static 1.5.0", "proc-macro2", @@ -200,6 +226,20 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "aquamarine" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f50776554130342de4836ba542aa85a4ddb361690d7e8df13774d7284c3d5c2" +dependencies = [ + "include_dir", + "itertools 0.10.5", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "ark-ec" version = "0.4.2" @@ -228,7 +268,7 @@ dependencies = [ "ark-poly 0.5.0", "ark-serialize 0.5.0", "ark-std 0.5.0", - "educe", + "educe 0.6.0", "fnv", "hashbrown 0.15.5", "itertools 0.13.0", @@ -270,7 +310,7 @@ dependencies = [ "ark-std 0.5.0", "arrayvec", "digest", - "educe", + "educe 0.6.0", "itertools 0.13.0", "num-bigint", "num-traits", @@ -347,7 +387,7 @@ dependencies = [ "ark-ff 0.5.0", "ark-serialize 0.5.0", "ark-std 0.5.0", - "educe", + "educe 0.6.0", "fnv", "hashbrown 0.15.5", ] @@ -687,6 +727,26 @@ dependencies = [ "virtue", ] +[[package]] +name = "bindgen" +version = "0.71.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +dependencies = [ + "bitflags 2.9.4", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.1", + "shlex", + "syn 2.0.106", +] + [[package]] name = "bit-set" version = "0.8.0" @@ -741,10 +801,11 @@ dependencies = [ [[package]] name = "blockifier" version = "0.15.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56888e7ed4421c6eeb2b0bc44a715b24cd1d9d3cc6b06e1e80b6688d5b93a313" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" dependencies = [ "anyhow", + "apollo_compilation_utils", + "apollo_compile_to_native", "apollo_compile_to_native_types", "apollo_config", "apollo_infra_utils", @@ -758,6 +819,7 @@ dependencies = [ "cairo-lang-casm", "cairo-lang-runner", "cairo-lang-starknet-classes", + "cairo-native", "cairo-vm", "dashmap", "derive_more 0.99.20", @@ -789,8 +851,7 @@ dependencies = [ [[package]] name = "blockifier_test_utils" version = "0.15.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81be5b363638f8618dda40b810bd768e5cdfa3bc8810a04c37db6d994e87f9d3" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" dependencies = [ "apollo_infra_utils", "cairo-lang-starknet-classes", @@ -907,9 +968,9 @@ dependencies = [ [[package]] name = "cairo-lang-casm" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe853af0ccb1b54b0194d9bd77fd5d74825650226cb4e56f8c5f4bacac3d9b1" +checksum = "5fcb67fa250ab8eb216e626dc58ed04081286885005519875bfbcfbb66485f33" dependencies = [ "cairo-lang-utils", "indoc", @@ -921,9 +982,9 @@ dependencies = [ [[package]] name = "cairo-lang-compiler" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f1d4581b52a0d6a151aafed3fae5b28fac3abcad5af6cea405d7786407deab" +checksum = "c4172274cc72c13e6bed80ba984b008ae66e2dab6baef2a2b507d901199e1905" dependencies = [ "anyhow", "cairo-lang-defs", @@ -948,18 +1009,18 @@ dependencies = [ [[package]] name = "cairo-lang-debug" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2257450e0cab180e677efde875c05fef61e522e859bf2930127a9269b13d285" +checksum = "87be3c3a2351b0931ce482c70b407e042c8cf952fdd4751c739c50078edad67b" dependencies = [ "cairo-lang-utils", ] [[package]] name = "cairo-lang-defs" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f012263db353ea6603227b8ae0f0c6a8afe4f7ae1e35d965a154b9940d70df61" +checksum = "db0dce1e9c902b91ec11d6baf0478537db91be457e7b0b03b1395f8daa3c7fe6" dependencies = [ "bincode", "cairo-lang-debug", @@ -978,9 +1039,9 @@ dependencies = [ [[package]] name = "cairo-lang-diagnostics" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9478358838cfc24a9e70b27ab6f15fcd743077aa6eae3075c2a3642b05b2be07" +checksum = "d4a040088f9b8ec09ad7e2a461490276b17b9abd0eb4c5e95b8d87d66ca04255" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", @@ -990,9 +1051,9 @@ dependencies = [ [[package]] name = "cairo-lang-eq-solver" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecf1d9e5a6e691675221334f7e48daa12c56521a097f35357b53cae80db8dd7" +checksum = "fe2a4ed344b143eceaf818d7212a45a5d6dc9e50ebfc5afabb30ff05d8298261" dependencies = [ "cairo-lang-utils", "good_lp", @@ -1000,9 +1061,9 @@ dependencies = [ [[package]] name = "cairo-lang-filesystem" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d139fb25755cbfe8b72530c280f391b02953dbb4d744d58d21ca61711bba3f" +checksum = "694930930f52a131a0f82fa92d213874ac24aad2638cd73e43790e4339cd42c6" dependencies = [ "cairo-lang-debug", "cairo-lang-utils", @@ -1016,9 +1077,9 @@ dependencies = [ [[package]] name = "cairo-lang-formatter" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a597b7cbb9c1fa0b59ca1f2dc46b673931dc491806ff3d974e11921129f62d" +checksum = "c006f5a9342e77da68b07962ba419c20dfb6e0ba8f9e3b16a49cc131f3fbe709" dependencies = [ "anyhow", "cairo-lang-diagnostics", @@ -1035,9 +1096,9 @@ dependencies = [ [[package]] name = "cairo-lang-lowering" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "895b833b2e6e46847281d10f79d6ccbd0980d564115e36e9600a8e90514da81f" +checksum = "8db13296247c54c0be19efbc26c0d2be7758b8a459052931ff5b92ac71f9e670" dependencies = [ "assert_matches", "bincode", @@ -1062,9 +1123,9 @@ dependencies = [ [[package]] name = "cairo-lang-parser" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8db9da7f45d86489add2118fa4ea7cf1217bfec2b32908f2b9ed0e3954e45d0a" +checksum = "a3f75b0baca6666107c47d9d8d40684c99c82c930924eb85a13db90bc795928c" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -1072,7 +1133,7 @@ dependencies = [ "cairo-lang-syntax", "cairo-lang-syntax-codegen", "cairo-lang-utils", - "colored", + "colored 3.0.0", "itertools 0.14.0", "num-bigint", "num-traits", @@ -1083,9 +1144,9 @@ dependencies = [ [[package]] name = "cairo-lang-plugins" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c281268bc974f27771929ffad54a8e61aa28f7de1d16fcf2d484528847c185ac" +checksum = "1ac54f473248dfb00311b35b809e043e2e6ca3e288e63f3876c6ac3509d3c433" dependencies = [ "cairo-lang-defs", "cairo-lang-diagnostics", @@ -1108,9 +1169,9 @@ checksum = "123ac0ecadf31bacae77436d72b88fa9caef2b8e92c89ce63a125ae911a12fae" [[package]] name = "cairo-lang-proc-macros" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1692891334517dae27cfdc2a23c9205a99e88823b6be48a25636f0eeb24903fc" +checksum = "8b62a7d9aa7a499d9189248f2b474b2bdb57877cd4de1d71ab40a8c06de446e3" dependencies = [ "cairo-lang-debug", "quote", @@ -1119,9 +1180,9 @@ dependencies = [ [[package]] name = "cairo-lang-project" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3162a2534953db6dba28c1b4e911e201410abb3970679048ae9d8efca3185a8" +checksum = "755c0793df2fd2f6f2b119b09b844014779b05943541e6c90019540eb4aecf5f" dependencies = [ "cairo-lang-filesystem", "cairo-lang-utils", @@ -1132,9 +1193,9 @@ dependencies = [ [[package]] name = "cairo-lang-runnable-utils" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce4a4b1794fc48757c23310b6cf3de40176bdaab24f9c6de4f1d442108f93d3" +checksum = "f15491e1671043a61b9f97d14d7e0542b5cb53a5247b57bcd1014ccc5f9e91d6" dependencies = [ "cairo-lang-casm", "cairo-lang-sierra", @@ -1150,9 +1211,9 @@ dependencies = [ [[package]] name = "cairo-lang-runner" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a9678e6962534df9f441b149790d590ac6ae95574d24f1e130afe2c24454242" +checksum = "45ac53dbf7eb23495ea035a1b15b4045ad858cdf6fc40301473d16dde0d37471" dependencies = [ "ark-ff 0.5.0", "ark-secp256k1 0.5.0", @@ -1180,9 +1241,9 @@ dependencies = [ [[package]] name = "cairo-lang-semantic" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957d1321013e3cead7fe777ef0e2e2293c2cc0c483e0c965efa98f93ad836b25" +checksum = "bb70bf8a95400e0c853374a6c10e79cf750ba9004ad2eca15dcd87034af995f7" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -1207,9 +1268,9 @@ dependencies = [ [[package]] name = "cairo-lang-sierra" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc19ef658d46da7735185397e04791e841168dc0ae3ad762f1bf48f2a490720d" +checksum = "104eba8e6509ecd4e96cac9cd3dee9f0c698bfc8cba0097b32f6378eaac3edd8" dependencies = [ "anyhow", "cairo-lang-utils", @@ -1234,9 +1295,9 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-ap-change" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "796dcd14c9120a0c3480656a870ab921c66bbfaa4dfc6ca4a3b20668ddaee1a7" +checksum = "9131235c4854d3c483a3d363cbfa70b6c2e8b5ca05e37ecbb9edc4a5dfc2e186" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -1250,9 +1311,9 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-gas" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a8f1448c80729780b581382b9979e06ad39fa70a878f8bf1eb436e736880e14" +checksum = "b6d9d0fed2440c8ea6717e9580d0efa4b10e07415ca9508afe2d9cb9be8aca64" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -1266,9 +1327,9 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-generator" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6cf960c78fc865b1df80c1ac71cd43d8fdb5a95e5dcd680556a60cf6c5132ce" +checksum = "873d0ebb98354c061a9b8f81c2a1e58c7c4e746aa3f96c7e42c427216b2d9ca1" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -1290,9 +1351,9 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-to-casm" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde6acc78b96b9e18e0cb6ba4236b0090b7ce91e6758e57e44a0b17d401086e1" +checksum = "4a47906641d467f18f2035c5017bee4afd6e118eba80e217b82a03b678764919" dependencies = [ "assert_matches", "cairo-lang-casm", @@ -1311,9 +1372,9 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-type-size" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a74f2a90f33f1328d5eb7522432219a4af20de29b5500de96503d16a5b83592" +checksum = "1a7cc6bb50cc23e0252a48a1b2ee9b17edcb74d2dab86c70b095bcd287ecfd66" dependencies = [ "cairo-lang-sierra", "cairo-lang-utils", @@ -1321,9 +1382,9 @@ dependencies = [ [[package]] name = "cairo-lang-starknet" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8b1eea1ef1447020cd5f810820d546fe56b5904bc26fd883399d412ac5a9c0c" +checksum = "2f8264e92d3b921dd72656c8c80d85342d7f7ffd8fa5a77c13862620c9f27240" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -1353,9 +1414,9 @@ dependencies = [ [[package]] name = "cairo-lang-starknet-classes" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b2bfe0e756ea74ab045625cf4e7d766290f9085abd44a2ef62e41552116f1e3" +checksum = "0d46f87d7dc5ee23ff1e67cb88210f3273ee97b82efc84e30f5abf17a96d7510" dependencies = [ "cairo-lang-casm", "cairo-lang-sierra", @@ -1376,9 +1437,9 @@ dependencies = [ [[package]] name = "cairo-lang-syntax" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4df3767d1f764a24c07b0547489d2ad80ef011d6b4e8bab5e853624348e72d3" +checksum = "0638177321676b1ffc6350e3d7a62cd3d48df46d90bfc561b32a38589f0eeee0" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", @@ -1404,9 +1465,9 @@ dependencies = [ [[package]] name = "cairo-lang-test-plugin" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d11cc191cf980457a39de7ce7ca8133bb77df4d4f23b6374a11f8204db5eb3" +checksum = "43110b7397ba63a594e79f52c61c7be1ff295b5b460d123ef68099569ccc0a44" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -1432,22 +1493,22 @@ dependencies = [ [[package]] name = "cairo-lang-test-utils" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ddd9bfe3166875daee9864c4f79c6e4610ca597ab934e9961d4734b6855ddd" +checksum = "d325a9afabf56b9d20de7799fa370f83a673fc0a9ff62e0abcabf62cb1efbff9" dependencies = [ "cairo-lang-formatter", "cairo-lang-utils", - "colored", + "colored 3.0.0", "log", "pretty_assertions", ] [[package]] name = "cairo-lang-utils" -version = "2.12.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e414e98233e2d0ee7a4259e39e4281092aab91f0cc4f1335c63dc44d1abeeff" +checksum = "3443f4fc17986f362f5b87cd8c37dafeadf5e0a0909a19f2541cd55baae25a74" dependencies = [ "hashbrown 0.15.5", "indexmap 2.11.0", @@ -1460,6 +1521,61 @@ dependencies = [ "smol_str", ] +[[package]] +name = "cairo-native" +version = "0.6.0" +source = "git+https://github.com/lambdaclass/cairo_native.git?rev=d7f8352657a4c1228b227807b90aea10f97f746d#d7f8352657a4c1228b227807b90aea10f97f746d" +dependencies = [ + "anyhow", + "aquamarine", + "ark-ec 0.5.0", + "ark-ff 0.5.0", + "ark-secp256k1 0.5.0", + "ark-secp256r1 0.5.0", + "bumpalo", + "cairo-lang-compiler", + "cairo-lang-defs", + "cairo-lang-filesystem", + "cairo-lang-runner", + "cairo-lang-semantic", + "cairo-lang-sierra", + "cairo-lang-sierra-ap-change", + "cairo-lang-sierra-gas", + "cairo-lang-sierra-generator", + "cairo-lang-sierra-to-casm", + "cairo-lang-starknet", + "cairo-lang-starknet-classes", + "cairo-lang-test-plugin", + "cairo-lang-utils", + "cc", + "clap", + "colored 2.2.0", + "educe 0.5.11", + "itertools 0.14.0", + "keccak", + "lazy_static 1.5.0", + "libc", + "libloading", + "llvm-sys", + "melior", + "mlir-sys", + "num-bigint", + "num-integer", + "num-traits", + "rand 0.9.2", + "serde", + "serde_json", + "sha2", + "starknet-curve 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "starknet-types-core", + "stats_alloc", + "tempfile", + "thiserror 2.0.16", + "tracing", + "tracing-subscriber", + "utf8_iter", +] + [[package]] name = "cairo-serde-macros" version = "1.0.0" @@ -1510,6 +1626,15 @@ dependencies = [ "serde", ] +[[package]] +name = "caseless" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6fd507454086c8edfd769ca6ada439193cdb209c7681712ef6275cccbfe5d8" +dependencies = [ + "unicode-normalization", +] + [[package]] name = "cc" version = "1.2.35" @@ -1520,6 +1645,15 @@ dependencies = [ "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.3" @@ -1534,7 +1668,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "cheatnet" -version = "0.49.0" +version = "0.48.1" dependencies = [ "anyhow", "bimap", @@ -1604,6 +1738,17 @@ dependencies = [ "inout", ] +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "clap" version = "4.5.47" @@ -1670,6 +1815,16 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static 1.5.0", + "windows-sys 0.59.0", +] + [[package]] name = "colored" version = "3.0.0" @@ -1679,6 +1834,20 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "comrak" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39bff2cbb80102771ca62bd2375bc6f6611dc1493373440b23aa08a155538708" +dependencies = [ + "caseless", + "entities", + "memchr", + "slug", + "typed-arena", + "unicode_categories", +] + [[package]] name = "config" version = "0.14.1" @@ -1823,6 +1992,15 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "convert_case" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "convert_case" version = "0.8.0" @@ -2160,6 +2338,12 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "deunicode" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" + [[package]] name = "dialoguer" version = "0.11.0" @@ -2297,7 +2481,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "docs" -version = "0.49.0" +version = "0.48.1" dependencies = [ "anyhow", "camino", @@ -2354,6 +2538,18 @@ dependencies = [ "spki", ] +[[package]] +name = "educe" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4bd92664bf78c4d3dba9b7cdafce6fa15b13ed3ed16175218196942e99168a8" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "educe" version = "0.6.0" @@ -2423,6 +2619,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "entities" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" + [[package]] name = "enum-ordinalize" version = "4.3.0" @@ -2639,7 +2841,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "forge" -version = "0.49.0" +version = "0.48.1" dependencies = [ "anyhow", "assert_fs", @@ -2696,7 +2898,7 @@ dependencies = [ [[package]] name = "forge_runner" -version = "0.49.0" +version = "0.48.1" dependencies = [ "anyhow", "blockifier", @@ -2754,7 +2956,7 @@ dependencies = [ [[package]] name = "foundry-ui" -version = "0.49.0" +version = "0.48.1" dependencies = [ "anyhow", "console", @@ -3740,6 +3942,16 @@ version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.3", +] + [[package]] name = "libm" version = "0.2.15" @@ -3774,6 +3986,20 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +[[package]] +name = "llvm-sys" +version = "191.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "893cddf1adf0354b93411e413553dd4daf5c43195d73f1acfa1e394bdd371456" +dependencies = [ + "anyhow", + "cc", + "lazy_static 1.5.0", + "libc", + "regex-lite", + "semver", +] + [[package]] name = "lock_api" version = "0.4.13" @@ -3836,6 +4062,32 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "melior" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2af6454b7bcd7edc8c2060a3726a18ceaed60e25c34d9f8de9c6b44e82eb647" +dependencies = [ + "melior-macro", + "mlir-sys", +] + +[[package]] +name = "melior-macro" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99671327250df8e24e56d8304474a970e7a2c6bb8f6dc71382d188136fe4d1b" +dependencies = [ + "comrak", + "convert_case 0.7.1", + "proc-macro2", + "quote", + "regex", + "syn 2.0.106", + "tblgen", + "unindent", +] + [[package]] name = "memchr" version = "2.7.5" @@ -3903,6 +4155,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "mlir-sys" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cee4047ffefa7e9853412025a98b38a66968584543918cf084a6e4df9144b71b" +dependencies = [ + "bindgen", +] + [[package]] name = "mockall" version = "0.12.1" @@ -4581,6 +4842,16 @@ dependencies = [ "yansi", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.106", +] + [[package]] name = "primeorder" version = "0.13.6" @@ -4648,6 +4919,27 @@ dependencies = [ "version_check", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", +] + [[package]] name = "proc-macro2" version = "1.0.101" @@ -4957,6 +5249,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" + [[package]] name = "regex-syntax" version = "0.8.6" @@ -5037,6 +5335,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rlimit" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7043b63bd0cd1aaa628e476b80e6d4023a3b50eb32789f2728908107bd0c793a" +dependencies = [ + "libc", +] + [[package]] name = "rlp" version = "0.5.2" @@ -5799,6 +6106,16 @@ version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +[[package]] +name = "slug" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + [[package]] name = "smallvec" version = "1.15.1" @@ -5839,7 +6156,7 @@ dependencies = [ [[package]] name = "sncast" -version = "0.49.0" +version = "0.48.1" dependencies = [ "anyhow", "async-trait", @@ -6184,9 +6501,8 @@ dependencies = [ [[package]] name = "starknet_api" -version = "0.15.0-rc.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8aab893c06cdba387b8ef61a079ae0ccdcb926d6ecca11107f536d788e62a8" +version = "0.15.0-rc.3" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" dependencies = [ "apollo_infra_utils", "base64 0.13.1", @@ -6224,6 +6540,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stats_alloc" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0e04424e733e69714ca1bbb9204c1a57f09f5493439520f9f68c132ad25eec" + [[package]] name = "str-buf" version = "1.0.6" @@ -6369,6 +6691,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +[[package]] +name = "tblgen" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c155c9310c9e11e6f642b4c8a30ae572ea0cad013d5c9e28bb264b52fa8163bb" +dependencies = [ + "bindgen", + "cc", + "paste", + "thiserror 2.0.16", +] + [[package]] name = "tempfile" version = "3.21.0" @@ -6782,6 +7116,16 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.20" @@ -6792,12 +7136,15 @@ dependencies = [ "nu-ansi-term", "once_cell", "regex-automata", + "serde", + "serde_json", "sharded-slab", "smallvec", "thread_local", "tracing", "tracing-core", "tracing-log", + "tracing-serde", ] [[package]] @@ -6837,6 +7184,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typeid" version = "1.0.3" @@ -6963,6 +7316,18 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + +[[package]] +name = "unindent" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7264e107f553ccae879d21fbea1d6724ac785e8c3bfc762137959b5802826ef3" + [[package]] name = "universal-sierra-compiler-api" version = "1.0.0" diff --git a/Cargo.toml b/Cargo.toml index 0f435fd4e3..d496117cca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,9 +33,9 @@ license = "MIT" license-file = "LICENSE" [workspace.dependencies] -blockifier = { version = "0.15.0-rc.3", features = ["testing", "tracing"] } +blockifier = { git = "https://github.com/cptartur/sequencer.git", branch = "main-v0.14.0-fork", features = ["testing", "tracing", "cairo_native"] } bigdecimal = "0.4.8" -starknet_api = "0.15.0-rc.3" +starknet_api = { git = "https://github.com/cptartur/sequencer.git", branch = "main-v0.14.0-fork" } cairo-lang-casm = { version = "2.12.0", features = ["serde"] } cairo-lang-sierra = "2.12.0" cairo-lang-utils = "2.12.0" diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs index ebf4c70d19..f69cb73c84 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs @@ -174,6 +174,7 @@ pub fn execute_call_entry_point( cheatnet_state, context, ), + RunnableCompiledClass::V1Native(_) => todo!(), }; context .tracked_resource_stack From d78be5e7cced7771da37584f9ecde1a8e803370c Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Fri, 22 Aug 2025 12:18:41 +0200 Subject: [PATCH 02/45] Execute simple_package on native --- Cargo.lock | 1 + Cargo.toml | 1 + crates/cheatnet/Cargo.toml | 1 + .../execution/entry_point.rs | 25 +++++++++++-- .../cheatcodes/declare.rs | 35 ++++++++++++++++++- crates/cheatnet/src/runtime_extensions/mod.rs | 1 + .../runtime_extensions/native/execution.rs | 28 +++++++++++++++ .../src/runtime_extensions/native/mod.rs | 1 + 8 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 crates/cheatnet/src/runtime_extensions/native/execution.rs create mode 100644 crates/cheatnet/src/runtime_extensions/native/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 91384bea52..bf02c20130 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1677,6 +1677,7 @@ dependencies = [ "cairo-lang-casm", "cairo-lang-starknet-classes", "cairo-lang-utils", + "cairo-native", "cairo-vm", "camino", "conversions", diff --git a/Cargo.toml b/Cargo.toml index d496117cca..ddcae29fbc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ license-file = "LICENSE" blockifier = { git = "https://github.com/cptartur/sequencer.git", branch = "main-v0.14.0-fork", features = ["testing", "tracing", "cairo_native"] } bigdecimal = "0.4.8" starknet_api = { git = "https://github.com/cptartur/sequencer.git", branch = "main-v0.14.0-fork" } +cairo-native = { git = "https://github.com/lambdaclass/cairo_native.git", rev = "d7f8352657a4c1228b227807b90aea10f97f746d" } cairo-lang-casm = { version = "2.12.0", features = ["serde"] } cairo-lang-sierra = "2.12.0" cairo-lang-utils = "2.12.0" diff --git a/crates/cheatnet/Cargo.toml b/crates/cheatnet/Cargo.toml index b999b5d710..22c22dc977 100644 --- a/crates/cheatnet/Cargo.toml +++ b/crates/cheatnet/Cargo.toml @@ -44,6 +44,7 @@ shared.workspace = true rand.workspace = true foundry-ui = { path = "../foundry-ui" } scarb-oracle-hint-service.workspace = true +cairo-native.workspace = true [dev-dependencies] ctor.workspace = true diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs index f69cb73c84..9a12544412 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs @@ -3,11 +3,12 @@ use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution:: use crate::runtime_extensions::call_to_blockifier_runtime_extension::rpc::{AddressOrClassHash, CallResult}; use crate::runtime_extensions::call_to_blockifier_runtime_extension::CheatnetState; use crate::runtime_extensions::common::{get_relocated_vm_trace, get_syscalls_gas_consumed, sum_syscall_usage}; +use crate::runtime_extensions::forge_runtime_extension::{get_nested_calls_syscalls_sierra_gas, get_nested_calls_syscalls_vm_resources}; use crate::state::CheatStatus; use blockifier::execution::call_info::{CallExecution, Retdata, StorageAccessTracker}; -use crate::runtime_extensions::forge_runtime_extension::{get_nested_calls_syscalls_sierra_gas, get_nested_calls_syscalls_vm_resources}; use blockifier::execution::contract_class::{RunnableCompiledClass, TrackedResource}; +use crate::runtime_extensions::native::execution::execute_entry_point_call_native; use blockifier::execution::entry_point::{EntryPointRevertInfo, ExecutableCallEntryPoint}; use blockifier::execution::stack_trace::{ Cairo1RevertHeader, extract_trailing_cairo1_revert_trace, @@ -174,7 +175,27 @@ pub fn execute_call_entry_point( cheatnet_state, context, ), - RunnableCompiledClass::V1Native(_) => todo!(), + RunnableCompiledClass::V1Native(native_compiled_class_v1) => { + // if context.tracked_resource_stack.last() == Some(&TrackedResource::CairoSteps) { + // // We cannot run native with cairo steps as the tracked resources (it's a vm + // // resouorce). + // panic!("We want to test execution of native entry only"); + // // execute_entry_point_call_cairo1( + // // call, + // // compiled_class.casm(), + // // state, + // // context, + // // ) + // } else { + println!("Executing native entry point"); + execute_entry_point_call_native( + entry_point.clone(), + native_compiled_class_v1, + state, + cheatnet_state, + context, + ) + } // } }; context .tracked_resource_stack diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs index 1a229e3113..8844ffadf3 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs @@ -5,10 +5,14 @@ use crate::runtime_extensions::forge_runtime_extension::{ }; use anyhow::{Context, Result}; use blockifier::execution::contract_class::{CompiledClassV1, RunnableCompiledClass}; +use blockifier::execution::native::contract_class::NativeCompiledClassV1; use blockifier::state::{errors::StateError, state_api::State}; +use cairo_lang_starknet_classes::contract_class::ContractClass; +use cairo_native::executor::AotContractExecutor; use conversions::IntoConv; use conversions::serde::serialize::CairoSerialize; use starknet::core::types::contract::SierraClass; +use starknet_api::contract_class::SierraVersion; use starknet_api::core::{ClassHash, CompiledClassHash}; #[derive(CairoSerialize)] @@ -27,12 +31,41 @@ pub fn declare( .with_context(|| format!("Failed to get contract artifact for name = {contract_name}.")) .map_err(EnhancedHintError::from)?; + let sierra_contract_class: ContractClass = + serde_json::from_str(&contract_artifact.sierra).unwrap(); + + let sierra_program = sierra_contract_class + .extract_sierra_program() + .expect("Cannot extract sierra program from sierra contract class"); + + let sierra_version_values = sierra_contract_class + .sierra_program + .iter() + .take(3) + .map(|x| x.value.clone()) + .collect::>(); + + let sierra_version = SierraVersion::extract_from_program(&sierra_version_values) + .expect("Cannot extract sierra version from sierra program"); + + let executor = AotContractExecutor::new( + &sierra_program, + &sierra_contract_class.entry_points_by_type, + sierra_version.clone().into(), + cairo_native::OptLevel::Default, + // `stats` - Passing a [cairo_native::statistics::Statistics] object enables collecting + // compilation statistics. + None, + ) + .expect("Cannot compile sierra into native"); + let contract_class = CompiledClassV1::try_from_json_string( &contract_artifact.casm, get_current_sierra_version(), ) .expect("Failed to read contract class from json"); - let contract_class = RunnableCompiledClass::V1(contract_class); + let contract_class = NativeCompiledClassV1::new(executor, contract_class); + let contract_class = RunnableCompiledClass::V1Native(contract_class); let class_hash = *contracts_data .get_class_hash(contract_name) diff --git a/crates/cheatnet/src/runtime_extensions/mod.rs b/crates/cheatnet/src/runtime_extensions/mod.rs index 69a31b68be..3db8b52ecc 100644 --- a/crates/cheatnet/src/runtime_extensions/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/mod.rs @@ -4,3 +4,4 @@ pub mod common; pub mod deprecated_cheatable_starknet_extension; pub mod forge_config_extension; pub mod forge_runtime_extension; +mod native; diff --git a/crates/cheatnet/src/runtime_extensions/native/execution.rs b/crates/cheatnet/src/runtime_extensions/native/execution.rs new file mode 100644 index 0000000000..b1aa6a1fb5 --- /dev/null +++ b/crates/cheatnet/src/runtime_extensions/native/execution.rs @@ -0,0 +1,28 @@ +use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{ + CallInfoWithExecutionData, ContractClassEntryPointExecutionResult, +}; +use crate::state::CheatnetState; +use blockifier::execution::entry_point::{EntryPointExecutionContext, ExecutableCallEntryPoint}; +use blockifier::execution::native::contract_class::NativeCompiledClassV1; +use blockifier::execution::native::entry_point_execution::execute_entry_point_call; +use blockifier::state::state_api::State; +use std::default::Default; + +pub(crate) fn execute_entry_point_call_native( + call: ExecutableCallEntryPoint, + native_compiled_class_v1: NativeCompiledClassV1, + state: &mut dyn State, + cheatnet_state: &mut CheatnetState, // Added parameter + context: &mut EntryPointExecutionContext, +) -> ContractClassEntryPointExecutionResult { + // TODO error handling + let call_info = execute_entry_point_call(call, native_compiled_class_v1, state, context) + .expect("Native execution failed"); + + Ok(CallInfoWithExecutionData { + call_info, + syscall_usage_vm_resources: Default::default(), + syscall_usage_sierra_gas: Default::default(), + vm_trace: None, + }) +} diff --git a/crates/cheatnet/src/runtime_extensions/native/mod.rs b/crates/cheatnet/src/runtime_extensions/native/mod.rs new file mode 100644 index 0000000000..fa9fe75038 --- /dev/null +++ b/crates/cheatnet/src/runtime_extensions/native/mod.rs @@ -0,0 +1 @@ +pub mod execution; From e78b97ffaf08cd67db9d635ee25dec0a60631e2a Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Fri, 22 Aug 2025 12:45:21 +0200 Subject: [PATCH 03/45] Add simple test with cheat --- .../simple_package_with_cheats/Scarb.toml | 15 ++++++++ .../simple_package_with_cheats/src/lib.cairo | 35 +++++++++++++++++++ .../tests/contract.cairo | 23 ++++++++++++ crates/forge/tests/e2e/running.rs | 21 +++++++++++ 4 files changed, 94 insertions(+) create mode 100644 crates/forge/tests/data/simple_package_with_cheats/Scarb.toml create mode 100644 crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo create mode 100644 crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo diff --git a/crates/forge/tests/data/simple_package_with_cheats/Scarb.toml b/crates/forge/tests/data/simple_package_with_cheats/Scarb.toml new file mode 100644 index 0000000000..2ab9f60c85 --- /dev/null +++ b/crates/forge/tests/data/simple_package_with_cheats/Scarb.toml @@ -0,0 +1,15 @@ +[package] +name = "simple_package_with_cheats" +version = "0.1.0" +edition = "2024_07" + +[dependencies] +starknet = "2.12.0" + +[dev-dependencies] +snforge_std = { path = "../../../../../snforge_std" } + +[[target.starknet-contract]] + +[tool.snforge] +exit_first = false diff --git a/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo b/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo new file mode 100644 index 0000000000..adb5cd79fa --- /dev/null +++ b/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo @@ -0,0 +1,35 @@ +#[starknet::interface] +pub trait IHelloStarknet { + fn increase_balance(ref self: TContractState, amount: felt252); + fn get_balance(self: @TContractState) -> felt252; + fn get_block_number(self: @TContractState) -> u64; +} + +#[starknet::contract] +pub mod HelloStarknet { + use core::array::ArrayTrait; + use starknet::get_block_number; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + + #[storage] + struct Storage { + balance: felt252, + } + + #[abi(embed_v0)] + impl IHelloStarknetImpl of super::IHelloStarknet { + // Increases the balance by the given amount + fn increase_balance(ref self: ContractState, amount: felt252) { + self.balance.write(self.balance.read() + amount); + } + + // Returns the current balance + fn get_balance(self: @ContractState) -> felt252 { + self.balance.read() + } + + fn get_block_number(self: @ContractState) -> u64 { + get_block_number() + } + } +} diff --git a/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo b/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo new file mode 100644 index 0000000000..eb047c9cf3 --- /dev/null +++ b/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo @@ -0,0 +1,23 @@ +use core::array::ArrayTrait; +use core::result::ResultTrait; +use simple_package_with_cheats::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; +use snforge_std::cheatcodes::contract_class::DeclareResultTrait; +use snforge_std::{ContractClassTrait, declare, start_cheat_block_number_global}; + +#[test] +fn call_and_invoke() { + let contract = declare("HelloStarknet").unwrap().contract_class(); + let constructor_calldata = @ArrayTrait::new(); + let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); + let dispatcher = IHelloStarknetDispatcher { contract_address }; + + let block_number = dispatcher.get_block_number(); + println!("block number {}", block_number); + // TODO investigate why the default is 2000 + assert(block_number == 2000, 'block_info == 2000'); + + start_cheat_block_number_global(123); + + let block_number = dispatcher.get_block_number(); + assert(block_number == 123, 'block_info == 123'); +} diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 02462c318d..1fc14b428f 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -51,6 +51,27 @@ fn simple_package() { ); } +#[test] +fn simple_package_with_cheats() { + let temp = setup_package("simple_package_with_cheats"); + let output = test_runner(&temp).assert().code(0); + + assert_stdout_contains( + output, + indoc! {r" + [..]Compiling[..] + [..]Finished[..] + + + Collected 1 test(s) from simple_package_with_cheats package + Running 0 test(s) from src/ + Running 1 test(s) from tests/ + [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke [..] + "}, + ); +} + + #[test] fn simple_package_with_git_dependency() { let temp = setup_package("simple_package"); From 6555a15de25a874121d5401ef3e7ed580e58fbb6 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Fri, 22 Aug 2025 16:05:07 +0200 Subject: [PATCH 04/45] Implement chetable syscall handler for native, implement cheating `get_execution_info_v2` --- .../runtime_extensions/native/execution.rs | 196 +++++++- .../src/runtime_extensions/native/mod.rs | 1 + .../native/native_syscall_handler.rs | 463 ++++++++++++++++++ crates/forge/tests/e2e/running.rs | 1 - 4 files changed, 657 insertions(+), 4 deletions(-) create mode 100644 crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs diff --git a/crates/cheatnet/src/runtime_extensions/native/execution.rs b/crates/cheatnet/src/runtime_extensions/native/execution.rs index b1aa6a1fb5..e2270a7761 100644 --- a/crates/cheatnet/src/runtime_extensions/native/execution.rs +++ b/crates/cheatnet/src/runtime_extensions/native/execution.rs @@ -1,11 +1,24 @@ use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{ CallInfoWithExecutionData, ContractClassEntryPointExecutionResult, }; +use crate::runtime_extensions::native::native_syscall_handler::CheatableNativeSyscallHandler; use crate::state::CheatnetState; -use blockifier::execution::entry_point::{EntryPointExecutionContext, ExecutableCallEntryPoint}; +use blockifier::execution::call_info::{BuiltinCounterMap, CallExecution, CallInfo, Retdata}; +use blockifier::execution::contract_class::TrackedResource; +use blockifier::execution::entry_point::{ + EntryPointExecutionContext, EntryPointExecutionResult, ExecutableCallEntryPoint, +}; +use blockifier::execution::errors::{ + EntryPointExecutionError, PostExecutionError, PreExecutionError, +}; use blockifier::execution::native::contract_class::NativeCompiledClassV1; -use blockifier::execution::native::entry_point_execution::execute_entry_point_call; +use blockifier::execution::native::syscall_handler::NativeSyscallHandler; use blockifier::state::state_api::State; +use blockifier::transaction::objects::ExecutionResourcesTraits; +use blockifier::utils::add_maps; +use cairo_native::execution_result::{BuiltinStats, ContractExecutionResult}; +use cairo_native::utils::BuiltinCosts; +use cairo_vm::types::builtin_name::BuiltinName; use std::default::Default; pub(crate) fn execute_entry_point_call_native( @@ -15,8 +28,13 @@ pub(crate) fn execute_entry_point_call_native( cheatnet_state: &mut CheatnetState, // Added parameter context: &mut EntryPointExecutionContext, ) -> ContractClassEntryPointExecutionResult { + let syscall_handler = CheatableNativeSyscallHandler { + cheatnet_state, + native_syscall_handler: &mut NativeSyscallHandler::new(call.clone(), state, context), + }; + // TODO error handling - let call_info = execute_entry_point_call(call, native_compiled_class_v1, state, context) + let call_info = execute_entry_point_call(call, native_compiled_class_v1, syscall_handler) .expect("Native execution failed"); Ok(CallInfoWithExecutionData { @@ -26,3 +44,175 @@ pub(crate) fn execute_entry_point_call_native( vm_trace: None, }) } + +// Copied from blockifier +// todo(rodrigo): add an `entry point not found` test for Native +#[allow(clippy::result_large_err)] +pub fn execute_entry_point_call( + call: ExecutableCallEntryPoint, + compiled_class: NativeCompiledClassV1, + // state: &mut dyn State, + // context: &mut EntryPointExecutionContext, + mut syscall_handler: CheatableNativeSyscallHandler, +) -> EntryPointExecutionResult { + let entry_point = compiled_class.get_entry_point(&call.type_and_selector())?; + + // let mut syscall_handler: NativeSyscallHandler<'_> = + // NativeSyscallHandler::new(call, state, context); + + let gas_costs = &syscall_handler + .native_syscall_handler + .base + .context + .gas_costs(); + let builtin_costs = BuiltinCosts { + // todo(rodrigo): Unsure of what value `const` means, but 1 is the right value. + r#const: 1, + pedersen: gas_costs.builtins.pedersen, + bitwise: gas_costs.builtins.bitwise, + ecop: gas_costs.builtins.ecop, + poseidon: gas_costs.builtins.poseidon, + add_mod: gas_costs.builtins.add_mod, + mul_mod: gas_costs.builtins.mul_mod, + }; + + // Pre-charge entry point's initial budget to ensure sufficient gas for executing a minimal + // entry point code. When redepositing is used, the entry point is aware of this pre-charge + // and adjusts the gas counter accordingly if a smaller amount of gas is required. + let initial_budget = syscall_handler + .native_syscall_handler + .base + .context + .gas_costs() + .base + .entry_point_initial_budget; + let call_initial_gas = syscall_handler + .native_syscall_handler + .base + .call + .initial_gas + .checked_sub(initial_budget) + .ok_or(PreExecutionError::InsufficientEntryPointGas)?; + + let execution_result = compiled_class.executor.run( + entry_point.selector.0, + &syscall_handler + .native_syscall_handler + .base + .call + .calldata + .0 + .clone(), + call_initial_gas, + Some(builtin_costs), + &mut syscall_handler, + ); + + syscall_handler.native_syscall_handler.finalize(); + + let call_result = execution_result.map_err(EntryPointExecutionError::NativeUnexpectedError)?; + + // TODO consider modifying this so it doesn't use take internally + if let Some(error) = syscall_handler.unrecoverable_error() { + return Err(EntryPointExecutionError::NativeUnrecoverableError( + Box::new(error), + )); + } + + create_callinfo(call_result, syscall_handler) +} + +// Copied from blockifier +#[allow(clippy::result_large_err)] +fn create_callinfo( + call_result: ContractExecutionResult, + syscall_handler: CheatableNativeSyscallHandler<'_>, +) -> Result { + let remaining_gas = call_result.remaining_gas; + + if remaining_gas > syscall_handler.native_syscall_handler.base.call.initial_gas { + return Err(PostExecutionError::MalformedReturnData { + error_message: format!( + "Unexpected remaining gas. Used gas is greater than initial gas: {} > {}", + remaining_gas, syscall_handler.native_syscall_handler.base.call.initial_gas + ), + } + .into()); + } + + let gas_consumed = syscall_handler.native_syscall_handler.base.call.initial_gas - remaining_gas; + let vm_resources = CallInfo::summarize_vm_resources( + syscall_handler + .native_syscall_handler + .base + .inner_calls + .iter(), + ); + + // Retrieve the builtin counts from the syscall handler + let version_constants = syscall_handler + .native_syscall_handler + .base + .context + .versioned_constants(); + let syscall_builtin_counts = version_constants + .get_additional_os_syscall_resources( + &syscall_handler.native_syscall_handler.base.syscalls_usage, + ) + .filter_unused_builtins() + .prover_builtins(); + let entry_point_builtins = builtin_stats_to_builtin_counter_map(call_result.builtin_stats); + let mut builtin_counters = syscall_builtin_counts; + add_maps(&mut builtin_counters, &entry_point_builtins); + + Ok(CallInfo { + call: syscall_handler + .native_syscall_handler + .base + .call + .clone() + .into(), + execution: CallExecution { + retdata: Retdata(call_result.return_values), + events: syscall_handler.native_syscall_handler.base.events.clone(), + cairo_native: true, + l2_to_l1_messages: syscall_handler + .native_syscall_handler + .base + .l2_to_l1_messages + .clone(), + failed: call_result.failure_flag, + gas_consumed, + }, + resources: vm_resources, + inner_calls: syscall_handler + .native_syscall_handler + .base + .inner_calls + .clone(), + storage_access_tracker: syscall_handler + .native_syscall_handler + .base + .storage_access_tracker + .clone(), + tracked_resource: TrackedResource::SierraGas, + builtin_counters, + }) +} + +fn builtin_stats_to_builtin_counter_map(builtin_stats: BuiltinStats) -> BuiltinCounterMap { + let builtins = [ + (BuiltinName::range_check, builtin_stats.range_check), + (BuiltinName::pedersen, builtin_stats.pedersen), + (BuiltinName::bitwise, builtin_stats.bitwise), + (BuiltinName::ec_op, builtin_stats.ec_op), + (BuiltinName::poseidon, builtin_stats.poseidon), + (BuiltinName::range_check96, builtin_stats.range_check96), + (BuiltinName::add_mod, builtin_stats.add_mod), + (BuiltinName::mul_mod, builtin_stats.mul_mod), + ]; + builtins + .into_iter() + .filter(|(_, count)| *count > 0) + .collect() +} diff --git a/crates/cheatnet/src/runtime_extensions/native/mod.rs b/crates/cheatnet/src/runtime_extensions/native/mod.rs index fa9fe75038..2c7f77e86c 100644 --- a/crates/cheatnet/src/runtime_extensions/native/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/native/mod.rs @@ -1 +1,2 @@ pub mod execution; +pub mod native_syscall_handler; diff --git a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs new file mode 100644 index 0000000000..923bb55519 --- /dev/null +++ b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs @@ -0,0 +1,463 @@ +use crate::state::CheatnetState; +use blockifier::execution::native::syscall_handler::NativeSyscallHandler; +use blockifier::execution::syscalls::hint_processor::{SyscallExecutionError, OUT_OF_GAS_ERROR}; +use blockifier::execution::syscalls::vm_syscall_utils::SyscallSelector; +use cairo_native::starknet::{ + BlockInfo, ExecutionInfo, ExecutionInfoV2, ResourceBounds, Secp256k1Point, Secp256r1Point, + StarknetSyscallHandler, SyscallResult, TxV2Info, U256, +}; +use num_traits::ToPrimitive; +use starknet_api::execution_resources::GasAmount; +use starknet_types_core::felt::Felt; + +pub struct CheatableNativeSyscallHandler<'a> { + pub native_syscall_handler: &'a mut NativeSyscallHandler<'a>, + pub cheatnet_state: &'a mut CheatnetState, +} + +impl CheatableNativeSyscallHandler<'_> { + // TODO consider modifying this so it doesn't use take + pub fn unrecoverable_error(&mut self) -> Option { + self.native_syscall_handler.unrecoverable_error.take() + } + + /// Handles all gas-related logics, syscall usage counting and perform additional checks. In + /// native, we need to explicitly call this method at the beginning of each syscall. + #[allow(clippy::result_large_err)] + fn pre_execute_syscall( + &mut self, + remaining_gas: &mut u64, + total_gas_cost: u64, + selector: SyscallSelector, + ) -> SyscallResult<()> { + if self.native_syscall_handler.unrecoverable_error.is_some() { + // An unrecoverable error was found in a previous syscall, we return immediately to + // accelerate the end of the execution. The returned data is not important + return Err(vec![]); + } + + // Keccak syscall usages' increments are handled inside its implementation. + if !matches!(selector, SyscallSelector::Keccak) { + self.native_syscall_handler + .base + .increment_syscall_count_by(selector, 1); + } + + // Refund `SYSCALL_BASE_GAS_COST` as it was pre-charged. + let required_gas = total_gas_cost + - self + .native_syscall_handler + .gas_costs() + .base + .syscall_base_gas_cost; + + if *remaining_gas < required_gas { + // Out of gas failure. + return Err(vec![ + Felt::from_hex(OUT_OF_GAS_ERROR) + .expect("Failed to parse OUT_OF_GAS_ERROR hex string"), + ]); + } + + *remaining_gas -= required_gas; + + // To support sierra gas charge for blockifier revert flow, we track the remaining gas left + // before executing a syscall if the current tracked resource is gas. + // 1. If the syscall does not run Cairo code (i.e. not library call, not call contract, and + // not a deploy), any failure will not run in the OS, so no need to charge - the value + // before entering the callback is good enough to charge. + // 2. If the syscall runs Cairo code, but the tracked resource is steps (and not gas), the + // additional charge of reverted cairo steps will cover the inner cost, and the outer + // cost we track here will be the additional reverted gas. + // 3. If the syscall runs Cairo code and the tracked resource is gas, either the inner + // failure will be a Cairo1 revert (and the gas consumed on the call info will override + // the current tracked value), or we will pass through another syscall before failing - + // and by induction (we will reach this point again), the gas will be charged correctly. + self.native_syscall_handler + .base + .context + .update_revert_gas_with_next_remaining_gas(GasAmount(*remaining_gas)); + + Ok(()) + } +} + +impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { + fn get_block_hash( + &mut self, + block_number: u64, + remaining_gas: &mut u64, + ) -> SyscallResult { + self.native_syscall_handler + .get_block_hash(block_number, remaining_gas) + } + + fn get_execution_info(&mut self, remaining_gas: &mut u64) -> SyscallResult { + self.native_syscall_handler + .get_execution_info(remaining_gas) + } + + #[expect(clippy::too_many_lines)] + fn get_execution_info_v2(&mut self, remaining_gas: &mut u64) -> SyscallResult { + self.pre_execute_syscall( + remaining_gas, + self.native_syscall_handler + .gas_costs() + .syscalls + .get_execution_info + .base_syscall_cost(), + SyscallSelector::GetBlockHash, + )?; + + let original_data = self + .native_syscall_handler + .get_execution_info_v2(remaining_gas)?; + + let cheated_data = self + .cheatnet_state + .get_cheated_data(self.native_syscall_handler.base.call.storage_address); + + let block_number = cheated_data + .block_number + .unwrap_or(original_data.block_info.block_number); + let block_timestamp = cheated_data + .block_timestamp + .unwrap_or(original_data.block_info.block_timestamp); + let sequencer_address = cheated_data + .sequencer_address + .map_or(original_data.block_info.sequencer_address, std::convert::Into::into); + + let version = cheated_data + .tx_info + .version + .unwrap_or(original_data.tx_info.version); + let account_contract_address = cheated_data + .tx_info + .account_contract_address + .unwrap_or(original_data.tx_info.account_contract_address); + let max_fee = cheated_data + .tx_info + .max_fee + .map_or(original_data.tx_info.max_fee, |max_fee| { + max_fee.to_u128().unwrap() + }); + let signature = cheated_data + .tx_info + .signature + .unwrap_or(original_data.tx_info.signature); + let transaction_hash = cheated_data + .tx_info + .transaction_hash + .unwrap_or(original_data.tx_info.transaction_hash); + let chain_id = cheated_data + .tx_info + .chain_id + .unwrap_or(original_data.tx_info.chain_id); + let nonce = cheated_data + .tx_info + .nonce + .unwrap_or(original_data.tx_info.nonce); + // TODO impl conversions + let resource_bounds = cheated_data.tx_info.resource_bounds.map_or( + original_data.tx_info.resource_bounds, + |rb| { + rb.iter() + .map(|item| ResourceBounds { + resource: item.resource, + max_amount: item.max_amount, + max_price_per_unit: item.max_price_per_unit, + }) + .collect() + }, + ); + let tip = cheated_data + .tx_info + .tip + .map_or(original_data.tx_info.tip, |tip| tip.to_u128().unwrap()); + let paymaster_data = cheated_data + .tx_info + .paymaster_data + .unwrap_or(original_data.tx_info.paymaster_data); + let nonce_data_availability_mode = cheated_data + .tx_info + .nonce_data_availability_mode + .map_or(original_data.tx_info.nonce_data_availability_mode, |mode| { + mode.to_u32().unwrap() + }); + let fee_data_availability_mode = cheated_data + .tx_info + .fee_data_availability_mode + .map_or(original_data.tx_info.fee_data_availability_mode, |mode| { + mode.to_u32().unwrap() + }); + let account_deployment_data = cheated_data + .tx_info + .account_deployment_data + .unwrap_or(original_data.tx_info.account_deployment_data); + + let caller_address = cheated_data + .caller_address + .map_or(original_data.caller_address, std::convert::Into::into); + let contract_address = cheated_data + .contract_address + .map_or(original_data.contract_address, std::convert::Into::into); + let entry_point_selector = original_data.entry_point_selector; + + Ok(ExecutionInfoV2 { + block_info: BlockInfo { + block_number, + block_timestamp, + sequencer_address, + }, + tx_info: TxV2Info { + version, + account_contract_address, + max_fee, + signature, + transaction_hash, + chain_id, + nonce, + resource_bounds, + tip, + paymaster_data, + nonce_data_availability_mode, + fee_data_availability_mode, + account_deployment_data, + }, + caller_address, + contract_address, + entry_point_selector, + }) + } + + fn deploy( + &mut self, + class_hash: Felt, + contract_address_salt: Felt, + calldata: &[Felt], + deploy_from_zero: bool, + remaining_gas: &mut u64, + ) -> SyscallResult<(Felt, Vec)> { + self.native_syscall_handler.deploy( + class_hash, + contract_address_salt, + calldata, + deploy_from_zero, + remaining_gas, + ) + } + + fn replace_class(&mut self, class_hash: Felt, remaining_gas: &mut u64) -> SyscallResult<()> { + self.native_syscall_handler + .replace_class(class_hash, remaining_gas) + } + + fn library_call( + &mut self, + class_hash: Felt, + function_selector: Felt, + calldata: &[Felt], + remaining_gas: &mut u64, + ) -> SyscallResult> { + self.native_syscall_handler.library_call( + class_hash, + function_selector, + calldata, + remaining_gas, + ) + } + + fn call_contract( + &mut self, + address: Felt, + entry_point_selector: Felt, + calldata: &[Felt], + remaining_gas: &mut u64, + ) -> SyscallResult> { + self.native_syscall_handler.call_contract( + address, + entry_point_selector, + calldata, + remaining_gas, + ) + } + + fn storage_read( + &mut self, + address_domain: u32, + address: Felt, + remaining_gas: &mut u64, + ) -> SyscallResult { + self.native_syscall_handler + .storage_read(address_domain, address, remaining_gas) + } + + fn storage_write( + &mut self, + address_domain: u32, + address: Felt, + value: Felt, + remaining_gas: &mut u64, + ) -> SyscallResult<()> { + self.native_syscall_handler + .storage_write(address_domain, address, value, remaining_gas) + } + + fn emit_event( + &mut self, + keys: &[Felt], + data: &[Felt], + remaining_gas: &mut u64, + ) -> SyscallResult<()> { + self.native_syscall_handler + .emit_event(keys, data, remaining_gas) + } + + fn send_message_to_l1( + &mut self, + to_address: Felt, + payload: &[Felt], + remaining_gas: &mut u64, + ) -> SyscallResult<()> { + self.native_syscall_handler + .send_message_to_l1(to_address, payload, remaining_gas) + } + + fn keccak(&mut self, input: &[u64], remaining_gas: &mut u64) -> SyscallResult { + self.native_syscall_handler.keccak(input, remaining_gas) + } + + fn secp256k1_new( + &mut self, + x: U256, + y: U256, + remaining_gas: &mut u64, + ) -> SyscallResult> { + self.native_syscall_handler + .secp256k1_new(x, y, remaining_gas) + } + + fn secp256k1_add( + &mut self, + p0: Secp256k1Point, + p1: Secp256k1Point, + remaining_gas: &mut u64, + ) -> SyscallResult { + self.native_syscall_handler + .secp256k1_add(p0, p1, remaining_gas) + } + + fn secp256k1_mul( + &mut self, + p: Secp256k1Point, + m: U256, + remaining_gas: &mut u64, + ) -> SyscallResult { + self.native_syscall_handler + .secp256k1_mul(p, m, remaining_gas) + } + + fn secp256k1_get_point_from_x( + &mut self, + x: U256, + y_parity: bool, + remaining_gas: &mut u64, + ) -> SyscallResult> { + self.native_syscall_handler + .secp256k1_get_point_from_x(x, y_parity, remaining_gas) + } + + fn secp256k1_get_xy( + &mut self, + p: Secp256k1Point, + remaining_gas: &mut u64, + ) -> SyscallResult<(U256, U256)> { + self.native_syscall_handler + .secp256k1_get_xy(p, remaining_gas) + } + + fn secp256r1_new( + &mut self, + x: U256, + y: U256, + remaining_gas: &mut u64, + ) -> SyscallResult> { + self.native_syscall_handler + .secp256r1_new(x, y, remaining_gas) + } + + fn secp256r1_add( + &mut self, + p0: Secp256r1Point, + p1: Secp256r1Point, + remaining_gas: &mut u64, + ) -> SyscallResult { + self.native_syscall_handler + .secp256r1_add(p0, p1, remaining_gas) + } + + fn secp256r1_mul( + &mut self, + p: Secp256r1Point, + m: U256, + remaining_gas: &mut u64, + ) -> SyscallResult { + self.native_syscall_handler + .secp256r1_mul(p, m, remaining_gas) + } + + fn secp256r1_get_point_from_x( + &mut self, + x: U256, + y_parity: bool, + remaining_gas: &mut u64, + ) -> SyscallResult> { + self.native_syscall_handler + .secp256r1_get_point_from_x(x, y_parity, remaining_gas) + } + + fn secp256r1_get_xy( + &mut self, + p: Secp256r1Point, + remaining_gas: &mut u64, + ) -> SyscallResult<(U256, U256)> { + self.native_syscall_handler + .secp256r1_get_xy(p, remaining_gas) + } + + fn sha256_process_block( + &mut self, + state: &mut [u32; 8], + block: &[u32; 16], + remaining_gas: &mut u64, + ) -> SyscallResult<()> { + self.native_syscall_handler + .sha256_process_block(state, block, remaining_gas) + } + + fn get_class_hash_at( + &mut self, + contract_address: Felt, + remaining_gas: &mut u64, + ) -> SyscallResult { + self.native_syscall_handler + .get_class_hash_at(contract_address, remaining_gas) + } + + fn meta_tx_v0( + &mut self, + address: Felt, + entry_point_selector: Felt, + calldata: &[Felt], + signature: &[Felt], + remaining_gas: &mut u64, + ) -> SyscallResult> { + self.native_syscall_handler.meta_tx_v0( + address, + entry_point_selector, + calldata, + signature, + remaining_gas, + ) + } +} diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 1fc14b428f..9b3761d052 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -71,7 +71,6 @@ fn simple_package_with_cheats() { ); } - #[test] fn simple_package_with_git_dependency() { let temp = setup_package("simple_package"); From 900d3b9c0c76de0fc0f5ef376617ce9aed10d598 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 17:35:10 +0200 Subject: [PATCH 05/45] Formatting --- .../native/native_syscall_handler.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs index 923bb55519..5a2eedb49e 100644 --- a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs +++ b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs @@ -1,14 +1,22 @@ use crate::state::CheatnetState; +use blockifier::execution::common_hints::ExecutionMode; +use blockifier::execution::entry_point::{CallEntryPoint, CallType}; use blockifier::execution::native::syscall_handler::NativeSyscallHandler; -use blockifier::execution::syscalls::hint_processor::{SyscallExecutionError, OUT_OF_GAS_ERROR}; -use blockifier::execution::syscalls::vm_syscall_utils::SyscallSelector; +use blockifier::execution::syscalls::hint_processor::{OUT_OF_GAS_ERROR, SyscallExecutionError}; +use blockifier::execution::syscalls::vm_syscall_utils::{ + SyscallExecutorBaseError, SyscallSelector, +}; use cairo_native::starknet::{ BlockInfo, ExecutionInfo, ExecutionInfoV2, ResourceBounds, Secp256k1Point, Secp256r1Point, StarknetSyscallHandler, SyscallResult, TxV2Info, U256, }; use num_traits::ToPrimitive; +use starknet_api::contract_class::EntryPointType; +use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector}; use starknet_api::execution_resources::GasAmount; +use starknet_api::transaction::fields::Calldata; use starknet_types_core::felt::Felt; +use std::sync::Arc; pub struct CheatableNativeSyscallHandler<'a> { pub native_syscall_handler: &'a mut NativeSyscallHandler<'a>, @@ -123,9 +131,10 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { let block_timestamp = cheated_data .block_timestamp .unwrap_or(original_data.block_info.block_timestamp); - let sequencer_address = cheated_data - .sequencer_address - .map_or(original_data.block_info.sequencer_address, std::convert::Into::into); + let sequencer_address = cheated_data.sequencer_address.map_or( + original_data.block_info.sequencer_address, + std::convert::Into::into, + ); let version = cheated_data .tx_info From 1d5dbf9a2fa14cefd48fd331e71564a597143030 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 17:55:32 +0200 Subject: [PATCH 06/45] Add test for nested cheated calls --- .../simple_package_with_cheats/src/lib.cairo | 31 +++++++++++++++++++ .../tests/contract.cairo | 27 +++++++++++++++- crates/forge/tests/e2e/running.rs | 6 ++-- 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo b/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo index adb5cd79fa..7699e08a59 100644 --- a/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo +++ b/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo @@ -5,6 +5,37 @@ pub trait IHelloStarknet { fn get_block_number(self: @TContractState) -> u64; } +#[starknet::interface] +pub trait IHelloStarknetProxy { + fn get_block_number(self: @TContractState) -> u64; +} + +#[starknet::contract] +pub mod HelloStarknetProxy { + use starknet::ContractAddress; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + use crate::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; + + #[storage] + struct Storage { + address: ContractAddress, + } + + #[constructor] + fn constructor(ref self: ContractState, address: ContractAddress) { + self.address.write(address); + } + + #[abi(embed_v0)] + impl IHelloStarknetProxyImpl of super::IHelloStarknetProxy { + fn get_block_number(self: @ContractState) -> u64 { + let address = self.address.read(); + let proxied = IHelloStarknetDispatcher { contract_address: address }; + proxied.get_block_number() + } + } +} + #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; diff --git a/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo b/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo index eb047c9cf3..b1ff77b9b6 100644 --- a/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo +++ b/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo @@ -1,8 +1,12 @@ use core::array::ArrayTrait; use core::result::ResultTrait; -use simple_package_with_cheats::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; +use simple_package_with_cheats::{ + IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait, IHelloStarknetProxyDispatcher, + IHelloStarknetProxyDispatcherTrait, +}; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; use snforge_std::{ContractClassTrait, declare, start_cheat_block_number_global}; +use starknet::contract_address; #[test] fn call_and_invoke() { @@ -21,3 +25,24 @@ fn call_and_invoke() { let block_number = dispatcher.get_block_number(); assert(block_number == 123, 'block_info == 123'); } + +#[test] +fn call_and_invoke_proxy() { + let contract = declare("HelloStarknet").unwrap().contract_class(); + let constructor_calldata = @ArrayTrait::new(); + let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); + + let proxy_contract = declare("HelloStarknetProxy").unwrap().contract_class(); + let mut constructor_calldata = ArrayTrait::new(); + contract_address.serialize(ref constructor_calldata); + let (proxy_contract_address, _) = proxy_contract.deploy(@constructor_calldata).unwrap(); + let dispatcher = IHelloStarknetProxyDispatcher { contract_address: proxy_contract_address }; + + let block_number = dispatcher.get_block_number(); + assert(block_number == 2000, 'block_number == 2000'); + + start_cheat_block_number_global(123); + + let block_number = dispatcher.get_block_number(); + assert(block_number == 123, 'block_number == 123'); +} diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 9b3761d052..7a2f60762a 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -63,10 +63,12 @@ fn simple_package_with_cheats() { [..]Finished[..] - Collected 1 test(s) from simple_package_with_cheats package + Collected 2 test(s) from simple_package_with_cheats package Running 0 test(s) from src/ - Running 1 test(s) from tests/ + Running 2 test(s) from tests/ [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke [..] + [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_proxy [..] + Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } From aa1ce72ffcb374bdd9d39ad941a86121205c95ed Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 18:10:10 +0200 Subject: [PATCH 07/45] TODO initial support for `call_contract` --- .../native/native_syscall_handler.rs | 205 +++++++++++++++++- 1 file changed, 198 insertions(+), 7 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs index 5a2eedb49e..8c5e213c16 100644 --- a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs +++ b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs @@ -1,10 +1,17 @@ +use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::execute_call_entry_point; use crate::state::CheatnetState; +use blockifier::execution::call_info::{CallInfo, Retdata}; use blockifier::execution::common_hints::ExecutionMode; use blockifier::execution::entry_point::{CallEntryPoint, CallType}; +use blockifier::execution::errors::EntryPointExecutionError; +use blockifier::execution::execution_utils::update_remaining_gas; use blockifier::execution::native::syscall_handler::NativeSyscallHandler; -use blockifier::execution::syscalls::hint_processor::{OUT_OF_GAS_ERROR, SyscallExecutionError}; +use blockifier::execution::syscalls::hint_processor::{ + ENTRYPOINT_FAILED_ERROR, OUT_OF_GAS_ERROR, SyscallExecutionError, +}; +use blockifier::execution::syscalls::syscall_base::SyscallHandlerBase; use blockifier::execution::syscalls::vm_syscall_utils::{ - SyscallExecutorBaseError, SyscallSelector, + SelfOrRevert, SyscallExecutorBaseError, SyscallSelector, TryExtractRevert, }; use cairo_native::starknet::{ BlockInfo, ExecutionInfo, ExecutionInfoV2, ResourceBounds, Secp256k1Point, Secp256r1Point, @@ -23,6 +30,63 @@ pub struct CheatableNativeSyscallHandler<'a> { pub cheatnet_state: &'a mut CheatnetState, } +pub type BaseSyscallResult = Result; + +#[allow(clippy::result_large_err)] +pub fn execute_inner_call( + syscall_handler_base: &mut SyscallHandlerBase, + cheatnet_state: &mut CheatnetState, + call: &mut CallEntryPoint, + remaining_gas: &mut u64, +) -> BaseSyscallResult> { + let revert_idx = syscall_handler_base.context.revert_infos.0.len(); + + // region: Modified blockifier code + let call_info = execute_call_entry_point( + call, + syscall_handler_base.state, + cheatnet_state, + syscall_handler_base.context, + true, + )?; + // TODO not sure if to keep it + update_remaining_gas(remaining_gas, &call_info); + // endregion + + let mut raw_retdata = call_info.execution.retdata.0.clone(); + let failed = call_info.execution.failed; + syscall_handler_base.inner_calls.push(call_info); + if failed { + syscall_handler_base + .context + .revert(revert_idx, syscall_handler_base.state)?; + + // Delete events and l2_to_l1_messages from the reverted call. + let reverted_call = &mut syscall_handler_base.inner_calls.last_mut().unwrap(); + let mut stack: Vec<&mut CallInfo> = vec![reverted_call]; + while let Some(call_info) = stack.pop() { + call_info.execution.events.clear(); + call_info.execution.l2_to_l1_messages.clear(); + // Add inner calls that did not fail to the stack. + // The events and l2_to_l1_messages of the failed calls were already cleared. + stack.extend( + call_info + .inner_calls + .iter_mut() + .filter(|call_info| !call_info.execution.failed), + ); + } + + raw_retdata + .push(Felt::from_hex(ENTRYPOINT_FAILED_ERROR).map_err(SyscallExecutionError::from)?); + return Err(SyscallExecutionError::Revert { + error_data: raw_retdata, + }); + } + + Ok(raw_retdata) +} + impl CheatableNativeSyscallHandler<'_> { // TODO consider modifying this so it doesn't use take pub fn unrecoverable_error(&mut self) -> Option { @@ -88,6 +152,73 @@ impl CheatableNativeSyscallHandler<'_> { Ok(()) } + + #[allow(clippy::result_large_err)] + fn execute_inner_call( + &mut self, + entry_point: &mut CallEntryPoint, + remaining_gas: &mut u64, + class_hash: ClassHash, + error_wrapper_fn: impl Fn( + SyscallExecutionError, + ClassHash, + ContractAddress, + EntryPointSelector, + ) -> SyscallExecutionError, + ) -> SyscallResult { + let entry_point_clone = entry_point.clone(); + let raw_data = execute_inner_call( + &mut self.native_syscall_handler.base, + &mut self.cheatnet_state, + entry_point, + remaining_gas, + ) + .map_err(|e| { + self.handle_error( + remaining_gas, + SyscallExecutionError::from_self_or_revert(e.try_extract_revert().map_original( + |error| { + error_wrapper_fn( + error, + class_hash, + entry_point_clone.storage_address, + entry_point_clone.entry_point_selector, + ) + }, + )), + ) + })?; + Ok(Retdata(raw_data)) + } + + fn handle_error(&mut self, remaining_gas: &mut u64, error: SyscallExecutionError) -> Vec { + // In case of more than one inner call and because each inner call has their own + // syscall handler, if there is an unrecoverable error at call `n` it will create a + // `NativeExecutionError`. When rolling back, each call from `n-1` to `1` will also + // store the result of a previous `NativeExecutionError` in a `NativeExecutionError` + // creating multiple wraps around the same error. This function is meant to prevent that. + fn unwrap_native_error(error: SyscallExecutionError) -> SyscallExecutionError { + match error { + SyscallExecutionError::EntryPointExecutionError( + EntryPointExecutionError::NativeUnrecoverableError(e), + ) => *e, + _ => error, + } + } + + match error.try_extract_revert() { + SelfOrRevert::Revert(revert_error) => revert_error.error_data, + SelfOrRevert::Original(error) => { + assert!( + self.native_syscall_handler.unrecoverable_error.is_none(), + "Trying to set an unrecoverable error twice in Native Syscall Handler" + ); + self.native_syscall_handler.unrecoverable_error = Some(unwrap_native_error(error)); + *remaining_gas = 0; + vec![] + } + } + } } impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { @@ -283,12 +414,72 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { calldata: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult> { - self.native_syscall_handler.call_contract( - address, - entry_point_selector, - calldata, + self.pre_execute_syscall( remaining_gas, - ) + self.native_syscall_handler + .gas_costs() + .syscalls + .call_contract + .base_syscall_cost(), + SyscallSelector::CallContract, + )?; + + let contract_address = ContractAddress::try_from(address) + .map_err(|error| self.handle_error(remaining_gas, error.into()))?; + + let class_hash = self + .native_syscall_handler + .base + .state + .get_class_hash_at(contract_address) + .map_err(|e| self.handle_error(remaining_gas, e.into()))?; + if self.native_syscall_handler.base.context.execution_mode == ExecutionMode::Validate + && self.native_syscall_handler.base.call.storage_address != contract_address + { + let err = SyscallExecutorBaseError::InvalidSyscallInExecutionMode { + syscall_name: "call_contract".to_string(), + execution_mode: self.native_syscall_handler.base.context.execution_mode, + }; + return Err(self.handle_error(remaining_gas, err.into())); + } + let selector = EntryPointSelector(entry_point_selector); + // TODO restore blocking + // self + // .native_syscall_handler + // .base + // .maybe_block_direct_execute_call(selector) + // .map_err(|e| self.handle_error(remaining_gas, e))?; + + let wrapper_calldata = Calldata(Arc::new(calldata.to_vec())); + + let mut entry_point = CallEntryPoint { + class_hash: None, + code_address: Some(contract_address), + entry_point_type: EntryPointType::External, + entry_point_selector: selector, + calldata: wrapper_calldata, + storage_address: contract_address, + caller_address: self.native_syscall_handler.base.call.storage_address, + call_type: CallType::Call, + initial_gas: *remaining_gas, + }; + + let error_wrapper_function = + |e: SyscallExecutionError, + class_hash: ClassHash, + storage_address: ContractAddress, + selector: EntryPointSelector| { + e.as_call_contract_execution_error(class_hash, storage_address, selector) + }; + + Ok(self + .execute_inner_call( + &mut entry_point, + remaining_gas, + class_hash, + error_wrapper_function, + )? + .0) } fn storage_read( From e95edf1349b8cbfc21031eec99986f8adcc5d531 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 18:56:47 +0200 Subject: [PATCH 08/45] Add tests for library call --- .../simple_package_with_cheats/src/lib.cairo | 13 +++++++++++- .../tests/contract.cairo | 21 +++++++++++++++++++ crates/forge/tests/e2e/running.rs | 7 ++++--- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo b/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo index 7699e08a59..b6451206f7 100644 --- a/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo +++ b/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo @@ -8,13 +8,17 @@ pub trait IHelloStarknet { #[starknet::interface] pub trait IHelloStarknetProxy { fn get_block_number(self: @TContractState) -> u64; + fn get_block_number_library_call(self: @TContractState) -> u64; } #[starknet::contract] pub mod HelloStarknetProxy { use starknet::ContractAddress; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; - use crate::{IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait}; + use starknet::syscalls::get_class_hash_at_syscall; + use crate::{ + IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait, IHelloStarknetLibraryDispatcher, + }; #[storage] struct Storage { @@ -33,6 +37,13 @@ pub mod HelloStarknetProxy { let proxied = IHelloStarknetDispatcher { contract_address: address }; proxied.get_block_number() } + + fn get_block_number_library_call(self: @ContractState) -> u64 { + let address = self.address.read(); + let class_hash = get_class_hash_at_syscall(address).unwrap(); + let library_dispatcher = IHelloStarknetLibraryDispatcher { class_hash }; + library_dispatcher.get_block_number() + } } } diff --git a/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo b/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo index b1ff77b9b6..3192793d1e 100644 --- a/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo +++ b/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo @@ -46,3 +46,24 @@ fn call_and_invoke_proxy() { let block_number = dispatcher.get_block_number(); assert(block_number == 123, 'block_number == 123'); } + +#[test] +fn call_and_invoke_library_call() { + let contract = declare("HelloStarknet").unwrap().contract_class(); + let constructor_calldata = @ArrayTrait::new(); + let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); + + let proxy_contract = declare("HelloStarknetProxy").unwrap().contract_class(); + let mut constructor_calldata = ArrayTrait::new(); + contract_address.serialize(ref constructor_calldata); + let (proxy_contract_address, _) = proxy_contract.deploy(@constructor_calldata).unwrap(); + let dispatcher = IHelloStarknetProxyDispatcher { contract_address: proxy_contract_address }; + + let block_number = dispatcher.get_block_number_library_call(); + assert(block_number == 2000, 'block_number == 2000'); + + start_cheat_block_number_global(123); + + let block_number = dispatcher.get_block_number_library_call(); + assert(block_number == 123, 'block_number == 123'); +} diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 7a2f60762a..6844f29d3a 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -63,12 +63,13 @@ fn simple_package_with_cheats() { [..]Finished[..] - Collected 2 test(s) from simple_package_with_cheats package + Collected 3 test(s) from simple_package_with_cheats package Running 0 test(s) from src/ - Running 2 test(s) from tests/ + Running 3 test(s) from tests/ [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke [..] [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_proxy [..] - Tests: 2 passed, 0 failed, 0 ignored, 0 filtered out + [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_library_call [..] + Tests: 3 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } From 90f62dc6fd30bc184deb695922b53ed0473568df Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 19:01:07 +0200 Subject: [PATCH 09/45] Initial support for `library_call` --- .../native/native_syscall_handler.rs | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs index 8c5e213c16..e6e54d2a01 100644 --- a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs +++ b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs @@ -399,12 +399,51 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { calldata: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult> { - self.native_syscall_handler.library_call( - class_hash, - function_selector, - calldata, + self.pre_execute_syscall( remaining_gas, - ) + self.native_syscall_handler + .gas_costs() + .syscalls + .library_call + .base_syscall_cost(), + SyscallSelector::LibraryCall, + )?; + + let class_hash = ClassHash(class_hash); + + let wrapper_calldata = Calldata(Arc::new(calldata.to_vec())); + + let selector = EntryPointSelector(function_selector); + + let mut entry_point = CallEntryPoint { + class_hash: Some(class_hash), + code_address: None, + entry_point_type: EntryPointType::External, + entry_point_selector: selector, + calldata: wrapper_calldata, + // The call context remains the same in a library call. + storage_address: self.native_syscall_handler.base.call.storage_address, + caller_address: self.native_syscall_handler.base.call.caller_address, + call_type: CallType::Delegate, + initial_gas: *remaining_gas, + }; + + let error_wrapper_function = + |e: SyscallExecutionError, + class_hash: ClassHash, + storage_address: ContractAddress, + selector: EntryPointSelector| { + e.as_lib_call_execution_error(class_hash, storage_address, selector) + }; + + Ok(self + .execute_inner_call( + &mut entry_point, + remaining_gas, + class_hash, + error_wrapper_function, + )? + .0) } fn call_contract( From 251569bac59172ab03500c47da7c7d2418b4e767 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 19:23:41 +0200 Subject: [PATCH 10/45] Add tests for deploy_syscall --- .../simple_package_with_cheats/src/lib.cairo | 45 ++++++++++++++++++- .../tests/contract.cairo | 30 ++++++++++++- crates/forge/tests/e2e/running.rs | 7 +-- 3 files changed, 76 insertions(+), 6 deletions(-) diff --git a/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo b/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo index b6451206f7..cbe3df41ab 100644 --- a/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo +++ b/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo @@ -1,3 +1,5 @@ +use starknet::{ClassHash, ContractAddress}; + #[starknet::interface] pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); @@ -5,17 +7,49 @@ pub trait IHelloStarknet { fn get_block_number(self: @TContractState) -> u64; } +#[starknet::interface] +pub trait ICheatedConstructor { + fn get_stored_block_number(self: @TContractState) -> u64; +} + +#[starknet::contract] +pub mod CheatedConstructor { + use starknet::get_block_number; + use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + + #[storage] + struct Storage { + block_number: u64, + } + + #[constructor] + fn constructor(ref self: ContractState) { + let block_number = get_block_number(); + self.block_number.write(block_number); + } + + #[abi(embed_v0)] + impl ICheatedConstructorImpl of super::ICheatedConstructor { + fn get_stored_block_number(self: @ContractState) -> u64 { + self.block_number.read() + } + } +} + #[starknet::interface] pub trait IHelloStarknetProxy { fn get_block_number(self: @TContractState) -> u64; fn get_block_number_library_call(self: @TContractState) -> u64; + fn deploy_cheated_constructor_contract( + ref self: TContractState, class_hash: ClassHash, salt: felt252, + ) -> ContractAddress; } #[starknet::contract] pub mod HelloStarknetProxy { - use starknet::ContractAddress; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; - use starknet::syscalls::get_class_hash_at_syscall; + use starknet::syscalls::{deploy_syscall, get_class_hash_at_syscall}; + use starknet::{ClassHash, ContractAddress}; use crate::{ IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait, IHelloStarknetLibraryDispatcher, }; @@ -44,6 +78,13 @@ pub mod HelloStarknetProxy { let library_dispatcher = IHelloStarknetLibraryDispatcher { class_hash }; library_dispatcher.get_block_number() } + + fn deploy_cheated_constructor_contract( + ref self: ContractState, class_hash: ClassHash, salt: felt252, + ) -> ContractAddress { + let (address, _) = deploy_syscall(class_hash, salt, array![].span(), false).unwrap(); + address + } } } diff --git a/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo b/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo index 3192793d1e..58fecd109d 100644 --- a/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo +++ b/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo @@ -1,7 +1,8 @@ use core::array::ArrayTrait; use core::result::ResultTrait; use simple_package_with_cheats::{ - IHelloStarknetDispatcher, IHelloStarknetDispatcherTrait, IHelloStarknetProxyDispatcher, + ICheatedConstructorDispatcher, ICheatedConstructorDispatcherTrait, IHelloStarknetDispatcher, + IHelloStarknetDispatcherTrait, IHelloStarknetProxyDispatcher, IHelloStarknetProxyDispatcherTrait, }; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; @@ -67,3 +68,30 @@ fn call_and_invoke_library_call() { let block_number = dispatcher.get_block_number_library_call(); assert(block_number == 123, 'block_number == 123'); } + +#[test] +fn deploy_syscall() { + let contract = declare("HelloStarknet").unwrap().contract_class(); + let constructor_calldata = @ArrayTrait::new(); + let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); + + let proxy_contract = declare("HelloStarknetProxy").unwrap().contract_class(); + let mut constructor_calldata = ArrayTrait::new(); + contract_address.serialize(ref constructor_calldata); + let (proxy_contract_address, _) = proxy_contract.deploy(@constructor_calldata).unwrap(); + let dispatcher = IHelloStarknetProxyDispatcher { contract_address: proxy_contract_address }; + + let class_hash = declare("CheatedConstructor").unwrap().contract_class().class_hash; + + let contract_address = dispatcher.deploy_cheated_constructor_contract(*class_hash, 111); + let cheated_constructor_dispatcher = ICheatedConstructorDispatcher { contract_address }; + let block_number = cheated_constructor_dispatcher.get_stored_block_number(); + assert(block_number == 2000, 'block_number == 2000'); + + start_cheat_block_number_global(123); + + let contract_address = dispatcher.deploy_cheated_constructor_contract(*class_hash, 222); + let cheated_constructor_dispatcher = ICheatedConstructorDispatcher { contract_address }; + let block_number = cheated_constructor_dispatcher.get_stored_block_number(); + assert(block_number == 123, 'block_number == 123'); +} diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 6844f29d3a..f89c62d549 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -63,13 +63,14 @@ fn simple_package_with_cheats() { [..]Finished[..] - Collected 3 test(s) from simple_package_with_cheats package + Collected 4 test(s) from simple_package_with_cheats package Running 0 test(s) from src/ - Running 3 test(s) from tests/ + Running 4 test(s) from tests/ [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke [..] [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_proxy [..] [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_library_call [..] - Tests: 3 passed, 0 failed, 0 ignored, 0 filtered out + [PASS] simple_package_with_cheats_integrationtest::contract::deploy_syscall [..] + Tests: 4 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } From ec42c35bce239dec22f5bd67c65f6bb72dda768b Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 20:04:10 +0200 Subject: [PATCH 11/45] Support deploy syscall --- .../execution/cheated_syscalls.rs | 4 +- .../native/native_syscall_handler.rs | 191 +++++++++++++++++- 2 files changed, 184 insertions(+), 11 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cheated_syscalls.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cheated_syscalls.rs index b48434c8b4..cb9b1d34ba 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cheated_syscalls.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/cheated_syscalls.rs @@ -85,9 +85,7 @@ pub fn deploy_syscall( request.constructor_calldata.0.len(), ); - // region: Modified blockifier code - let deployer_address = syscall_handler.storage_address(); - // endregion + let deployer_address = syscall_handler.base.call.storage_address; let deployer_address_for_calculation = if request.deploy_from_zero { ContractAddress::default() } else { diff --git a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs index e6e54d2a01..753d558d0c 100644 --- a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs +++ b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs @@ -2,8 +2,13 @@ use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution:: use crate::state::CheatnetState; use blockifier::execution::call_info::{CallInfo, Retdata}; use blockifier::execution::common_hints::ExecutionMode; -use blockifier::execution::entry_point::{CallEntryPoint, CallType}; -use blockifier::execution::errors::EntryPointExecutionError; +use blockifier::execution::entry_point::{ + CallEntryPoint, CallType, ConstructorContext, ConstructorEntryPointExecutionResult, + EntryPointExecutionContext, handle_empty_constructor, +}; +use blockifier::execution::errors::{ + ConstructorEntryPointExecutionError, EntryPointExecutionError, +}; use blockifier::execution::execution_utils::update_remaining_gas; use blockifier::execution::native::syscall_handler::NativeSyscallHandler; use blockifier::execution::syscalls::hint_processor::{ @@ -13,15 +18,20 @@ use blockifier::execution::syscalls::syscall_base::SyscallHandlerBase; use blockifier::execution::syscalls::vm_syscall_utils::{ SelfOrRevert, SyscallExecutorBaseError, SyscallSelector, TryExtractRevert, }; +use blockifier::state::errors::StateError; +use blockifier::state::state_api::State; +use blockifier::utils::u64_from_usize; use cairo_native::starknet::{ BlockInfo, ExecutionInfo, ExecutionInfoV2, ResourceBounds, Secp256k1Point, Secp256r1Point, StarknetSyscallHandler, SyscallResult, TxV2Info, U256, }; use num_traits::ToPrimitive; use starknet_api::contract_class::EntryPointType; -use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector}; +use starknet_api::core::{ + ClassHash, ContractAddress, EntryPointSelector, calculate_contract_address, +}; use starknet_api::execution_resources::GasAmount; -use starknet_api::transaction::fields::Calldata; +use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; use starknet_types_core::felt::Felt; use std::sync::Arc; @@ -87,6 +97,152 @@ pub fn execute_inner_call( Ok(raw_retdata) } +#[allow(clippy::result_large_err)] +pub fn execute_constructor_entry_point( + state: &mut dyn State, + cheatnet_state: &mut CheatnetState, + context: &mut EntryPointExecutionContext, + ctor_context: ConstructorContext, + calldata: Calldata, + remaining_gas: &mut u64, +) -> ConstructorEntryPointExecutionResult { + // Ensure the class is declared (by reading it). + let compiled_class = state + .get_compiled_class(ctor_context.class_hash) + .map_err(|error| { + ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None) + })?; + let Some(constructor_selector) = compiled_class.constructor_selector() else { + // Contract has no constructor. + return handle_empty_constructor( + compiled_class, + context, + &ctor_context, + calldata, + *remaining_gas, + ) + .map_err(|error| ConstructorEntryPointExecutionError::new(error, &ctor_context, None)); + }; + + let mut constructor_call = CallEntryPoint { + class_hash: None, + code_address: ctor_context.code_address, + entry_point_type: EntryPointType::Constructor, + entry_point_selector: constructor_selector, + calldata, + storage_address: ctor_context.storage_address, + caller_address: ctor_context.caller_address, + call_type: CallType::Call, + initial_gas: *remaining_gas, + }; + + let call_info = + execute_call_entry_point(&mut constructor_call, state, cheatnet_state, context, false) + .map_err(|error| { + ConstructorEntryPointExecutionError::new( + error, + &ctor_context, + Some(constructor_selector), + ) + })?; + + Ok(call_info) +} + +pub fn execute_deployment( + state: &mut dyn State, + cheatnet_state: &mut CheatnetState, + context: &mut EntryPointExecutionContext, + ctor_context: ConstructorContext, + constructor_calldata: Calldata, + remaining_gas: &mut u64, +) -> ConstructorEntryPointExecutionResult { + // Address allocation in the state is done before calling the constructor, so that it is + // visible from it. + let deployed_contract_address = ctor_context.storage_address; + let current_class_hash = + state + .get_class_hash_at(deployed_contract_address) + .map_err(|error| { + ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None) + })?; + if current_class_hash != ClassHash::default() { + return Err(ConstructorEntryPointExecutionError::new( + StateError::UnavailableContractAddress(deployed_contract_address).into(), + &ctor_context, + None, + )); + } + + state + .set_class_hash_at(deployed_contract_address, ctor_context.class_hash) + .map_err(|error| { + ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None) + })?; + + execute_constructor_entry_point( + state, + cheatnet_state, + context, + ctor_context, + constructor_calldata, + remaining_gas, + ) +} + +fn deploy( + syscall_handler_base: &mut SyscallHandlerBase, + cheatnet_state: &mut CheatnetState, + class_hash: ClassHash, + contract_address_salt: ContractAddressSalt, + constructor_calldata: Calldata, + deploy_from_zero: bool, + remaining_gas: &mut u64, +) -> BaseSyscallResult<(ContractAddress, CallInfo)> { + syscall_handler_base + .increment_syscall_linear_factor_by(&SyscallSelector::Deploy, constructor_calldata.0.len()); + // let versioned_constants = &syscall_handler_base + // .context + // .tx_context + // .block_context + // .versioned_constants; + // TODO support for reject + // if should_reject_deploy( + // versioned_constants.disable_deploy_in_validation_mode, + // syscall_handler_base.context.execution_mode, + // ) { + // syscall_handler_base.reject_syscall_in_validate_mode("deploy")?; + // } + + let deployer_address = syscall_handler_base.call.storage_address; + let deployer_address_for_calculation = match deploy_from_zero { + true => ContractAddress::default(), + false => deployer_address, + }; + let deployed_contract_address = calculate_contract_address( + contract_address_salt, + class_hash, + &constructor_calldata, + deployer_address_for_calculation, + )?; + + let ctor_context = ConstructorContext { + class_hash, + code_address: Some(deployed_contract_address), + storage_address: deployed_contract_address, + caller_address: deployer_address, + }; + let call_info = execute_deployment( + syscall_handler_base.state, + cheatnet_state, + syscall_handler_base.context, + ctor_context, + constructor_calldata, + remaining_gas, + )?; + Ok((deployed_contract_address, call_info)) +} + impl CheatableNativeSyscallHandler<'_> { // TODO consider modifying this so it doesn't use take pub fn unrecoverable_error(&mut self) -> Option { @@ -378,13 +534,32 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { deploy_from_zero: bool, remaining_gas: &mut u64, ) -> SyscallResult<(Felt, Vec)> { - self.native_syscall_handler.deploy( - class_hash, - contract_address_salt, - calldata, + // The cost of deploying a contract is the base cost plus the linear cost of the calldata + // len. + let total_gas_cost = self + .native_syscall_handler + .gas_costs() + .syscalls + .deploy + .get_syscall_cost(u64_from_usize(calldata.len())); + + self.pre_execute_syscall(remaining_gas, total_gas_cost, SyscallSelector::Deploy)?; + + let (deployed_contract_address, call_info) = deploy( + &mut self.native_syscall_handler.base, + &mut self.cheatnet_state, + ClassHash(class_hash), + ContractAddressSalt(contract_address_salt), + Calldata(Arc::new(calldata.to_vec())), deploy_from_zero, remaining_gas, ) + .map_err(|err| self.handle_error(remaining_gas, err))?; + + let constructor_retdata = call_info.execution.retdata.0[..].to_vec(); + self.native_syscall_handler.base.inner_calls.push(call_info); + + Ok((Felt::from(deployed_contract_address), constructor_retdata)) } fn replace_class(&mut self, class_hash: Felt, remaining_gas: &mut u64) -> SyscallResult<()> { From 487de3360a5db9e14b266ffd33cda4d8874f1297 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 20:31:53 +0200 Subject: [PATCH 12/45] Add test for get_block_hash syscall --- .../simple_package_with_cheats/src/lib.cairo | 8 +++++++- .../tests/contract.cairo | 20 ++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo b/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo index cbe3df41ab..47f2454d28 100644 --- a/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo +++ b/crates/forge/tests/data/simple_package_with_cheats/src/lib.cairo @@ -5,6 +5,7 @@ pub trait IHelloStarknet { fn increase_balance(ref self: TContractState, amount: felt252); fn get_balance(self: @TContractState) -> felt252; fn get_block_number(self: @TContractState) -> u64; + fn get_block_hash(self: @TContractState) -> felt252; } #[starknet::interface] @@ -91,8 +92,9 @@ pub mod HelloStarknetProxy { #[starknet::contract] pub mod HelloStarknet { use core::array::ArrayTrait; - use starknet::get_block_number; use starknet::storage::{StoragePointerReadAccess, StoragePointerWriteAccess}; + use starknet::syscalls::get_block_hash_syscall; + use starknet::{SyscallResultTrait, get_block_number}; #[storage] struct Storage { @@ -114,5 +116,9 @@ pub mod HelloStarknet { fn get_block_number(self: @ContractState) -> u64 { get_block_number() } + + fn get_block_hash(self: @ContractState) -> felt252 { + get_block_hash_syscall(100).unwrap_syscall() + } } } diff --git a/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo b/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo index 58fecd109d..c06ac40a6c 100644 --- a/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo +++ b/crates/forge/tests/data/simple_package_with_cheats/tests/contract.cairo @@ -6,7 +6,9 @@ use simple_package_with_cheats::{ IHelloStarknetProxyDispatcherTrait, }; use snforge_std::cheatcodes::contract_class::DeclareResultTrait; -use snforge_std::{ContractClassTrait, declare, start_cheat_block_number_global}; +use snforge_std::{ + ContractClassTrait, declare, start_cheat_block_hash_global, start_cheat_block_number_global, +}; use starknet::contract_address; #[test] @@ -95,3 +97,19 @@ fn deploy_syscall() { let block_number = cheated_constructor_dispatcher.get_stored_block_number(); assert(block_number == 123, 'block_number == 123'); } + +#[test] +fn block_hash() { + let contract = declare("HelloStarknet").unwrap().contract_class(); + let constructor_calldata = @ArrayTrait::new(); + let (contract_address, _) = contract.deploy(constructor_calldata).unwrap(); + let dispatcher = IHelloStarknetDispatcher { contract_address }; + + let block_hash = dispatcher.get_block_hash(); + assert(block_hash == 0, 'bloch_hash == 0'); + + start_cheat_block_hash_global(100, 111); + + let block_hash = dispatcher.get_block_hash(); + assert(block_hash == 111, 'bloch_hash == 111'); +} From c05406e6ebea4ce2a84d44b953cbd5e3c26bded9 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 20:32:24 +0200 Subject: [PATCH 13/45] Support get_block_hash syscall --- .../cheatcodes/cheat_block_hash.rs | 32 +++++++++++++------ .../native/native_syscall_handler.rs | 29 +++++++++++++++-- crates/forge/tests/e2e/running.rs | 7 ++-- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/cheat_block_hash.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/cheat_block_hash.rs index 2d497cd16f..990ae9672e 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/cheat_block_hash.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/cheat_block_hash.rs @@ -78,13 +78,11 @@ impl CheatnetState { self.cheat_block_hash(block_number, Operation::StopGlobal); } - #[expect(clippy::result_large_err)] - pub fn get_block_hash_for_contract( + pub fn get_cheated_block_hash_for_contract( &mut self, contract_address: ContractAddress, block_number: u64, - syscall_handler: &mut SyscallHintProcessor, - ) -> SyscallResult { + ) -> Option { if let Some((cheat_span, block_hash)) = self .block_hash_contracts .get(&(contract_address, block_number)) @@ -110,17 +108,33 @@ impl CheatnetState { } CheatSpan::Indefinite => {} } - return Ok(BlockHash(StarkHash::from(block_hash))); + return Some(BlockHash(StarkHash::from(block_hash))); } if let Some((block_hash, except)) = self.global_block_hash.get(&block_number) && !except.contains(&contract_address) { - return Ok(BlockHash(StarkHash::from(*block_hash))); + return Some(BlockHash(StarkHash::from(*block_hash))); } - Ok(BlockHash( - syscall_handler.base.get_block_hash(block_number)?, - )) + None + } + + #[expect(clippy::result_large_err)] + pub fn get_block_hash_for_contract( + &mut self, + contract_address: ContractAddress, + block_number: u64, + syscall_handler: &mut SyscallHintProcessor, + ) -> SyscallResult { + let cheated_block_hash = + self.get_cheated_block_hash_for_contract(contract_address, block_number); + if let Some(cheated_block_hash) = cheated_block_hash { + Ok(cheated_block_hash) + } else { + Ok(BlockHash( + syscall_handler.base.get_block_hash(block_number)?, + )) + } } } diff --git a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs index 753d558d0c..7f05cfd585 100644 --- a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs +++ b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs @@ -383,8 +383,33 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { block_number: u64, remaining_gas: &mut u64, ) -> SyscallResult { - self.native_syscall_handler - .get_block_hash(block_number, remaining_gas) + self.pre_execute_syscall( + remaining_gas, + self.native_syscall_handler + .gas_costs() + .syscalls + .get_block_hash + .base_syscall_cost(), + SyscallSelector::GetBlockHash, + )?; + + let block_hash = self.cheatnet_state.get_cheated_block_hash_for_contract( + self.native_syscall_handler.base.call.storage_address, + block_number, + ); + + if let Some(block_hash) = block_hash { + Ok(block_hash.0) + } else { + match self + .native_syscall_handler + .base + .get_block_hash(block_number) + { + Ok(value) => Ok(value), + Err(e) => Err(self.handle_error(remaining_gas, e)), + } + } } fn get_execution_info(&mut self, remaining_gas: &mut u64) -> SyscallResult { diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index f89c62d549..b80cc5f928 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -63,14 +63,15 @@ fn simple_package_with_cheats() { [..]Finished[..] - Collected 4 test(s) from simple_package_with_cheats package + Collected 5 test(s) from simple_package_with_cheats package Running 0 test(s) from src/ - Running 4 test(s) from tests/ + Running 5 test(s) from tests/ [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke [..] [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_proxy [..] [PASS] simple_package_with_cheats_integrationtest::contract::call_and_invoke_library_call [..] [PASS] simple_package_with_cheats_integrationtest::contract::deploy_syscall [..] - Tests: 4 passed, 0 failed, 0 ignored, 0 filtered out + [PASS] simple_package_with_cheats_integrationtest::contract::block_hash [..] + Tests: 5 passed, 0 failed, 0 ignored, 0 filtered out "}, ); } From 1aebb1a2e06aae0ebafbe806bbf171da51d59434 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 20:39:28 +0200 Subject: [PATCH 14/45] Move code to dedicated modules --- .../src/runtime_extensions/native/call.rs | 64 +++++ .../src/runtime_extensions/native/deploy.rs | 162 +++++++++++++ .../src/runtime_extensions/native/mod.rs | 2 + .../native/native_syscall_handler.rs | 227 +----------------- 4 files changed, 235 insertions(+), 220 deletions(-) create mode 100644 crates/cheatnet/src/runtime_extensions/native/call.rs create mode 100644 crates/cheatnet/src/runtime_extensions/native/deploy.rs diff --git a/crates/cheatnet/src/runtime_extensions/native/call.rs b/crates/cheatnet/src/runtime_extensions/native/call.rs new file mode 100644 index 0000000000..f795480a57 --- /dev/null +++ b/crates/cheatnet/src/runtime_extensions/native/call.rs @@ -0,0 +1,64 @@ +use blockifier::execution::call_info::CallInfo; +use blockifier::execution::entry_point::CallEntryPoint; +use blockifier::execution::execution_utils::update_remaining_gas; +use blockifier::execution::syscalls::hint_processor::{SyscallExecutionError, ENTRYPOINT_FAILED_ERROR}; +use blockifier::execution::syscalls::syscall_base::SyscallHandlerBase; +use starknet_types_core::felt::Felt; +use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::execute_call_entry_point; +use crate::runtime_extensions::native::native_syscall_handler::BaseSyscallResult; +use crate::state::CheatnetState; + +#[allow(clippy::result_large_err)] +pub fn execute_inner_call( + syscall_handler_base: &mut SyscallHandlerBase, + cheatnet_state: &mut CheatnetState, + call: &mut CallEntryPoint, + remaining_gas: &mut u64, +) -> BaseSyscallResult> { + let revert_idx = syscall_handler_base.context.revert_infos.0.len(); + + // region: Modified blockifier code + let call_info = execute_call_entry_point( + call, + syscall_handler_base.state, + cheatnet_state, + syscall_handler_base.context, + true, + )?; + // TODO not sure if to keep it + update_remaining_gas(remaining_gas, &call_info); + // endregion + + let mut raw_retdata = call_info.execution.retdata.0.clone(); + let failed = call_info.execution.failed; + syscall_handler_base.inner_calls.push(call_info); + if failed { + syscall_handler_base + .context + .revert(revert_idx, syscall_handler_base.state)?; + + // Delete events and l2_to_l1_messages from the reverted call. + let reverted_call = &mut syscall_handler_base.inner_calls.last_mut().unwrap(); + let mut stack: Vec<&mut CallInfo> = vec![reverted_call]; + while let Some(call_info) = stack.pop() { + call_info.execution.events.clear(); + call_info.execution.l2_to_l1_messages.clear(); + // Add inner calls that did not fail to the stack. + // The events and l2_to_l1_messages of the failed calls were already cleared. + stack.extend( + call_info + .inner_calls + .iter_mut() + .filter(|call_info| !call_info.execution.failed), + ); + } + + raw_retdata + .push(Felt::from_hex(ENTRYPOINT_FAILED_ERROR).map_err(SyscallExecutionError::from)?); + return Err(SyscallExecutionError::Revert { + error_data: raw_retdata, + }); + } + + Ok(raw_retdata) +} \ No newline at end of file diff --git a/crates/cheatnet/src/runtime_extensions/native/deploy.rs b/crates/cheatnet/src/runtime_extensions/native/deploy.rs new file mode 100644 index 0000000000..dd2ba65a69 --- /dev/null +++ b/crates/cheatnet/src/runtime_extensions/native/deploy.rs @@ -0,0 +1,162 @@ +use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::execute_call_entry_point; +use crate::runtime_extensions::native::native_syscall_handler::BaseSyscallResult; +use crate::state::CheatnetState; +use blockifier::execution::call_info::CallInfo; +use blockifier::execution::entry_point::{ + CallEntryPoint, CallType, ConstructorContext, ConstructorEntryPointExecutionResult, + EntryPointExecutionContext, handle_empty_constructor, +}; +use blockifier::execution::errors::ConstructorEntryPointExecutionError; +use blockifier::execution::syscalls::syscall_base::SyscallHandlerBase; +use blockifier::execution::syscalls::vm_syscall_utils::SyscallSelector; +use blockifier::state::errors::StateError; +use blockifier::state::state_api::State; +use starknet_api::contract_class::EntryPointType; +use starknet_api::core::{ClassHash, ContractAddress, calculate_contract_address}; +use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; + +#[allow(clippy::result_large_err)] +pub fn execute_constructor_entry_point( + state: &mut dyn State, + cheatnet_state: &mut CheatnetState, + context: &mut EntryPointExecutionContext, + ctor_context: ConstructorContext, + calldata: Calldata, + remaining_gas: &mut u64, +) -> ConstructorEntryPointExecutionResult { + // Ensure the class is declared (by reading it). + let compiled_class = state + .get_compiled_class(ctor_context.class_hash) + .map_err(|error| { + ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None) + })?; + let Some(constructor_selector) = compiled_class.constructor_selector() else { + // Contract has no constructor. + return handle_empty_constructor( + compiled_class, + context, + &ctor_context, + calldata, + *remaining_gas, + ) + .map_err(|error| ConstructorEntryPointExecutionError::new(error, &ctor_context, None)); + }; + + let mut constructor_call = CallEntryPoint { + class_hash: None, + code_address: ctor_context.code_address, + entry_point_type: EntryPointType::Constructor, + entry_point_selector: constructor_selector, + calldata, + storage_address: ctor_context.storage_address, + caller_address: ctor_context.caller_address, + call_type: CallType::Call, + initial_gas: *remaining_gas, + }; + + let call_info = + execute_call_entry_point(&mut constructor_call, state, cheatnet_state, context, false) + .map_err(|error| { + ConstructorEntryPointExecutionError::new( + error, + &ctor_context, + Some(constructor_selector), + ) + })?; + + Ok(call_info) +} + +fn execute_deployment( + state: &mut dyn State, + cheatnet_state: &mut CheatnetState, + context: &mut EntryPointExecutionContext, + ctor_context: ConstructorContext, + constructor_calldata: Calldata, + remaining_gas: &mut u64, +) -> ConstructorEntryPointExecutionResult { + // Address allocation in the state is done before calling the constructor, so that it is + // visible from it. + let deployed_contract_address = ctor_context.storage_address; + let current_class_hash = + state + .get_class_hash_at(deployed_contract_address) + .map_err(|error| { + ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None) + })?; + if current_class_hash != ClassHash::default() { + return Err(ConstructorEntryPointExecutionError::new( + StateError::UnavailableContractAddress(deployed_contract_address).into(), + &ctor_context, + None, + )); + } + + state + .set_class_hash_at(deployed_contract_address, ctor_context.class_hash) + .map_err(|error| { + ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None) + })?; + + execute_constructor_entry_point( + state, + cheatnet_state, + context, + ctor_context, + constructor_calldata, + remaining_gas, + ) +} + +pub fn deploy( + syscall_handler_base: &mut SyscallHandlerBase, + cheatnet_state: &mut CheatnetState, + class_hash: ClassHash, + contract_address_salt: ContractAddressSalt, + constructor_calldata: Calldata, + deploy_from_zero: bool, + remaining_gas: &mut u64, +) -> BaseSyscallResult<(ContractAddress, CallInfo)> { + syscall_handler_base + .increment_syscall_linear_factor_by(&SyscallSelector::Deploy, constructor_calldata.0.len()); + // let versioned_constants = &syscall_handler_base + // .context + // .tx_context + // .block_context + // .versioned_constants; + // TODO support for reject + // if should_reject_deploy( + // versioned_constants.disable_deploy_in_validation_mode, + // syscall_handler_base.context.execution_mode, + // ) { + // syscall_handler_base.reject_syscall_in_validate_mode("deploy")?; + // } + + let deployer_address = syscall_handler_base.call.storage_address; + let deployer_address_for_calculation = match deploy_from_zero { + true => ContractAddress::default(), + false => deployer_address, + }; + let deployed_contract_address = calculate_contract_address( + contract_address_salt, + class_hash, + &constructor_calldata, + deployer_address_for_calculation, + )?; + + let ctor_context = ConstructorContext { + class_hash, + code_address: Some(deployed_contract_address), + storage_address: deployed_contract_address, + caller_address: deployer_address, + }; + let call_info = execute_deployment( + syscall_handler_base.state, + cheatnet_state, + syscall_handler_base.context, + ctor_context, + constructor_calldata, + remaining_gas, + )?; + Ok((deployed_contract_address, call_info)) +} diff --git a/crates/cheatnet/src/runtime_extensions/native/mod.rs b/crates/cheatnet/src/runtime_extensions/native/mod.rs index 2c7f77e86c..dd0149cfa6 100644 --- a/crates/cheatnet/src/runtime_extensions/native/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/native/mod.rs @@ -1,2 +1,4 @@ pub mod execution; pub mod native_syscall_handler; +mod deploy; +mod call; diff --git a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs index 7f05cfd585..a5dd651e07 100644 --- a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs +++ b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs @@ -1,25 +1,15 @@ -use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::execute_call_entry_point; +use crate::runtime_extensions::native::call::execute_inner_call; +use crate::runtime_extensions::native::deploy::deploy; use crate::state::CheatnetState; -use blockifier::execution::call_info::{CallInfo, Retdata}; +use blockifier::execution::call_info::Retdata; use blockifier::execution::common_hints::ExecutionMode; -use blockifier::execution::entry_point::{ - CallEntryPoint, CallType, ConstructorContext, ConstructorEntryPointExecutionResult, - EntryPointExecutionContext, handle_empty_constructor, -}; -use blockifier::execution::errors::{ - ConstructorEntryPointExecutionError, EntryPointExecutionError, -}; -use blockifier::execution::execution_utils::update_remaining_gas; +use blockifier::execution::entry_point::{CallEntryPoint, CallType}; +use blockifier::execution::errors::EntryPointExecutionError; use blockifier::execution::native::syscall_handler::NativeSyscallHandler; -use blockifier::execution::syscalls::hint_processor::{ - ENTRYPOINT_FAILED_ERROR, OUT_OF_GAS_ERROR, SyscallExecutionError, -}; -use blockifier::execution::syscalls::syscall_base::SyscallHandlerBase; +use blockifier::execution::syscalls::hint_processor::{OUT_OF_GAS_ERROR, SyscallExecutionError}; use blockifier::execution::syscalls::vm_syscall_utils::{ SelfOrRevert, SyscallExecutorBaseError, SyscallSelector, TryExtractRevert, }; -use blockifier::state::errors::StateError; -use blockifier::state::state_api::State; use blockifier::utils::u64_from_usize; use cairo_native::starknet::{ BlockInfo, ExecutionInfo, ExecutionInfoV2, ResourceBounds, Secp256k1Point, Secp256r1Point, @@ -27,9 +17,7 @@ use cairo_native::starknet::{ }; use num_traits::ToPrimitive; use starknet_api::contract_class::EntryPointType; -use starknet_api::core::{ - ClassHash, ContractAddress, EntryPointSelector, calculate_contract_address, -}; +use starknet_api::core::{ClassHash, ContractAddress, EntryPointSelector}; use starknet_api::execution_resources::GasAmount; use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; use starknet_types_core::felt::Felt; @@ -42,207 +30,6 @@ pub struct CheatableNativeSyscallHandler<'a> { pub type BaseSyscallResult = Result; -#[allow(clippy::result_large_err)] -pub fn execute_inner_call( - syscall_handler_base: &mut SyscallHandlerBase, - cheatnet_state: &mut CheatnetState, - call: &mut CallEntryPoint, - remaining_gas: &mut u64, -) -> BaseSyscallResult> { - let revert_idx = syscall_handler_base.context.revert_infos.0.len(); - - // region: Modified blockifier code - let call_info = execute_call_entry_point( - call, - syscall_handler_base.state, - cheatnet_state, - syscall_handler_base.context, - true, - )?; - // TODO not sure if to keep it - update_remaining_gas(remaining_gas, &call_info); - // endregion - - let mut raw_retdata = call_info.execution.retdata.0.clone(); - let failed = call_info.execution.failed; - syscall_handler_base.inner_calls.push(call_info); - if failed { - syscall_handler_base - .context - .revert(revert_idx, syscall_handler_base.state)?; - - // Delete events and l2_to_l1_messages from the reverted call. - let reverted_call = &mut syscall_handler_base.inner_calls.last_mut().unwrap(); - let mut stack: Vec<&mut CallInfo> = vec![reverted_call]; - while let Some(call_info) = stack.pop() { - call_info.execution.events.clear(); - call_info.execution.l2_to_l1_messages.clear(); - // Add inner calls that did not fail to the stack. - // The events and l2_to_l1_messages of the failed calls were already cleared. - stack.extend( - call_info - .inner_calls - .iter_mut() - .filter(|call_info| !call_info.execution.failed), - ); - } - - raw_retdata - .push(Felt::from_hex(ENTRYPOINT_FAILED_ERROR).map_err(SyscallExecutionError::from)?); - return Err(SyscallExecutionError::Revert { - error_data: raw_retdata, - }); - } - - Ok(raw_retdata) -} - -#[allow(clippy::result_large_err)] -pub fn execute_constructor_entry_point( - state: &mut dyn State, - cheatnet_state: &mut CheatnetState, - context: &mut EntryPointExecutionContext, - ctor_context: ConstructorContext, - calldata: Calldata, - remaining_gas: &mut u64, -) -> ConstructorEntryPointExecutionResult { - // Ensure the class is declared (by reading it). - let compiled_class = state - .get_compiled_class(ctor_context.class_hash) - .map_err(|error| { - ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None) - })?; - let Some(constructor_selector) = compiled_class.constructor_selector() else { - // Contract has no constructor. - return handle_empty_constructor( - compiled_class, - context, - &ctor_context, - calldata, - *remaining_gas, - ) - .map_err(|error| ConstructorEntryPointExecutionError::new(error, &ctor_context, None)); - }; - - let mut constructor_call = CallEntryPoint { - class_hash: None, - code_address: ctor_context.code_address, - entry_point_type: EntryPointType::Constructor, - entry_point_selector: constructor_selector, - calldata, - storage_address: ctor_context.storage_address, - caller_address: ctor_context.caller_address, - call_type: CallType::Call, - initial_gas: *remaining_gas, - }; - - let call_info = - execute_call_entry_point(&mut constructor_call, state, cheatnet_state, context, false) - .map_err(|error| { - ConstructorEntryPointExecutionError::new( - error, - &ctor_context, - Some(constructor_selector), - ) - })?; - - Ok(call_info) -} - -pub fn execute_deployment( - state: &mut dyn State, - cheatnet_state: &mut CheatnetState, - context: &mut EntryPointExecutionContext, - ctor_context: ConstructorContext, - constructor_calldata: Calldata, - remaining_gas: &mut u64, -) -> ConstructorEntryPointExecutionResult { - // Address allocation in the state is done before calling the constructor, so that it is - // visible from it. - let deployed_contract_address = ctor_context.storage_address; - let current_class_hash = - state - .get_class_hash_at(deployed_contract_address) - .map_err(|error| { - ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None) - })?; - if current_class_hash != ClassHash::default() { - return Err(ConstructorEntryPointExecutionError::new( - StateError::UnavailableContractAddress(deployed_contract_address).into(), - &ctor_context, - None, - )); - } - - state - .set_class_hash_at(deployed_contract_address, ctor_context.class_hash) - .map_err(|error| { - ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None) - })?; - - execute_constructor_entry_point( - state, - cheatnet_state, - context, - ctor_context, - constructor_calldata, - remaining_gas, - ) -} - -fn deploy( - syscall_handler_base: &mut SyscallHandlerBase, - cheatnet_state: &mut CheatnetState, - class_hash: ClassHash, - contract_address_salt: ContractAddressSalt, - constructor_calldata: Calldata, - deploy_from_zero: bool, - remaining_gas: &mut u64, -) -> BaseSyscallResult<(ContractAddress, CallInfo)> { - syscall_handler_base - .increment_syscall_linear_factor_by(&SyscallSelector::Deploy, constructor_calldata.0.len()); - // let versioned_constants = &syscall_handler_base - // .context - // .tx_context - // .block_context - // .versioned_constants; - // TODO support for reject - // if should_reject_deploy( - // versioned_constants.disable_deploy_in_validation_mode, - // syscall_handler_base.context.execution_mode, - // ) { - // syscall_handler_base.reject_syscall_in_validate_mode("deploy")?; - // } - - let deployer_address = syscall_handler_base.call.storage_address; - let deployer_address_for_calculation = match deploy_from_zero { - true => ContractAddress::default(), - false => deployer_address, - }; - let deployed_contract_address = calculate_contract_address( - contract_address_salt, - class_hash, - &constructor_calldata, - deployer_address_for_calculation, - )?; - - let ctor_context = ConstructorContext { - class_hash, - code_address: Some(deployed_contract_address), - storage_address: deployed_contract_address, - caller_address: deployer_address, - }; - let call_info = execute_deployment( - syscall_handler_base.state, - cheatnet_state, - syscall_handler_base.context, - ctor_context, - constructor_calldata, - remaining_gas, - )?; - Ok((deployed_contract_address, call_info)) -} - impl CheatableNativeSyscallHandler<'_> { // TODO consider modifying this so it doesn't use take pub fn unrecoverable_error(&mut self) -> Option { From 91151e04481906657476b8e0ce15bfefeec82120 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 21:10:43 +0200 Subject: [PATCH 15/45] Some lints --- .../execution/entry_point.rs | 4 ++-- .../src/runtime_extensions/native/call.rs | 13 ++++++++----- .../src/runtime_extensions/native/deploy.rs | 12 ++++++++++++ .../src/runtime_extensions/native/execution.rs | 15 +++++++++------ .../cheatnet/src/runtime_extensions/native/mod.rs | 4 ++-- .../native/native_syscall_handler.rs | 9 +++++++-- 6 files changed, 40 insertions(+), 17 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs index 9a12544412..2f5e504b54 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs @@ -189,8 +189,8 @@ pub fn execute_call_entry_point( // } else { println!("Executing native entry point"); execute_entry_point_call_native( - entry_point.clone(), - native_compiled_class_v1, + &entry_point, + &native_compiled_class_v1, state, cheatnet_state, context, diff --git a/crates/cheatnet/src/runtime_extensions/native/call.rs b/crates/cheatnet/src/runtime_extensions/native/call.rs index f795480a57..5e59741fac 100644 --- a/crates/cheatnet/src/runtime_extensions/native/call.rs +++ b/crates/cheatnet/src/runtime_extensions/native/call.rs @@ -1,13 +1,16 @@ +use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::execute_call_entry_point; +use crate::runtime_extensions::native::native_syscall_handler::BaseSyscallResult; +use crate::state::CheatnetState; use blockifier::execution::call_info::CallInfo; use blockifier::execution::entry_point::CallEntryPoint; use blockifier::execution::execution_utils::update_remaining_gas; -use blockifier::execution::syscalls::hint_processor::{SyscallExecutionError, ENTRYPOINT_FAILED_ERROR}; +use blockifier::execution::syscalls::hint_processor::{ + ENTRYPOINT_FAILED_ERROR, SyscallExecutionError, +}; use blockifier::execution::syscalls::syscall_base::SyscallHandlerBase; use starknet_types_core::felt::Felt; -use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::execute_call_entry_point; -use crate::runtime_extensions::native::native_syscall_handler::BaseSyscallResult; -use crate::state::CheatnetState; +#[expect(clippy::mut_mut)] #[allow(clippy::result_large_err)] pub fn execute_inner_call( syscall_handler_base: &mut SyscallHandlerBase, @@ -61,4 +64,4 @@ pub fn execute_inner_call( } Ok(raw_retdata) -} \ No newline at end of file +} diff --git a/crates/cheatnet/src/runtime_extensions/native/deploy.rs b/crates/cheatnet/src/runtime_extensions/native/deploy.rs index dd2ba65a69..b1be659d20 100644 --- a/crates/cheatnet/src/runtime_extensions/native/deploy.rs +++ b/crates/cheatnet/src/runtime_extensions/native/deploy.rs @@ -15,10 +15,14 @@ use starknet_api::contract_class::EntryPointType; use starknet_api::core::{ClassHash, ContractAddress, calculate_contract_address}; use starknet_api::transaction::fields::{Calldata, ContractAddressSalt}; +// Copied from blockifer/src/execution/entry_point.rs #[allow(clippy::result_large_err)] +#[expect(clippy::needless_pass_by_value)] pub fn execute_constructor_entry_point( state: &mut dyn State, + // region: Modified blockifer code cheatnet_state: &mut CheatnetState, + // endregion context: &mut EntryPointExecutionContext, ctor_context: ConstructorContext, calldata: Calldata, @@ -54,6 +58,7 @@ pub fn execute_constructor_entry_point( initial_gas: *remaining_gas, }; + // region: Modified blockifer code let call_info = execute_call_entry_point(&mut constructor_call, state, cheatnet_state, context, false) .map_err(|error| { @@ -65,8 +70,11 @@ pub fn execute_constructor_entry_point( })?; Ok(call_info) + // endregion } +#[expect(clippy::result_large_err)] +// Copied from blockifer/src/execution/execution_utils.rs fn execute_deployment( state: &mut dyn State, cheatnet_state: &mut CheatnetState, @@ -108,6 +116,8 @@ fn execute_deployment( ) } +#[expect(clippy::match_bool, clippy::result_large_err)] +// Copied from blockifer/src/execution/syscalls/syscall_base.rs pub fn deploy( syscall_handler_base: &mut SyscallHandlerBase, cheatnet_state: &mut CheatnetState, @@ -150,6 +160,7 @@ pub fn deploy( storage_address: deployed_contract_address, caller_address: deployer_address, }; + // region: Modified blockifer code let call_info = execute_deployment( syscall_handler_base.state, cheatnet_state, @@ -158,5 +169,6 @@ pub fn deploy( constructor_calldata, remaining_gas, )?; + // endregion Ok((deployed_contract_address, call_info)) } diff --git a/crates/cheatnet/src/runtime_extensions/native/execution.rs b/crates/cheatnet/src/runtime_extensions/native/execution.rs index e2270a7761..04445ba1ea 100644 --- a/crates/cheatnet/src/runtime_extensions/native/execution.rs +++ b/crates/cheatnet/src/runtime_extensions/native/execution.rs @@ -19,11 +19,13 @@ use blockifier::utils::add_maps; use cairo_native::execution_result::{BuiltinStats, ContractExecutionResult}; use cairo_native::utils::BuiltinCosts; use cairo_vm::types::builtin_name::BuiltinName; +use std::collections::HashMap; use std::default::Default; +#[expect(clippy::result_large_err)] pub(crate) fn execute_entry_point_call_native( - call: ExecutableCallEntryPoint, - native_compiled_class_v1: NativeCompiledClassV1, + call: &ExecutableCallEntryPoint, + native_compiled_class_v1: &NativeCompiledClassV1, state: &mut dyn State, cheatnet_state: &mut CheatnetState, // Added parameter context: &mut EntryPointExecutionContext, @@ -39,8 +41,8 @@ pub(crate) fn execute_entry_point_call_native( Ok(CallInfoWithExecutionData { call_info, - syscall_usage_vm_resources: Default::default(), - syscall_usage_sierra_gas: Default::default(), + syscall_usage_vm_resources: HashMap::default(), + syscall_usage_sierra_gas: HashMap::default(), vm_trace: None, }) } @@ -49,8 +51,8 @@ pub(crate) fn execute_entry_point_call_native( // todo(rodrigo): add an `entry point not found` test for Native #[allow(clippy::result_large_err)] pub fn execute_entry_point_call( - call: ExecutableCallEntryPoint, - compiled_class: NativeCompiledClassV1, + call: &ExecutableCallEntryPoint, + compiled_class: &NativeCompiledClassV1, // state: &mut dyn State, // context: &mut EntryPointExecutionContext, mut syscall_handler: CheatableNativeSyscallHandler, @@ -124,6 +126,7 @@ pub fn execute_entry_point_call( // Copied from blockifier #[allow(clippy::result_large_err)] +#[expect(clippy::needless_pass_by_value)] fn create_callinfo( call_result: ContractExecutionResult, syscall_handler: CheatableNativeSyscallHandler<'_>, diff --git a/crates/cheatnet/src/runtime_extensions/native/mod.rs b/crates/cheatnet/src/runtime_extensions/native/mod.rs index dd0149cfa6..be5ecd4266 100644 --- a/crates/cheatnet/src/runtime_extensions/native/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/native/mod.rs @@ -1,4 +1,4 @@ +mod call; +mod deploy; pub mod execution; pub mod native_syscall_handler; -mod deploy; -mod call; diff --git a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs index a5dd651e07..9387d1cca2 100644 --- a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs +++ b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs @@ -96,6 +96,7 @@ impl CheatableNativeSyscallHandler<'_> { Ok(()) } + // Copied from blockifer/src/exection/native/syscall_handler.rs #[allow(clippy::result_large_err)] fn execute_inner_call( &mut self, @@ -110,9 +111,10 @@ impl CheatableNativeSyscallHandler<'_> { ) -> SyscallExecutionError, ) -> SyscallResult { let entry_point_clone = entry_point.clone(); + // region: Modified blockifier code let raw_data = execute_inner_call( &mut self.native_syscall_handler.base, - &mut self.cheatnet_state, + self.cheatnet_state, entry_point, remaining_gas, ) @@ -131,6 +133,7 @@ impl CheatableNativeSyscallHandler<'_> { )), ) })?; + // endregion Ok(Retdata(raw_data)) } @@ -359,7 +362,7 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { let (deployed_contract_address, call_info) = deploy( &mut self.native_syscall_handler.base, - &mut self.cheatnet_state, + self.cheatnet_state, ClassHash(class_hash), ContractAddressSalt(contract_address_salt), Calldata(Arc::new(calldata.to_vec())), @@ -379,6 +382,7 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { .replace_class(class_hash, remaining_gas) } + // Copied from blockifier/src/execution/native/syscall_handler.rs fn library_call( &mut self, class_hash: Felt, @@ -433,6 +437,7 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { .0) } + // Copied from blockifier/src/execution/native/syscall_handler.rs fn call_contract( &mut self, address: Felt, From abe11728a742d7a31185e87f556e33a6d1d02783 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 25 Aug 2025 21:24:00 +0200 Subject: [PATCH 16/45] Add error handling --- crates/cheatnet/src/runtime_extensions/native/execution.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/native/execution.rs b/crates/cheatnet/src/runtime_extensions/native/execution.rs index 04445ba1ea..e8ce7a6bb6 100644 --- a/crates/cheatnet/src/runtime_extensions/native/execution.rs +++ b/crates/cheatnet/src/runtime_extensions/native/execution.rs @@ -1,5 +1,6 @@ use crate::runtime_extensions::call_to_blockifier_runtime_extension::execution::entry_point::{ CallInfoWithExecutionData, ContractClassEntryPointExecutionResult, + EntryPointExecutionErrorWithTrace, }; use crate::runtime_extensions::native::native_syscall_handler::CheatableNativeSyscallHandler; use crate::state::CheatnetState; @@ -35,9 +36,11 @@ pub(crate) fn execute_entry_point_call_native( native_syscall_handler: &mut NativeSyscallHandler::new(call.clone(), state, context), }; - // TODO error handling let call_info = execute_entry_point_call(call, native_compiled_class_v1, syscall_handler) - .expect("Native execution failed"); + .map_err(|err| EntryPointExecutionErrorWithTrace { + source: err, + trace: None, + })?; Ok(CallInfoWithExecutionData { call_info, From 43f5a76d2978132d3a927a5379bef217ba37b3ff Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 26 Aug 2025 15:40:34 +0200 Subject: [PATCH 17/45] Support events and l1 messages spys --- .../native/native_syscall_handler.rs | 54 +++++++++++++++++-- .../tests/integration/too_many_events.rs | 2 +- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs index 9387d1cca2..843298ee78 100644 --- a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs +++ b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs @@ -1,3 +1,5 @@ +use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_events::Event; +use crate::runtime_extensions::forge_runtime_extension::cheatcodes::spy_messages_to_l1::MessageToL1; use crate::runtime_extensions::native::call::execute_inner_call; use crate::runtime_extensions::native::deploy::deploy; use crate::state::CheatnetState; @@ -540,8 +542,30 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { data: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult<()> { - self.native_syscall_handler - .emit_event(keys, data, remaining_gas) + let syscall_result = self + .native_syscall_handler + .emit_event(keys, data, remaining_gas); + + if syscall_result.is_ok() { + let contract_address = self + .native_syscall_handler + .base + .call + // TODO why we default to code_address?? + .code_address + .unwrap_or(self.native_syscall_handler.base.call.storage_address); + let event = self + .native_syscall_handler + .base + .events + .last() + .expect("Event must have been emitted"); + self.cheatnet_state + .detected_events + .push(Event::from_ordered_event(event, contract_address)); + } + + syscall_result } fn send_message_to_l1( @@ -550,8 +574,30 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { payload: &[Felt], remaining_gas: &mut u64, ) -> SyscallResult<()> { - self.native_syscall_handler - .send_message_to_l1(to_address, payload, remaining_gas) + let syscall_result = + self.native_syscall_handler + .send_message_to_l1(to_address, payload, remaining_gas); + + if syscall_result.is_ok() { + let contract_address = self + .native_syscall_handler + .base + .call + // TODO why we default to code_address?? + .code_address + .unwrap_or(self.native_syscall_handler.base.call.storage_address); + let message = self + .native_syscall_handler + .base + .l2_to_l1_messages + .last() + .expect("Message must have been sent"); + self.cheatnet_state + .detected_messages_to_l1 + .push(MessageToL1::from_ordered_message(message, contract_address)); + } + + syscall_result } fn keccak(&mut self, input: &[u64], remaining_gas: &mut u64) -> SyscallResult { diff --git a/crates/forge/tests/integration/too_many_events.rs b/crates/forge/tests/integration/too_many_events.rs index a349b0ca33..b2e6ac1656 100644 --- a/crates/forge/tests/integration/too_many_events.rs +++ b/crates/forge/tests/integration/too_many_events.rs @@ -140,7 +140,7 @@ fn too_many_events() { &result, "emit_too_many_events", &format!( - "Got an exception while executing a hint: Exceeded the maximum number of events, number events: {emit_too_many_events}, max number events: {max_n_emitted_events}." + "Got an exception while executing a hint: [..] Exceeded the maximum number of events, number events: {emit_too_many_events}, max number events: {max_n_emitted_events}." ), ); assert_case_output_contains( From ad512b6c070c7fda74cb0197b6a95fd523e91631 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 26 Aug 2025 15:52:46 +0200 Subject: [PATCH 18/45] Only run cairo native is tracked resources is sierra gas --- .../execution/entry_point.rs | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs index 2f5e504b54..d9577edf3b 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs @@ -176,26 +176,25 @@ pub fn execute_call_entry_point( context, ), RunnableCompiledClass::V1Native(native_compiled_class_v1) => { - // if context.tracked_resource_stack.last() == Some(&TrackedResource::CairoSteps) { - // // We cannot run native with cairo steps as the tracked resources (it's a vm - // // resouorce). - // panic!("We want to test execution of native entry only"); - // // execute_entry_point_call_cairo1( - // // call, - // // compiled_class.casm(), - // // state, - // // context, - // // ) - // } else { - println!("Executing native entry point"); - execute_entry_point_call_native( - &entry_point, - &native_compiled_class_v1, - state, - cheatnet_state, - context, - ) - } // } + if context.tracked_resource_stack.last() == Some(&TrackedResource::CairoSteps) { + execute_entry_point_call_cairo1( + entry_point.clone(), + &native_compiled_class_v1.casm(), + state, + cheatnet_state, + context, + ) + } else { + println!("Executing native entry point"); + execute_entry_point_call_native( + &entry_point, + &native_compiled_class_v1, + state, + cheatnet_state, + context, + ) + } + } }; context .tracked_resource_stack From ee9d9d95b10a223985ce345b617b2baac138771c Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 26 Aug 2025 20:20:42 +0200 Subject: [PATCH 19/45] Add empty node to trace on deploy without constructor --- crates/cheatnet/src/runtime_extensions/native/deploy.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/cheatnet/src/runtime_extensions/native/deploy.rs b/crates/cheatnet/src/runtime_extensions/native/deploy.rs index b1be659d20..d6e6f4d4ba 100644 --- a/crates/cheatnet/src/runtime_extensions/native/deploy.rs +++ b/crates/cheatnet/src/runtime_extensions/native/deploy.rs @@ -35,6 +35,11 @@ pub fn execute_constructor_entry_point( ConstructorEntryPointExecutionError::new(error.into(), &ctor_context, None) })?; let Some(constructor_selector) = compiled_class.constructor_selector() else { + // region: Modified blockifer code + cheatnet_state + .trace_data + .add_deploy_without_constructor_node(); + // endregion // Contract has no constructor. return handle_empty_constructor( compiled_class, From 71ba13fad61ca3a4a518859b8a146cffed545813 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 26 Aug 2025 20:21:23 +0200 Subject: [PATCH 20/45] Return resources for native execution --- .../src/runtime_extensions/native/execution.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/native/execution.rs b/crates/cheatnet/src/runtime_extensions/native/execution.rs index e8ce7a6bb6..0cf7461f22 100644 --- a/crates/cheatnet/src/runtime_extensions/native/execution.rs +++ b/crates/cheatnet/src/runtime_extensions/native/execution.rs @@ -31,21 +31,25 @@ pub(crate) fn execute_entry_point_call_native( cheatnet_state: &mut CheatnetState, // Added parameter context: &mut EntryPointExecutionContext, ) -> ContractClassEntryPointExecutionResult { - let syscall_handler = CheatableNativeSyscallHandler { + let mut syscall_handler = CheatableNativeSyscallHandler { cheatnet_state, native_syscall_handler: &mut NativeSyscallHandler::new(call.clone(), state, context), }; - let call_info = execute_entry_point_call(call, native_compiled_class_v1, syscall_handler) + let call_info = execute_entry_point_call(call, native_compiled_class_v1, &mut syscall_handler) .map_err(|err| EntryPointExecutionErrorWithTrace { source: err, trace: None, })?; + let syscall_usage = &syscall_handler.native_syscall_handler.base.syscalls_usage; + Ok(CallInfoWithExecutionData { call_info, + // Native execution doesn't support VM resources. + // If we got to this point, it means tracked resources are SierraGas. syscall_usage_vm_resources: HashMap::default(), - syscall_usage_sierra_gas: HashMap::default(), + syscall_usage_sierra_gas: syscall_usage.clone(), vm_trace: None, }) } @@ -58,7 +62,7 @@ pub fn execute_entry_point_call( compiled_class: &NativeCompiledClassV1, // state: &mut dyn State, // context: &mut EntryPointExecutionContext, - mut syscall_handler: CheatableNativeSyscallHandler, + syscall_handler: &mut CheatableNativeSyscallHandler, ) -> EntryPointExecutionResult { let entry_point = compiled_class.get_entry_point(&call.type_and_selector())?; @@ -110,7 +114,7 @@ pub fn execute_entry_point_call( .clone(), call_initial_gas, Some(builtin_costs), - &mut syscall_handler, + &mut *syscall_handler, ); syscall_handler.native_syscall_handler.finalize(); @@ -129,10 +133,9 @@ pub fn execute_entry_point_call( // Copied from blockifier #[allow(clippy::result_large_err)] -#[expect(clippy::needless_pass_by_value)] fn create_callinfo( call_result: ContractExecutionResult, - syscall_handler: CheatableNativeSyscallHandler<'_>, + syscall_handler: &mut CheatableNativeSyscallHandler<'_>, ) -> Result { let remaining_gas = call_result.remaining_gas; From bcafe6eae6142a347bbea1c60c3040f995fc660a Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 26 Aug 2025 20:22:02 +0200 Subject: [PATCH 21/45] Do not run pre_execute_syscall for get_execution_info_v2 --- .../native/native_syscall_handler.rs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs index 843298ee78..399e817a7c 100644 --- a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs +++ b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs @@ -211,15 +211,8 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { #[expect(clippy::too_many_lines)] fn get_execution_info_v2(&mut self, remaining_gas: &mut u64) -> SyscallResult { - self.pre_execute_syscall( - remaining_gas, - self.native_syscall_handler - .gas_costs() - .syscalls - .get_execution_info - .base_syscall_cost(), - SyscallSelector::GetBlockHash, - )?; + // We don't need to call pre_execute_syscall here because the call to `get_execution_info_v2` + // on the native syscall handler is does that internally, and we don't want to do it twice. let original_data = self .native_syscall_handler From eb4a66874f064c207d4a906330b014a55fed0feb Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 26 Aug 2025 20:23:44 +0200 Subject: [PATCH 22/45] Restore message --- crates/forge/tests/integration/too_many_events.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/forge/tests/integration/too_many_events.rs b/crates/forge/tests/integration/too_many_events.rs index b2e6ac1656..abe372e836 100644 --- a/crates/forge/tests/integration/too_many_events.rs +++ b/crates/forge/tests/integration/too_many_events.rs @@ -65,7 +65,7 @@ fn ok_events() { .unwrap() ); - let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); + let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_passed(&result); } @@ -133,14 +133,14 @@ fn too_many_events() { .unwrap() ); - let result = run_test_case(&test, ForgeTrackedResource::CairoSteps); + let result = run_test_case(&test, ForgeTrackedResource::SierraGas); assert_failed(&result); assert_case_output_contains( &result, "emit_too_many_events", &format!( - "Got an exception while executing a hint: [..] Exceeded the maximum number of events, number events: {emit_too_many_events}, max number events: {max_n_emitted_events}." + "Got an exception while executing a hint: Exceeded the maximum number of events, number events: {emit_too_many_events}, max number events: {max_n_emitted_events}." ), ); assert_case_output_contains( From cef970a2b007a3f11f2bcac269d033037e90b2d5 Mon Sep 17 00:00:00 2001 From: ksew1 <95349104+ksew1@users.noreply.github.com> Date: Wed, 3 Sep 2025 17:17:56 +0200 Subject: [PATCH 23/45] Bump blockifier, change deps, make compile (#3713) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Stack**: - #3711 - #3710 - #3709 - #3713 ⬅ ⚠️ *Part of a stack created by [spr](https://github.com/ejoffe/spr). Do not merge manually using the UI - doing so may have unexpected results.* --------- Co-authored-by: Artur Michalek --- Cargo.lock | 11 +++ Cargo.toml | 1 + .../cheatcodes/declare.rs | 56 ++++-------- crates/cheatnet/tests/common/mod.rs | 12 ++- crates/forge/src/lib.rs | 14 ++- .../forge/src/profile_validation/backtrace.rs | 18 +++- crates/forge/src/profile_validation/mod.rs | 2 +- crates/forge/src/run_tests/package.rs | 13 +-- crates/forge/test_utils/src/runner.rs | 27 ++++-- crates/forge/tests/e2e/backtrace.rs | 12 +++ crates/forge/tests/e2e/running.rs | 45 ++++++++++ crates/native-api/Cargo.toml | 9 ++ crates/native-api/src/lib.rs | 40 +++++++++ crates/scarb-api/Cargo.toml | 2 + crates/scarb-api/src/artifacts.rs | 85 ++++++++++++++----- crates/scarb-api/src/lib.rs | 17 +++- crates/sncast/src/helpers/scarb_utils.rs | 10 +-- .../src/starknet_commands/script/run.rs | 1 + docs/src/appendix/snforge/test.md | 6 ++ 19 files changed, 288 insertions(+), 93 deletions(-) create mode 100644 crates/native-api/Cargo.toml create mode 100644 crates/native-api/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index bf02c20130..2fa95901a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4192,6 +4192,15 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "native-api" +version = "0.48.1" +dependencies = [ + "cairo-lang-starknet-classes", + "cairo-native", + "starknet_api", +] + [[package]] name = "native-tls" version = "0.2.14" @@ -5658,10 +5667,12 @@ version = "1.0.0" dependencies = [ "anyhow", "assert_fs", + "cairo-native", "camino", "foundry-ui", "indoc", "itertools 0.14.0", + "native-api", "rayon", "regex", "scarb-metadata", diff --git a/Cargo.toml b/Cargo.toml index ddcae29fbc..44dca4fbd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ members = [ "crates/debugging", "crates/testing/packages_validation", "crates/foundry-ui", + "crates/native-api", ] exclude = ["crates/snforge-scarb-plugin", "crates/snforge-scarb-plugin-deprecated"] diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs index 8844ffadf3..010b96193e 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs @@ -7,12 +7,10 @@ use anyhow::{Context, Result}; use blockifier::execution::contract_class::{CompiledClassV1, RunnableCompiledClass}; use blockifier::execution::native::contract_class::NativeCompiledClassV1; use blockifier::state::{errors::StateError, state_api::State}; -use cairo_lang_starknet_classes::contract_class::ContractClass; -use cairo_native::executor::AotContractExecutor; use conversions::IntoConv; use conversions::serde::serialize::CairoSerialize; +use scarb_api::StarknetContractArtifacts; use starknet::core::types::contract::SierraClass; -use starknet_api::contract_class::SierraVersion; use starknet_api::core::{ClassHash, CompiledClassHash}; #[derive(CairoSerialize)] @@ -31,41 +29,7 @@ pub fn declare( .with_context(|| format!("Failed to get contract artifact for name = {contract_name}.")) .map_err(EnhancedHintError::from)?; - let sierra_contract_class: ContractClass = - serde_json::from_str(&contract_artifact.sierra).unwrap(); - - let sierra_program = sierra_contract_class - .extract_sierra_program() - .expect("Cannot extract sierra program from sierra contract class"); - - let sierra_version_values = sierra_contract_class - .sierra_program - .iter() - .take(3) - .map(|x| x.value.clone()) - .collect::>(); - - let sierra_version = SierraVersion::extract_from_program(&sierra_version_values) - .expect("Cannot extract sierra version from sierra program"); - - let executor = AotContractExecutor::new( - &sierra_program, - &sierra_contract_class.entry_points_by_type, - sierra_version.clone().into(), - cairo_native::OptLevel::Default, - // `stats` - Passing a [cairo_native::statistics::Statistics] object enables collecting - // compilation statistics. - None, - ) - .expect("Cannot compile sierra into native"); - - let contract_class = CompiledClassV1::try_from_json_string( - &contract_artifact.casm, - get_current_sierra_version(), - ) - .expect("Failed to read contract class from json"); - let contract_class = NativeCompiledClassV1::new(executor, contract_class); - let contract_class = RunnableCompiledClass::V1Native(contract_class); + let contract_class = get_contract_class(contract_artifact); let class_hash = *contracts_data .get_class_hash(contract_name) @@ -101,3 +65,19 @@ pub fn declare( pub fn get_class_hash(sierra_class: &SierraClass) -> Result { Ok(sierra_class.class_hash()?.into_()) } + +fn get_contract_class(contract_artifact: &StarknetContractArtifacts) -> RunnableCompiledClass { + let contract_class = CompiledClassV1::try_from_json_string( + &contract_artifact.casm, + get_current_sierra_version(), + ) + .expect("Failed to read contract class from json"); + + match &contract_artifact.executor { + None => RunnableCompiledClass::V1(contract_class), + Some(executor) => RunnableCompiledClass::V1Native(NativeCompiledClassV1::new( + executor.clone(), + contract_class, + )), + } +} diff --git a/crates/cheatnet/tests/common/mod.rs b/crates/cheatnet/tests/common/mod.rs index 6fa825f66b..41431f13c4 100644 --- a/crates/cheatnet/tests/common/mod.rs +++ b/crates/cheatnet/tests/common/mod.rs @@ -29,7 +29,8 @@ use runtime::starknet::constants::TEST_ADDRESS; use runtime::starknet::context::build_context; use scarb_api::metadata::MetadataCommandExt; use scarb_api::{ - ScarbCommand, get_contracts_artifacts_and_source_sierra_paths, target_dir_for_workspace, + CompilationOpts, ScarbCommand, get_contracts_artifacts_and_source_sierra_paths, + target_dir_for_workspace, }; use starknet::core::utils::get_selector_from_name; use starknet_api::contract_class::EntryPointType; @@ -85,8 +86,13 @@ pub fn get_contracts() -> ContractsData { let package = scarb_metadata.packages.first().unwrap(); let ui = UI::default(); - let contracts = - get_contracts_artifacts_and_source_sierra_paths(&target_dir, package, false, &ui).unwrap(); + let contracts = get_contracts_artifacts_and_source_sierra_paths( + &target_dir, + package, + &ui, + CompilationOpts::default(), + ) + .unwrap(); ContractsData::try_from(contracts).unwrap() } diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 8b19cbf8a5..991ab09399 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -142,6 +142,12 @@ pub struct TestArgs { #[command(flatten)] trace_args: TraceArgs, + /// Run contracts on `cairo-native` instead of the default `cairo-vm`. + /// + /// Note: Only contracts execution through native is supported, test code itself will still run on `cairo-vm`. + #[arg(long)] + run_native: bool, + /// Use exact matches for `test_filter` #[arg(short, long)] exact: bool, @@ -169,7 +175,7 @@ pub struct TestArgs { include_ignored: bool, /// Display more detailed info about used resources - #[arg(long)] + #[arg(long, conflicts_with = "run_native")] detailed_resources: bool, /// Control when colored output is used @@ -181,15 +187,15 @@ pub struct TestArgs { rerun_failed: bool, /// Save execution traces of all test which have passed and are not fuzz tests - #[arg(long)] + #[arg(long, conflicts_with = "run_native")] save_trace_data: bool, /// Build profiles of all tests which have passed and are not fuzz tests using the cairo-profiler - #[arg(long, conflicts_with = "coverage")] + #[arg(long, conflicts_with_all = ["run_native", "coverage"])] build_profile: bool, /// Generate a coverage report for the executed tests which have passed and are not fuzz tests using the cairo-coverage - #[arg(long, conflicts_with = "build_profile")] + #[arg(long, conflicts_with_all = ["run_native", "build_profile"])] coverage: bool, /// Number of maximum steps during a single test. For fuzz tests this value is applied to each subtest separately. diff --git a/crates/forge/src/profile_validation/backtrace.rs b/crates/forge/src/profile_validation/backtrace.rs index 35caccd6ec..068647d65d 100644 --- a/crates/forge/src/profile_validation/backtrace.rs +++ b/crates/forge/src/profile_validation/backtrace.rs @@ -1,16 +1,30 @@ +use crate::TestArgs; use crate::profile_validation::{check_cairo_profile_entries, get_manifest}; use anyhow::ensure; use indoc::formatdoc; use scarb_metadata::Metadata; use semver::Version; -/// Checks if backtrace can be generated based on scarb version and profile settings extracted from the provided [`Metadata`]. -pub fn check_backtrace_compatibility(scarb_metadata: &Metadata) -> anyhow::Result<()> { +/// Checks if backtrace can be generated based on scarb version, profile settings extracted from +/// the provided [`Metadata`] and if native execution is disabled in the provided [`TestArgs`]. +pub fn check_backtrace_compatibility( + test_args: &TestArgs, + scarb_metadata: &Metadata, +) -> anyhow::Result<()> { + check_if_native_disabled(test_args)?; check_scarb_version(scarb_metadata)?; check_profile(scarb_metadata)?; Ok(()) } +/// Checks if native execution is disabled in the provided [`TestArgs`]. +fn check_if_native_disabled(test_args: &TestArgs) -> anyhow::Result<()> { + ensure!( + !test_args.run_native, + "Backtrace generation is not supported with `cairo-native` execution", + ); + Ok(()) +} /// Checks if the scarb version from the provided [`Metadata`] is greater than or equal to the minimal required version. fn check_scarb_version(scarb_metadata: &Metadata) -> anyhow::Result<()> { const MINIMAL_SCARB_VERSION: Version = Version::new(2, 8, 0); diff --git a/crates/forge/src/profile_validation/mod.rs b/crates/forge/src/profile_validation/mod.rs index bd31218165..ce7780bcf5 100644 --- a/crates/forge/src/profile_validation/mod.rs +++ b/crates/forge/src/profile_validation/mod.rs @@ -18,7 +18,7 @@ pub fn check_profile_compatibility( check_coverage_compatibility(scarb_metadata)?; } if is_backtrace_enabled() { - check_backtrace_compatibility(scarb_metadata)?; + check_backtrace_compatibility(test_args, scarb_metadata)?; } Ok(()) } diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index c41248491f..39344a27ff 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -31,7 +31,7 @@ use forge_runner::{ test_target_summary::TestTargetSummary, }; use foundry_ui::{UI, components::labeled::LabeledMessage}; -use scarb_api::get_contracts_artifacts_and_source_sierra_paths; +use scarb_api::{CompilationOpts, get_contracts_artifacts_and_source_sierra_paths}; use scarb_metadata::{Metadata, PackageMetadata}; use std::sync::Arc; @@ -83,11 +83,14 @@ impl RunForPackageArgs { let contracts = get_contracts_artifacts_and_source_sierra_paths( artifacts_dir, &package, - !should_compile_starknet_contract_target( - &scarb_metadata.app_version_info.version, - args.no_optimization, - ), ui, + CompilationOpts { + use_test_target_contracts: !should_compile_starknet_contract_target( + &scarb_metadata.app_version_info.version, + args.no_optimization, + ), + run_native: args.run_native, + }, )?; let contracts_data = ContractsData::try_from(contracts)?; diff --git a/crates/forge/test_utils/src/runner.rs b/crates/forge/test_utils/src/runner.rs index 5c06f6f612..3232b8431a 100644 --- a/crates/forge/test_utils/src/runner.rs +++ b/crates/forge/test_utils/src/runner.rs @@ -17,8 +17,9 @@ use forge_runner::{ use foundry_ui::UI; use indoc::formatdoc; use scarb_api::{ - ScarbCommand, StarknetContractArtifacts, get_contracts_artifacts_and_source_sierra_paths, - metadata::MetadataCommandExt, target_dir_for_workspace, + CompilationOpts, ScarbCommand, StarknetContractArtifacts, + get_contracts_artifacts_and_source_sierra_paths, metadata::MetadataCommandExt, + target_dir_for_workspace, }; use shared::command::CommandExt; use starknet_api::execution_resources::{GasAmount, GasVector}; @@ -104,12 +105,16 @@ impl Contract { .unwrap(); let artifacts_dir = target_dir_for_workspace(&scarb_metadata).join("dev"); - let contract = - get_contracts_artifacts_and_source_sierra_paths(&artifacts_dir, package, false, ui) - .unwrap() - .remove(&self.name) - .ok_or(anyhow!("there is no contract with name {}", self.name))? - .0; + let contract = get_contracts_artifacts_and_source_sierra_paths( + &artifacts_dir, + package, + ui, + CompilationOpts::default(), + ) + .unwrap() + .remove(&self.name) + .ok_or(anyhow!("there is no contract with name {}", self.name))? + .0; Ok((contract.sierra, contract.casm)) } @@ -221,7 +226,11 @@ impl<'a> TestCase { Ok(( name, ( - StarknetContractArtifacts { sierra, casm }, + StarknetContractArtifacts { + sierra, + casm, + executor: None, + }, Utf8PathBuf::default(), ), )) diff --git a/crates/forge/tests/e2e/backtrace.rs b/crates/forge/tests/e2e/backtrace.rs index 349d221aaa..839cba4159 100644 --- a/crates/forge/tests/e2e/backtrace.rs +++ b/crates/forge/tests/e2e/backtrace.rs @@ -23,6 +23,18 @@ fn test_backtrace_missing_env() { ); } +#[test] +fn test_backtrace_native_execution() { + let temp = setup_package("backtrace_vm_error"); + + test_runner(&temp) + .arg("--run-native") + .env("SNFORGE_BACKTRACE", "1") + .assert() + .code(2) + .stdout_eq("[ERROR] Backtrace generation is not supported with `cairo-native` execution\n"); +} + #[test] fn test_backtrace() { let temp = setup_package("backtrace_vm_error"); diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index b80cc5f928..ee7efa8b1e 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -51,6 +51,51 @@ fn simple_package() { ); } +#[test] +fn simple_package_native() { + let temp = setup_package("simple_package"); + let output = test_runner(&temp).arg("--run-native").assert().code(1); + + assert_stdout_contains( + output, + indoc! {r" + [..]Compiling[..] + [..]Finished[..] + + + Collected 13 test(s) from simple_package package + Running 2 test(s) from src/ + [PASS] simple_package::tests::test_fib [..] + [IGNORE] simple_package::tests::ignored_test + Running 11 test(s) from tests/ + [PASS] simple_package_integrationtest::contract::call_and_invoke [..] + [PASS] simple_package_integrationtest::ext_function_test::test_my_test [..] + [IGNORE] simple_package_integrationtest::ext_function_test::ignored_test + [PASS] simple_package_integrationtest::ext_function_test::test_simple [..] + [PASS] simple_package_integrationtest::test_simple::test_simple [..] + [PASS] simple_package_integrationtest::test_simple::test_simple2 [..] + [PASS] simple_package_integrationtest::test_simple::test_two [..] + [PASS] simple_package_integrationtest::test_simple::test_two_and_two [..] + [FAIL] simple_package_integrationtest::test_simple::test_failing + + Failure data: + 0x6661696c696e6720636865636b ('failing check') + + [FAIL] simple_package_integrationtest::test_simple::test_another_failing + + Failure data: + 0x6661696c696e6720636865636b ('failing check') + + [PASS] simple_package_integrationtest::without_prefix::five [..] + Tests: 9 passed, 2 failed, 2 ignored, 0 filtered out + + Failures: + simple_package_integrationtest::test_simple::test_failing + simple_package_integrationtest::test_simple::test_another_failing + "}, + ); +} + #[test] fn simple_package_with_cheats() { let temp = setup_package("simple_package_with_cheats"); diff --git a/crates/native-api/Cargo.toml b/crates/native-api/Cargo.toml new file mode 100644 index 0000000000..930c6a64a1 --- /dev/null +++ b/crates/native-api/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "native-api" +version.workspace = true +edition.workspace = true + +[dependencies] +starknet_api.workspace = true +cairo-lang-starknet-classes.workspace = true +cairo-native.workspace = true diff --git a/crates/native-api/src/lib.rs b/crates/native-api/src/lib.rs new file mode 100644 index 0000000000..b7af00a63b --- /dev/null +++ b/crates/native-api/src/lib.rs @@ -0,0 +1,40 @@ +//! This module provides functionality related to `cairo-native`. +//! +//! Currently, it includes: +//! - Compiling Sierra contract classes into `cairo-native` executors. + +use cairo_lang_starknet_classes::contract_class::ContractClass; +use cairo_native::executor::AotContractExecutor; +use starknet_api::contract_class::SierraVersion; + +/// Compiles a given Sierra [`ContractClass`] into an [`AotContractExecutor`] for `cairo-native` execution. +#[must_use] +pub fn compile_contract_class(contract_class: &ContractClass) -> AotContractExecutor { + let sierra_program = contract_class + .extract_sierra_program() + .expect("extraction should succeed"); + + let sierra_version = extract_sierra_version(contract_class); + + AotContractExecutor::new( + &sierra_program, + &contract_class.entry_points_by_type, + sierra_version.clone().into(), + cairo_native::OptLevel::Default, + None, + ) + .expect("compilation should succeed") +} + +/// Extracts the Sierra version from the given [`ContractClass`]. +fn extract_sierra_version(contract_class: &ContractClass) -> SierraVersion { + let sierra_version_values = contract_class + .sierra_program + .iter() + .take(3) + .map(|x| x.value.clone()) + .collect::>(); + + SierraVersion::extract_from_program(&sierra_version_values) + .expect("version extraction should succeed") +} diff --git a/crates/scarb-api/Cargo.toml b/crates/scarb-api/Cargo.toml index b1df523b19..f16642621d 100644 --- a/crates/scarb-api/Cargo.toml +++ b/crates/scarb-api/Cargo.toml @@ -19,6 +19,8 @@ rayon.workspace = true itertools.workspace = true universal-sierra-compiler-api = { path = "../universal-sierra-compiler-api" } foundry-ui = { path = "../foundry-ui" } +cairo-native.workspace = true +native-api = { path = "../native-api" } [dev-dependencies] assert_fs.workspace = true diff --git a/crates/scarb-api/src/artifacts.rs b/crates/scarb-api/src/artifacts.rs index e20b5ba32d..5e8ec1a040 100644 --- a/crates/scarb-api/src/artifacts.rs +++ b/crates/scarb-api/src/artifacts.rs @@ -1,6 +1,7 @@ use anyhow::Result; use crate::artifacts::representation::StarknetArtifactsRepresentation; +use cairo_native::executor::AotContractExecutor; use camino::{Utf8Path, Utf8PathBuf}; use itertools::Itertools; use rayon::iter::{IntoParallelIterator, ParallelIterator}; @@ -12,18 +13,31 @@ mod deserialized; mod representation; /// Contains compiled Starknet artifacts -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, Clone)] pub struct StarknetContractArtifacts { /// Compiled sierra code pub sierra: String, /// Compiled casm code pub casm: String, + + /// Optional AOT compiled native executor + pub executor: Option, +} + +impl PartialEq for StarknetContractArtifacts { + fn eq(&self, other: &Self) -> bool { + self.sierra == other.sierra + && self.casm == other.casm + // We only check if both have an executor or not, as the actual executor does not implement PartialEq + && self.executor.is_some() == other.executor.is_some() + } } #[derive(PartialEq, Debug)] pub(crate) struct StarknetArtifactsFiles { base: Utf8PathBuf, other: Vec, + compile_native: bool, } impl StarknetArtifactsFiles { @@ -31,15 +45,21 @@ impl StarknetArtifactsFiles { Self { base: base_file, other: other_files, + compile_native: false, } } + pub(crate) fn compile_native(mut self, compile_native: bool) -> Self { + self.compile_native = compile_native; + self + } + pub(crate) fn load_contracts_artifacts( self, ) -> Result> { // TODO(#2626) handle duplicates - let mut base_artifacts: HashMap = - compile_artifacts( + let mut base_artifacts: HashMap = self + .compile_artifacts( StarknetArtifactsRepresentation::try_from_path(self.base.as_path())?.artifacts(), )?; @@ -52,12 +72,49 @@ impl StarknetArtifactsFiles { let other_artifacts: Vec<(String, Utf8PathBuf)> = unique_artifacts(other_artifact_representations, &base_artifacts); - let compiled_artifacts = compile_artifacts(other_artifacts)?; + let compiled_artifacts = self.compile_artifacts(other_artifacts)?; base_artifacts.extend(compiled_artifacts); Ok(base_artifacts) } + + fn compile_artifacts( + &self, + artifacts: Vec<(String, Utf8PathBuf)>, + ) -> Result> { + artifacts + .into_par_iter() + .map(|(name, path)| { + self.compile_artifact_at_path(&path) + .map(|artifact| (name.to_string(), (artifact, path))) + }) + .collect::>() + } + + fn compile_artifact_at_path(&self, path: &Utf8Path) -> Result { + let sierra = fs::read_to_string(path)?; + + let casm = compile_sierra_at_path(path, &SierraType::Contract)?; + + let executor = self.compile_to_native(&sierra)?; + + Ok(StarknetContractArtifacts { + sierra, + casm, + executor, + }) + } + + fn compile_to_native(&self, sierra: &str) -> Result> { + Ok(if self.compile_native { + Some(native_api::compile_contract_class(&serde_json::from_str( + sierra, + )?)) + } else { + None + }) + } } fn unique_artifacts( @@ -72,25 +129,6 @@ fn unique_artifacts( .collect() } -fn compile_artifacts( - artifacts: Vec<(String, Utf8PathBuf)>, -) -> Result> { - artifacts - .into_par_iter() - .map(|(name, path)| { - compile_artifact_at_path(&path).map(|artifact| (name.to_string(), (artifact, path))) - }) - .collect::>() -} - -fn compile_artifact_at_path(path: &Utf8Path) -> Result { - let sierra = fs::read_to_string(path)?; - - let casm = compile_sierra_at_path(path, &SierraType::Contract)?; - - Ok(StarknetContractArtifacts { sierra, casm }) -} - #[cfg(test)] mod tests { use super::*; @@ -109,6 +147,7 @@ mod tests { StarknetContractArtifacts { sierra: "sierra1".to_string(), casm: "casm1".to_string(), + executor: None, }, Utf8PathBuf::from("path1"), ), diff --git a/crates/scarb-api/src/lib.rs b/crates/scarb-api/src/lib.rs index 0cf9f7f627..841711ab16 100644 --- a/crates/scarb-api/src/lib.rs +++ b/crates/scarb-api/src/lib.rs @@ -93,12 +93,21 @@ fn get_starknet_artifacts_path( path.map(|path| StarknetArtifactsFiles::new(path, vec![])) } +#[derive(Default)] +pub struct CompilationOpts { + pub use_test_target_contracts: bool, + pub run_native: bool, +} + /// Get the map with `StarknetContractArtifacts` for the given package pub fn get_contracts_artifacts_and_source_sierra_paths( artifacts_dir: &Utf8Path, package: &PackageMetadata, - use_test_target_contracts: bool, ui: &UI, + CompilationOpts { + use_test_target_contracts, + run_native, + }: CompilationOpts, ) -> Result> { let starknet_artifact_files = if use_test_target_contracts { let test_targets = test_targets_by_name(package); @@ -115,7 +124,9 @@ pub fn get_contracts_artifacts_and_source_sierra_paths( }; if let Some(starknet_artifact_files) = starknet_artifact_files { - starknet_artifact_files.load_contracts_artifacts() + starknet_artifact_files + .compile_native(run_native) + .load_contracts_artifacts() } else { Ok(HashMap::default()) } @@ -577,8 +588,8 @@ mod tests { let contracts = get_contracts_artifacts_and_source_sierra_paths( target_dir.as_path(), package, - false, &ui, + CompilationOpts::default(), ) .unwrap(); diff --git a/crates/sncast/src/helpers/scarb_utils.rs b/crates/sncast/src/helpers/scarb_utils.rs index 56d83db9a5..8569d0b2ac 100644 --- a/crates/sncast/src/helpers/scarb_utils.rs +++ b/crates/sncast/src/helpers/scarb_utils.rs @@ -2,7 +2,7 @@ use anyhow::{Context, Result, anyhow}; use camino::{Utf8Path, Utf8PathBuf}; use foundry_ui::{UI, components::warning::WarningMessage}; use scarb_api::{ - ScarbCommand, ScarbCommandError, StarknetContractArtifacts, + CompilationOpts, ScarbCommand, ScarbCommandError, StarknetContractArtifacts, get_contracts_artifacts_and_source_sierra_paths, metadata::{Metadata, MetadataCommand, PackageMetadata}, target_dir_for_workspace, @@ -170,8 +170,8 @@ pub fn build_and_load_artifacts( Ok(get_contracts_artifacts_and_source_sierra_paths( &target_dir.join(&config.profile), package, - false, - ui + ui, + CompilationOpts::default() ).context("Failed to load artifacts. Make sure you have enabled sierra code generation in Scarb.toml")? .into_iter() .map(|(name, (artifacts, _))| (name, artifacts)) @@ -184,8 +184,8 @@ pub fn build_and_load_artifacts( Ok(get_contracts_artifacts_and_source_sierra_paths( &target_dir.join(default_profile), package, - false, - ui + ui, + CompilationOpts::default(), ).context("Failed to load artifacts. Make sure you have enabled sierra code generation in Scarb.toml")? .into_iter() .map(|(name, (artifacts, _))| (name, artifacts)) diff --git a/crates/sncast/src/starknet_commands/script/run.rs b/crates/sncast/src/starknet_commands/script/run.rs index 7168a4d360..c1189d594a 100644 --- a/crates/sncast/src/starknet_commands/script/run.rs +++ b/crates/sncast/src/starknet_commands/script/run.rs @@ -460,6 +460,7 @@ fn inject_lib_artifact( let lib_artifacts = ScriptStarknetContractArtifacts { sierra: fs::read_to_string(sierra_path)?, casm: String::new(), + executor: None, }; artifacts.insert(SCRIPT_LIB_ARTIFACT_NAME.to_string(), lib_artifacts); diff --git a/docs/src/appendix/snforge/test.md b/docs/src/appendix/snforge/test.md index ca0709615b..f891279dca 100644 --- a/docs/src/appendix/snforge/test.md +++ b/docs/src/appendix/snforge/test.md @@ -32,6 +32,12 @@ Available components: - `call-type` - `call-result` +## `--run-native` + +Run contracts on [`cairo-native`](https://github.com/lambdaclass/cairo_native) instead of the default `cairo-vm`. + +Note: Only contracts execution through native is supported, test code itself will still run on `cairo-vm`. + ## `-e`, `--exact` Will only run a test with a name exactly matching the test filter. From c33bb75b8aec6d533be17ab93ebecc291d75d42b Mon Sep 17 00:00:00 2001 From: ksew1 <95349104+ksew1@users.noreply.github.com> Date: Mon, 8 Sep 2025 20:53:09 +0200 Subject: [PATCH 24/45] Add sierra validation for native (#3714) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Stack**: - #3717 - #3714 ⬅ ⚠️ *Part of a stack created by [spr](https://github.com/ejoffe/spr). Do not merge manually using the UI - doing so may have unexpected results.* --- Cargo.lock | 1 + crates/native-api/Cargo.toml | 1 + crates/native-api/src/lib.rs | 32 +++++++++++++++++++++++++------ crates/scarb-api/src/artifacts.rs | 2 +- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2fa95901a5..2b3c847632 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4199,6 +4199,7 @@ dependencies = [ "cairo-lang-starknet-classes", "cairo-native", "starknet_api", + "thiserror 2.0.16", ] [[package]] diff --git a/crates/native-api/Cargo.toml b/crates/native-api/Cargo.toml index 930c6a64a1..47bf7f53ac 100644 --- a/crates/native-api/Cargo.toml +++ b/crates/native-api/Cargo.toml @@ -4,6 +4,7 @@ version.workspace = true edition.workspace = true [dependencies] +thiserror.workspace = true starknet_api.workspace = true cairo-lang-starknet-classes.workspace = true cairo-native.workspace = true diff --git a/crates/native-api/src/lib.rs b/crates/native-api/src/lib.rs index b7af00a63b..ae9688a46f 100644 --- a/crates/native-api/src/lib.rs +++ b/crates/native-api/src/lib.rs @@ -7,23 +7,31 @@ use cairo_lang_starknet_classes::contract_class::ContractClass; use cairo_native::executor::AotContractExecutor; use starknet_api::contract_class::SierraVersion; +#[derive(Debug, thiserror::Error)] +pub enum NativeCompilationError { + #[error("Unsupported Sierra version {0}. cairo-native requires version 1.7.0 or later.")] + UnsupportedSierraVersion(String), +} + /// Compiles a given Sierra [`ContractClass`] into an [`AotContractExecutor`] for `cairo-native` execution. -#[must_use] -pub fn compile_contract_class(contract_class: &ContractClass) -> AotContractExecutor { +pub fn compile_contract_class( + contract_class: &ContractClass, +) -> Result { + let sierra_version = extract_sierra_version(contract_class); + check_sierra_version(&sierra_version)?; + let sierra_program = contract_class .extract_sierra_program() .expect("extraction should succeed"); - let sierra_version = extract_sierra_version(contract_class); - - AotContractExecutor::new( + Ok(AotContractExecutor::new( &sierra_program, &contract_class.entry_points_by_type, sierra_version.clone().into(), cairo_native::OptLevel::Default, None, ) - .expect("compilation should succeed") + .expect("compilation should succeed")) } /// Extracts the Sierra version from the given [`ContractClass`]. @@ -38,3 +46,15 @@ fn extract_sierra_version(contract_class: &ContractClass) -> SierraVersion { SierraVersion::extract_from_program(&sierra_version_values) .expect("version extraction should succeed") } + +/// Checks if the given Sierra version is supported by `cairo-native`. +fn check_sierra_version(sierra_version: &SierraVersion) -> Result<(), NativeCompilationError> { + let minimal_supported_version = SierraVersion::new(1, 7, 0); + if sierra_version < &minimal_supported_version { + Err(NativeCompilationError::UnsupportedSierraVersion( + sierra_version.to_string(), + )) + } else { + Ok(()) + } +} diff --git a/crates/scarb-api/src/artifacts.rs b/crates/scarb-api/src/artifacts.rs index 5e8ec1a040..cb1e76ec5e 100644 --- a/crates/scarb-api/src/artifacts.rs +++ b/crates/scarb-api/src/artifacts.rs @@ -110,7 +110,7 @@ impl StarknetArtifactsFiles { Ok(if self.compile_native { Some(native_api::compile_contract_class(&serde_json::from_str( sierra, - )?)) + )?)?) } else { None }) From c5a059b8484ee7551a3d91b91a333e0fd14b76f9 Mon Sep 17 00:00:00 2001 From: ksew1 <95349104+ksew1@users.noreply.github.com> Date: Tue, 9 Sep 2025 09:31:33 +0200 Subject: [PATCH 25/45] Fix ci (#3717) commit-id:5e1132ec --- .github/actions/setup-rust-llvm/action.yml | 32 ++++++++++++++ .github/workflows/ci.yml | 51 ++++++---------------- crates/forge/tests/e2e/backtrace.rs | 10 +++-- 3 files changed, 53 insertions(+), 40 deletions(-) create mode 100644 .github/actions/setup-rust-llvm/action.yml diff --git a/.github/actions/setup-rust-llvm/action.yml b/.github/actions/setup-rust-llvm/action.yml new file mode 100644 index 0000000000..35f51173b3 --- /dev/null +++ b/.github/actions/setup-rust-llvm/action.yml @@ -0,0 +1,32 @@ +name: Setup Rust + LLVM 19 +description: Installs Rust and LLVM 19 toolchain + +runs: + using: "composite" + steps: + - uses: dtolnay/rust-toolchain@stable + + - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + + - name: Add LLVM APT repository + uses: myci-actions/add-deb-repo@11 + with: + repo: deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main + repo-name: llvm-repo + keys-asc: https://apt.llvm.org/llvm-snapshot.gpg.key + + - name: Install LLVM + shell: bash + run: | + sudo apt-get update + sudo apt-get install -y \ + llvm-19 llvm-19-dev llvm-19-runtime \ + clang-19 clang-tools-19 \ + lld-19 libpolly-19-dev libmlir-19-dev mlir-19-tools + + - name: Set environment variables + shell: bash + run: | + echo "MLIR_SYS_190_PREFIX=/usr/lib/llvm-19/" >> $GITHUB_ENV + echo "LLVM_SYS_191_PREFIX=/usr/lib/llvm-19/" >> $GITHUB_ENV + echo "TABLEGEN_190_PREFIX=/usr/lib/llvm-19/" >> $GITHUB_ENV diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab14051c64..b508f676fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,8 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - uses: software-mansion/setup-scarb@v1 - uses: software-mansion/setup-universal-sierra-compiler@v1 - run: cargo test --profile ci --lib -p forge @@ -30,8 +29,7 @@ jobs: fail-fast: false steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - name: Install nextest uses: taiki-e/install-action@v2 with: @@ -123,8 +121,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - uses: software-mansion/setup-universal-sierra-compiler@v1 - uses: asdf-vm/actions/install@1902764435ca0dd2f3388eea723a4f92a4eb8302 - run: cargo test --profile ci --package forge e2e::plugin_versions @@ -135,8 +132,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - uses: software-mansion/setup-universal-sierra-compiler@v1 - run: cargo test --profile ci --package forge --features no_scarb_installed --lib compatibility_check::tests::failing_tool_not_installed @@ -153,8 +149,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - uses: software-mansion/setup-scarb@v1 - uses: software-mansion/setup-universal-sierra-compiler@v1 @@ -166,8 +161,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - run: cargo test --profile ci -p forge_runner test-cheatnet: @@ -175,8 +169,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - uses: software-mansion/setup-scarb@v1 - uses: software-mansion/setup-universal-sierra-compiler@v1 - name: Run Cheatnet tests @@ -187,8 +180,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - name: Run Data Transformer tests run: cargo test --profile ci -p data-transformer @@ -197,8 +189,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 # v2.7.8 + - uses: ./.github/actions/setup-rust-llvm - uses: software-mansion/setup-scarb@v1 - uses: software-mansion/setup-universal-sierra-compiler@58146c4184fa6ec5e8aaf02309ab85e35f782ed0 # v1.0.0 - name: Run Forge Debugging tests @@ -235,10 +226,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b - with: - toolchain: stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - uses: asdf-vm/actions/install@05e0d2ed97b598bfce82fd30daf324ae0c4570e6 - uses: software-mansion/setup-scarb@v1 - uses: software-mansion/setup-universal-sierra-compiler@v1 @@ -250,10 +238,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b - with: - toolchain: stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - name: Run tests run: cargo test --profile ci -p conversions @@ -271,9 +256,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: software-mansion/setup-universal-sierra-compiler@v1 - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - uses: software-mansion/setup-scarb@v1 - uses: software-mansion/setup-universal-sierra-compiler@v1 - run: cargo test --profile ci -p scarb-api @@ -321,10 +304,7 @@ jobs: RUSTFLAGS: "-Dwarnings" steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b - with: - toolchain: stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-rust-llvm - run: cargo lint - name: Lint snforge-scarb-plugin @@ -341,11 +321,8 @@ jobs: env: MDBOOK_VERSION: 0.4.52 steps: - - uses: dtolnay/rust-toolchain@b3b07ba8b418998c39fb20f53e8b695cdcc8de1b - with: - toolchain: stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - uses: actions/checkout@v5 + - uses: ./.github/actions/setup-rust-llvm - uses: software-mansion/setup-scarb@v1 - uses: software-mansion/setup-universal-sierra-compiler@v1 - name: Install mdBook diff --git a/crates/forge/tests/e2e/backtrace.rs b/crates/forge/tests/e2e/backtrace.rs index 839cba4159..4d84ba088d 100644 --- a/crates/forge/tests/e2e/backtrace.rs +++ b/crates/forge/tests/e2e/backtrace.rs @@ -27,12 +27,16 @@ fn test_backtrace_missing_env() { fn test_backtrace_native_execution() { let temp = setup_package("backtrace_vm_error"); - test_runner(&temp) + let output = test_runner(&temp) .arg("--run-native") .env("SNFORGE_BACKTRACE", "1") .assert() - .code(2) - .stdout_eq("[ERROR] Backtrace generation is not supported with `cairo-native` execution\n"); + .code(2); + + assert_stdout_contains( + output, + "[ERROR] Backtrace generation is not supported with `cairo-native` execution\n", + ); } #[test] From 302e44c3bacbcce76b13189dfafec3af6c4925c6 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Fri, 12 Sep 2025 16:15:19 +0200 Subject: [PATCH 26/45] Use different blockifier fork branch --- Cargo.lock | 32 +++++++++----------------------- Cargo.toml | 5 +++-- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2b3c847632..8437c02034 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,7 +129,7 @@ checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "apollo_compilation_utils" version = "0.15.0-rc.3" -source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork-fix#2c5dda8bb899cbd5077741518dc6751d3cd52f32" dependencies = [ "apollo_infra_utils", "cairo-lang-sierra", @@ -144,23 +144,10 @@ dependencies = [ "thiserror 1.0.69", ] -[[package]] -name = "apollo_compile_to_native" -version = "0.15.0-rc.3" -source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" -dependencies = [ - "apollo_compilation_utils", - "apollo_compile_to_native_types", - "apollo_infra_utils", - "cairo-lang-starknet-classes", - "cairo-native", - "tempfile", -] - [[package]] name = "apollo_compile_to_native_types" version = "0.15.0-rc.3" -source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork-fix#2c5dda8bb899cbd5077741518dc6751d3cd52f32" dependencies = [ "apollo_config", "serde", @@ -170,7 +157,7 @@ dependencies = [ [[package]] name = "apollo_config" version = "0.15.0-rc.3" -source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork-fix#2c5dda8bb899cbd5077741518dc6751d3cd52f32" dependencies = [ "apollo_infra_utils", "clap", @@ -188,7 +175,7 @@ dependencies = [ [[package]] name = "apollo_infra_utils" version = "0.15.0-rc.3" -source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork-fix#2c5dda8bb899cbd5077741518dc6751d3cd52f32" dependencies = [ "apollo_proc_macros", "assert-json-diff", @@ -206,7 +193,7 @@ dependencies = [ [[package]] name = "apollo_metrics" version = "0.15.0-rc.3" -source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork-fix#2c5dda8bb899cbd5077741518dc6751d3cd52f32" dependencies = [ "indexmap 2.11.0", "metrics", @@ -218,7 +205,7 @@ dependencies = [ [[package]] name = "apollo_proc_macros" version = "0.15.0-rc.3" -source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork-fix#2c5dda8bb899cbd5077741518dc6751d3cd52f32" dependencies = [ "lazy_static 1.5.0", "proc-macro2", @@ -801,11 +788,10 @@ dependencies = [ [[package]] name = "blockifier" version = "0.15.0-rc.3" -source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork-fix#2c5dda8bb899cbd5077741518dc6751d3cd52f32" dependencies = [ "anyhow", "apollo_compilation_utils", - "apollo_compile_to_native", "apollo_compile_to_native_types", "apollo_config", "apollo_infra_utils", @@ -851,7 +837,7 @@ dependencies = [ [[package]] name = "blockifier_test_utils" version = "0.15.0-rc.3" -source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork-fix#2c5dda8bb899cbd5077741518dc6751d3cd52f32" dependencies = [ "apollo_infra_utils", "cairo-lang-starknet-classes", @@ -6515,7 +6501,7 @@ dependencies = [ [[package]] name = "starknet_api" version = "0.15.0-rc.3" -source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork#152f6e659c845ac264f076790cea6f5be7ed0558" +source = "git+https://github.com/cptartur/sequencer.git?branch=main-v0.14.0-fork-fix#2c5dda8bb899cbd5077741518dc6751d3cd52f32" dependencies = [ "apollo_infra_utils", "base64 0.13.1", diff --git a/Cargo.toml b/Cargo.toml index 44dca4fbd7..2828eb6a6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,9 +34,10 @@ license = "MIT" license-file = "LICENSE" [workspace.dependencies] -blockifier = { git = "https://github.com/cptartur/sequencer.git", branch = "main-v0.14.0-fork", features = ["testing", "tracing", "cairo_native"] } +# TODO do not use fork here, after apollo-compile-to-native dependency problem is resolved +blockifier = { git = "https://github.com/cptartur/sequencer.git", branch = "main-v0.14.0-fork-fix", features = ["testing", "tracing", "cairo_native"] } bigdecimal = "0.4.8" -starknet_api = { git = "https://github.com/cptartur/sequencer.git", branch = "main-v0.14.0-fork" } +starknet_api = { git = "https://github.com/cptartur/sequencer.git", branch = "main-v0.14.0-fork-fix" } cairo-native = { git = "https://github.com/lambdaclass/cairo_native.git", rev = "d7f8352657a4c1228b227807b90aea10f97f746d" } cairo-lang-casm = { version = "2.12.0", features = ["serde"] } cairo-lang-sierra = "2.12.0" From 0d0bb70edcf872ab86366d788e8a6a1695db0cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Micha=C5=82ek?= <52135326+cptartur@users.noreply.github.com> Date: Wed, 17 Sep 2025 15:36:52 +0200 Subject: [PATCH 27/45] Bundle LLVM (#3698) Closes # ## Introduced changes - ## Checklist - [ ] Linked relevant issue - [ ] Updated relevant documentation - [ ] Added relevant tests - [ ] Performed self-review of the code - [ ] Added changes to `CHANGELOG.md` --- .github/workflows/_build-binaries.yml | 68 +++++++++++++++++---------- Cross.toml | 19 ++++++++ NATIVE_INSTALLATION.md | 21 +++++++++ 3 files changed, 82 insertions(+), 26 deletions(-) create mode 100644 Cross.toml create mode 100644 NATIVE_INSTALLATION.md diff --git a/.github/workflows/_build-binaries.yml b/.github/workflows/_build-binaries.yml index 07cab1eca4..e5e13541e9 100644 --- a/.github/workflows/_build-binaries.yml +++ b/.github/workflows/_build-binaries.yml @@ -25,26 +25,27 @@ jobs: include: - target: x86_64-unknown-linux-gnu os: ubuntu-latest - # Use cross to link oldest GLIBC possible. - cross: true - - - target: x86_64-unknown-linux-musl - os: ubuntu-latest - cross: true - - - target: aarch64-unknown-linux-gnu - os: ubuntu-latest - cross: true - - - target: aarch64-unknown-linux-musl - os: ubuntu-latest - cross: true + system: linux +# +# - target: x86_64-unknown-linux-musl +# os: ubuntu-latest +# system: linux +# +# - target: aarch64-unknown-linux-gnu +# os: ubuntu-latest +# system: linux +# +# - target: aarch64-unknown-linux-musl +# os: ubuntu-latest +# system: linux - target: x86_64-apple-darwin - os: macos-latest + os: macos-14-large + system: macos - target: aarch64-apple-darwin os: macos-latest + system: macos steps: - name: Checkout with ref @@ -62,21 +63,36 @@ jobs: rustup target add ${{ matrix.target }} - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - with: - workspaces: starknet-foundry - - name: Install cross - if: matrix.cross - uses: taiki-e/install-action@cross + - name: Setup LLVM on MacOS + if: matrix.system == 'macos' + run: | + brew install llvm@19 + brew reinstall zstd + echo "MLIR_SYS_190_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV + echo "LLVM_SYS_191_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV + echo "TABLEGEN_190_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV + echo "LIBRARY_PATH=/opt/homebrew/lib" >> $GITHUB_ENV - - name: Enable cross-compilation - if: matrix.cross - shell: bash + - name: Install cross + if: matrix.system == 'linux' run: | - echo "CARGO=cross" >> $GITHUB_ENV + cargo install cross --git https://github.com/cross-rs/cross - - name: Build - run: ${{ env.CARGO }} build --release --locked --target ${{ matrix.target }} + - name: Build MacOS + if: matrix.system == 'macos' + run: | + export MLIR_SYS_190_PREFIX=${{ env.MLIR_SYS_190_PREFIX }} + export LLVM_SYS_191_PREFIX=${{ env.LLVM_SYS_191_PREFIX }} + export TABLEGEN_190_PREFIX=${{ env.TABLEGEN_190_PREFIX }} + export LIBRARY_PATH=${{ env.LIBRARY_PATH }} + cargo build --release --locked --target ${{ matrix.target }} + + - name: Build Linux + if: matrix.system == 'linux' + run: | + # Use cross to link oldest GLIBC possible. + cross build --release --locked --target ${{ matrix.target }} - name: Package shell: bash diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000000..e545486d92 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,19 @@ +[build] +pre-build = [ + "dpkg --add-architecture $CROSS_DEB_ARCH", + "apt-get update", + "apt-get install -y --no-install-recommends ca-certificates gnupg wget software-properties-common lsb-release", + "apt-get install -y --no-install-recommends zlib1g-dev libzstd-dev zlib1g-dev:$CROSS_DEB_ARCH libzstd-dev:$CROSS_DEB_ARCH", + "echo \"deb http://apt.llvm.org/focal/ llvm-toolchain-focal-19 main\" > /etc/apt/sources.list.d/llvm-toolchain-focal.list", + "echo \"deb-src http://apt.llvm.org/focal/ llvm-toolchain-focal-19 main\" >> /etc/apt/sources.list.d/llvm-toolchain-focal.list", + "wget -qO - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -", + "apt-get update", + "apt-get install -y --no-install-recommends llvm-19 llvm-19-dev llvm-19-runtime clang-19 clang-tools-19 lld-19 libpolly-19-dev libmlir-19-dev mlir-19-tools", +] + +[target.x86_64-unknown-linux-gnu.env] +passthrough = [ + "MLIR_SYS_190_PREFIX=/usr/lib/llvm-19", + "LLVM_SYS_191_PREFIX=/usr/lib/llvm-19", + "TABLEGEN_190_PREFIX=/usr/lib/llvm-19", +] diff --git a/NATIVE_INSTALLATION.md b/NATIVE_INSTALLATION.md new file mode 100644 index 0000000000..a679b4c6a4 --- /dev/null +++ b/NATIVE_INSTALLATION.md @@ -0,0 +1,21 @@ +# Installing Starknet Foundry With Cairo Native Support + +Cairo Native introduces additional dependencies outside of the Rust ecosystem. + +## LLVM + +LLVM is linked into Starknet Foundry binary, so it doesn't have to be installed separately at the cost an incrased +binary size. + +## `ld` + +Cairo Native crate makes direct calls to `ld`, which must in turn be installed on the system. + +### Linux + +The package `binutils` contains `ld`, install it with package manager relevant to your distribution or build it +from source. + +### MacOS + +`ld` is part of the Xcode command line tools. Install it with `xcode-select --install`. From 796e9a93d6d2752def83a5be6099e26db83666fa Mon Sep 17 00:00:00 2001 From: ksew1 <95349104+ksew1@users.noreply.github.com> Date: Wed, 17 Sep 2025 16:50:55 +0200 Subject: [PATCH 28/45] Run tests on native (#3730) --- .github/workflows/ci.yml | 12 +++++--- crates/cheatnet/Cargo.toml | 1 + crates/cheatnet/tests/common/mod.rs | 5 +++- crates/forge/Cargo.toml | 1 + crates/forge/src/lib.rs | 21 +++++++++++-- crates/forge/test_utils/Cargo.toml | 1 + crates/forge/test_utils/src/runner.rs | 5 +++- crates/forge/tests/e2e/clean.rs | 8 ++++- crates/forge/tests/e2e/common/mod.rs | 2 ++ crates/forge/tests/e2e/common/runner.rs | 22 ++++++++++++++ .../tests/e2e/docs_snippets_validation.rs | 4 +++ crates/forge/tests/e2e/fuzzing.rs | 2 +- crates/forge/tests/e2e/mod.rs | 10 +++++-- crates/forge/tests/e2e/running.rs | 14 +++++++-- crates/scarb-api/Cargo.toml | 3 ++ crates/scarb-api/src/artifacts.rs | 30 +++++++++++++++++-- crates/scarb-api/src/lib.rs | 5 +++- crates/sncast/src/helpers/scarb_utils.rs | 2 ++ 18 files changed, 128 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b508f676fd..28748ec7b6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,8 @@ jobs: with: tool: nextest@0.9.98 - name: Build and archive tests - run: cargo nextest archive --cargo-profile ci -p forge --features skip_plugin_checks --archive-file 'nextest-archive-${{ runner.os }}.tar.zst' + # TODO: run native only on scheduled workflow + run: cargo nextest archive --cargo-profile ci -p forge --features skip_plugin_checks,run-native --archive-file 'nextest-archive-${{ runner.os }}.tar.zst' - name: Upload archive to workflow uses: actions/upload-artifact@v4 with: @@ -173,7 +174,8 @@ jobs: - uses: software-mansion/setup-scarb@v1 - uses: software-mansion/setup-universal-sierra-compiler@v1 - name: Run Cheatnet tests - run: cargo test --profile ci -p cheatnet + # TODO: run native only on scheduled workflow + run: cargo test --profile ci -p cheatnet --features run-native test-data-transformer: name: Test Data Transformer @@ -193,7 +195,8 @@ jobs: - uses: software-mansion/setup-scarb@v1 - uses: software-mansion/setup-universal-sierra-compiler@58146c4184fa6ec5e8aaf02309ab85e35f782ed0 # v1.0.0 - name: Run Forge Debugging tests - run: cargo test --profile ci -p forge --features debugging --test main e2e::debugging + # TODO: run native only on scheduled workflow + run: cargo test --profile ci -p forge --features debugging,run-native --test main e2e::debugging test-forge-scarb-plugin: name: Test Forge Scarb Plugin @@ -259,7 +262,8 @@ jobs: - uses: ./.github/actions/setup-rust-llvm - uses: software-mansion/setup-scarb@v1 - uses: software-mansion/setup-universal-sierra-compiler@v1 - - run: cargo test --profile ci -p scarb-api + # TODO: run native only on scheduled workflow + - run: cargo test --profile ci -p scarb-api --features run-native scarbfmt: runs-on: ubuntu-latest diff --git a/crates/cheatnet/Cargo.toml b/crates/cheatnet/Cargo.toml index 22c22dc977..acafd8dd4f 100644 --- a/crates/cheatnet/Cargo.toml +++ b/crates/cheatnet/Cargo.toml @@ -5,6 +5,7 @@ edition.workspace = true [features] testing = [] +run-native = [] [dependencies] anyhow.workspace = true diff --git a/crates/cheatnet/tests/common/mod.rs b/crates/cheatnet/tests/common/mod.rs index 41431f13c4..1398cc9a65 100644 --- a/crates/cheatnet/tests/common/mod.rs +++ b/crates/cheatnet/tests/common/mod.rs @@ -90,7 +90,10 @@ pub fn get_contracts() -> ContractsData { &target_dir, package, &ui, - CompilationOpts::default(), + CompilationOpts { + use_test_target_contracts: false, + run_native: cfg!(feature = "run-native"), + }, ) .unwrap(); ContractsData::try_from(contracts).unwrap() diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 9e0fae161f..b80e804a35 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -13,6 +13,7 @@ debugging = [] assert_non_exact_gas = ["test_utils/assert_non_exact_gas"] skip_plugin_checks = [] interact-with-state = [] +run-native = ["test_utils/run-native"] [dependencies] anyhow.workspace = true diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 991ab09399..024cd985ff 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -142,7 +142,7 @@ pub struct TestArgs { #[command(flatten)] trace_args: TraceArgs, - /// Run contracts on `cairo-native` instead of the default `cairo-vm`. + /// Run contracts on `cairo-native` instead of the default `cairo-vm`. This will set `tracked-resource` to `sierra-gas`. /// /// Note: Only contracts execution through native is supported, test code itself will still run on `cairo-vm`. #[arg(long)] @@ -175,7 +175,7 @@ pub struct TestArgs { include_ignored: bool, /// Display more detailed info about used resources - #[arg(long, conflicts_with = "run_native")] + #[arg(long)] detailed_resources: bool, /// Control when colored output is used @@ -222,6 +222,20 @@ pub struct TestArgs { scarb_args: ScarbArgs, } +impl TestArgs { + /// Adjust dependent arguments based on related flags. + /// + /// This function mutates the `TestArgs` instance to enforce logical coherence + /// between fields. + pub fn normalize(&mut self) { + // Force using `SierraGas` as tracked resource when running with `cairo-native`, + // as otherwise it would run on vm. + if self.run_native { + self.tracked_resource = ForgeTrackedResource::SierraGas; + } + } +} + #[derive(Parser, Debug)] pub struct ScarbArgs { #[command(flatten)] @@ -294,7 +308,8 @@ pub fn main_execution(ui: Arc) -> Result { Ok(ExitStatus::Success) } - ForgeSubcommand::Test { args } => { + ForgeSubcommand::Test { mut args } => { + args.normalize(); check_requirements(false, &ui)?; let cores = if let Ok(available_cores) = available_parallelism() { available_cores.get() diff --git a/crates/forge/test_utils/Cargo.toml b/crates/forge/test_utils/Cargo.toml index f38b463274..56c41eee00 100644 --- a/crates/forge/test_utils/Cargo.toml +++ b/crates/forge/test_utils/Cargo.toml @@ -7,6 +7,7 @@ edition.workspace = true [features] assert_non_exact_gas = [] +run-native = [] [dependencies] walkdir.workspace = true diff --git a/crates/forge/test_utils/src/runner.rs b/crates/forge/test_utils/src/runner.rs index 3232b8431a..6282c43a7a 100644 --- a/crates/forge/test_utils/src/runner.rs +++ b/crates/forge/test_utils/src/runner.rs @@ -109,7 +109,10 @@ impl Contract { &artifacts_dir, package, ui, - CompilationOpts::default(), + CompilationOpts { + use_test_target_contracts: false, + run_native: cfg!(feature = "run-native"), + }, ) .unwrap() .remove(&self.name) diff --git a/crates/forge/tests/e2e/clean.rs b/crates/forge/tests/e2e/clean.rs index d57ea1c062..b489643a9c 100644 --- a/crates/forge/tests/e2e/clean.rs +++ b/crates/forge/tests/e2e/clean.rs @@ -21,6 +21,7 @@ struct CleanComponentsState { } #[test] +#[cfg_attr(feature = "run-native", ignore = "Native doesn't support coverage yet")] fn test_clean_coverage() { let temp_dir = setup_package("coverage_project"); @@ -54,6 +55,7 @@ fn test_clean_coverage() { } #[test] +#[cfg_attr(feature = "run-native", ignore = "Native doesn't support profiler yet")] fn test_clean_profile() { let temp_dir = setup_package("coverage_project"); @@ -118,6 +120,10 @@ fn test_clean_cache() { } #[test] +#[cfg_attr( + feature = "run-native", + ignore = "Native doesn't support trace, coverage and profiler yet" +)] fn test_clean_all() { let temp_dir = setup_package("coverage_project"); @@ -152,7 +158,7 @@ fn test_clean_all_and_component() { let clean_components_state = CleanComponentsState { coverage: false, cache: true, - trace: true, + trace: false, profile: false, }; generate_clean_components(clean_components_state, &temp_dir); diff --git a/crates/forge/tests/e2e/common/mod.rs b/crates/forge/tests/e2e/common/mod.rs index 0f6b4f3849..47b17b9107 100644 --- a/crates/forge/tests/e2e/common/mod.rs +++ b/crates/forge/tests/e2e/common/mod.rs @@ -1,9 +1,11 @@ +#[cfg(not(feature = "run-native"))] use cairo_annotations::trace_data::{ CallTraceNode as ProfilerCallTraceNode, CallTraceV1 as ProfilerCallTrace, }; pub mod runner; +#[cfg(not(feature = "run-native"))] pub fn get_trace_from_trace_node(trace_node: &ProfilerCallTraceNode) -> &ProfilerCallTrace { if let ProfilerCallTraceNode::EntryPointCall(trace) = trace_node { trace diff --git a/crates/forge/tests/e2e/common/runner.rs b/crates/forge/tests/e2e/common/runner.rs index 6e086d0039..6bdec987ed 100644 --- a/crates/forge/tests/e2e/common/runner.rs +++ b/crates/forge/tests/e2e/common/runner.rs @@ -31,7 +31,29 @@ pub fn snforge_test_bin_path() -> PathBuf { cargo_bin!("snforge").to_path_buf() } +/// Returns a command that runs `snforge test` in the given temporary directory. +/// If the `run-native` feature is enabled, it adds the `--run-native` flag. pub(crate) fn test_runner>(temp_dir: T) -> SnapboxCommand { + if cfg!(feature = "run-native") { + test_runner_native(temp_dir) + } else { + test_runner_vm(temp_dir) + } +} + +/// Returns a command that runs `snforge test --run-native` in the given temporary directory. +/// +/// This is useful for testing behavior that occurs only when the `--run-native` flag is passed. +/// If the behavior is not specific to native execution, use `test_runner` instead. +pub(crate) fn test_runner_native>(temp_dir: T) -> SnapboxCommand { + runner(temp_dir).arg("test").arg("--run-native") +} + +/// Returns a command that runs `snforge test` in the given temporary directory. +/// +/// This is useful for testing behavior that occurs only in the VM execution. +/// If the behavior is not specific to VM execution, use `test_runner` instead. +pub(crate) fn test_runner_vm>(temp_dir: T) -> SnapboxCommand { runner(temp_dir).arg("test") } diff --git a/crates/forge/tests/e2e/docs_snippets_validation.rs b/crates/forge/tests/e2e/docs_snippets_validation.rs index ac16e555a1..b1dd4f62b9 100644 --- a/crates/forge/tests/e2e/docs_snippets_validation.rs +++ b/crates/forge/tests/e2e/docs_snippets_validation.rs @@ -11,6 +11,10 @@ use shared::test_utils::output_assert::assert_stdout_contains; use super::common::runner::{runner, setup_package}; #[test] +#[cfg_attr( + feature = "run-native", + ignore = "TODO: Many snippets show vm resources witch cairo native doesn't support" +)] fn test_docs_snippets() { let root_dir = get_nth_ancestor(2); let docs_dir = root_dir.join("docs/src"); diff --git a/crates/forge/tests/e2e/fuzzing.rs b/crates/forge/tests/e2e/fuzzing.rs index 22f4960c27..4bae8d9221 100644 --- a/crates/forge/tests/e2e/fuzzing.rs +++ b/crates/forge/tests/e2e/fuzzing.rs @@ -285,7 +285,7 @@ fn generate_arg_cheatcode() { Failure data: "`generate_arg` cheatcode: `min_value` must be <= `max_value`, provided values after deserialization: 101 and 100" - [PASS] fuzzing_integrationtest::generate_arg::use_generate_arg_outside_fuzzer (l1_gas: ~0, l1_data_gas: ~0, l2_gas: ~40000) + [PASS] fuzzing_integrationtest::generate_arg::use_generate_arg_outside_fuzzer [..] Tests: 1 passed, 1 failed, 0 ignored, 23 filtered out "#}, ); diff --git a/crates/forge/tests/e2e/mod.rs b/crates/forge/tests/e2e/mod.rs index 8d23fe7b23..4769f9961f 100644 --- a/crates/forge/tests/e2e/mod.rs +++ b/crates/forge/tests/e2e/mod.rs @@ -1,16 +1,19 @@ -pub(crate) mod common; - +#[cfg(not(feature = "run-native"))] mod backtrace; +#[cfg(not(feature = "run-native"))] mod build_profile; +#[cfg(not(feature = "run-native"))] mod build_trace_data; mod clean; mod collection; mod color; +pub(crate) mod common; mod completions; mod components; mod contract_artifacts; +#[cfg(not(feature = "run-native"))] mod coverage; -#[cfg(feature = "debugging")] +#[cfg(all(feature = "debugging", not(feature = "run-native")))] mod debugging; mod docs_snippets_validation; mod env; @@ -29,5 +32,6 @@ mod running; mod steps; mod templates; mod trace_print; +#[cfg(not(feature = "run-native"))] mod trace_resources; mod workspaces; diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index ee7efa8b1e..907ec7ef8a 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -1,4 +1,6 @@ -use super::common::runner::{get_current_branch, get_remote_url, setup_package, test_runner}; +use super::common::runner::{ + get_current_branch, get_remote_url, setup_package, test_runner, test_runner_native, +}; use assert_fs::fixture::{FileWriteStr, PathChild}; use indoc::{formatdoc, indoc}; use shared::test_utils::output_assert::{AsOutput, assert_stdout, assert_stdout_contains}; @@ -54,7 +56,7 @@ fn simple_package() { #[test] fn simple_package_native() { let temp = setup_package("simple_package"); - let output = test_runner(&temp).arg("--run-native").assert().code(1); + let output = test_runner_native(&temp).assert().code(1); assert_stdout_contains( output, @@ -1056,6 +1058,10 @@ fn incompatible_snforge_std_version_error() { } #[test] +#[cfg_attr( + feature = "run-native", + ignore = "Native runner does not support vm resources tracking" +)] fn detailed_resources_flag() { let temp = setup_package("erc20_package"); let output = test_runner(&temp) @@ -1251,6 +1257,10 @@ fn exact_printing_mixed() { } #[test] +#[cfg_attr( + feature = "run-native", + ignore = "Native runner does not support panic backtrace yet" +)] fn dispatchers() { let temp = setup_package("dispatchers"); diff --git a/crates/scarb-api/Cargo.toml b/crates/scarb-api/Cargo.toml index f16642621d..279045192a 100644 --- a/crates/scarb-api/Cargo.toml +++ b/crates/scarb-api/Cargo.toml @@ -3,6 +3,9 @@ name = "scarb-api" version = "1.0.0" edition.workspace = true +[features] +run-native = [] + [dependencies] anyhow.workspace = true shared.workspace = true diff --git a/crates/scarb-api/src/artifacts.rs b/crates/scarb-api/src/artifacts.rs index cb1e76ec5e..f3c519e021 100644 --- a/crates/scarb-api/src/artifacts.rs +++ b/crates/scarb-api/src/artifacts.rs @@ -132,7 +132,10 @@ fn unique_artifacts( #[cfg(test)] mod tests { use super::*; + use crate::ScarbCommand; + use crate::tests::setup_package; + use assert_fs::TempDir; use assert_fs::fixture::{FileWriteStr, PathChild}; use camino::Utf8PathBuf; use deserialized::{StarknetArtifacts, StarknetContract, StarknetContractArtifactPaths}; @@ -191,9 +194,8 @@ mod tests { assert_eq!(result[0].0, "contract2"); } - #[test] - fn test_load_contracts_artifacts() { - let temp = crate::tests::setup_package("basic_package"); + fn setup() -> (TempDir, StarknetArtifactsFiles) { + let temp = setup_package("basic_package"); let tests_dir = temp.join("tests"); fs::create_dir(&tests_dir).unwrap(); @@ -235,11 +237,33 @@ mod tests { // Create `StarknetArtifactsFiles` let artifacts_files = StarknetArtifactsFiles::new(base_file, other_files); + (temp, artifacts_files) + } + + #[test] + fn test_load_contracts_artifacts() { + let (_temp, artifacts_files) = setup(); + + // Load the contracts + let result = artifacts_files.load_contracts_artifacts().unwrap(); + + // Assert the Contract Artifacts are loaded. + assert!(result.contains_key("ERC20")); + assert!(result.contains_key("HelloStarknet")); + } + + #[test] + fn test_load_contracts_artifacts_native() { + let (_temp, artifacts_files) = setup(); + + let artifacts_files = artifacts_files.compile_native(true); + // Load the contracts let result = artifacts_files.load_contracts_artifacts().unwrap(); // Assert the Contract Artifacts are loaded. assert!(result.contains_key("ERC20")); assert!(result.contains_key("HelloStarknet")); + assert!(result.get("ERC20").unwrap().0.executor.is_some()); } } diff --git a/crates/scarb-api/src/lib.rs b/crates/scarb-api/src/lib.rs index 841711ab16..8c6d173277 100644 --- a/crates/scarb-api/src/lib.rs +++ b/crates/scarb-api/src/lib.rs @@ -589,7 +589,10 @@ mod tests { target_dir.as_path(), package, &ui, - CompilationOpts::default(), + CompilationOpts { + use_test_target_contracts: false, + run_native: cfg!(feature = "run-native"), + }, ) .unwrap(); diff --git a/crates/sncast/src/helpers/scarb_utils.rs b/crates/sncast/src/helpers/scarb_utils.rs index 8569d0b2ac..180cbd6761 100644 --- a/crates/sncast/src/helpers/scarb_utils.rs +++ b/crates/sncast/src/helpers/scarb_utils.rs @@ -171,6 +171,7 @@ pub fn build_and_load_artifacts( &target_dir.join(&config.profile), package, ui, + // TODO: Reconsider running sncast with native CompilationOpts::default() ).context("Failed to load artifacts. Make sure you have enabled sierra code generation in Scarb.toml")? .into_iter() @@ -185,6 +186,7 @@ pub fn build_and_load_artifacts( &target_dir.join(default_profile), package, ui, + // TODO: Reconsider running sncast with native CompilationOpts::default(), ).context("Failed to load artifacts. Make sure you have enabled sierra code generation in Scarb.toml")? .into_iter() From 0c4c497eae8d64d265f290f43bc00836c68f4937 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Wed, 17 Sep 2025 16:55:05 +0200 Subject: [PATCH 29/45] Extend NATIVE_INSTALLATION.md --- CAIRO_NATIVE.md | 41 +++++++++++++++++++++++++++++++++++++++++ NATIVE_INSTALLATION.md | 21 --------------------- 2 files changed, 41 insertions(+), 21 deletions(-) create mode 100644 CAIRO_NATIVE.md delete mode 100644 NATIVE_INSTALLATION.md diff --git a/CAIRO_NATIVE.md b/CAIRO_NATIVE.md new file mode 100644 index 0000000000..94ec2b00bd --- /dev/null +++ b/CAIRO_NATIVE.md @@ -0,0 +1,41 @@ +# Running Cairo Native + + +* [Running Cairo Native](#running-cairo-native) + * [Installing Starknet Foundry With Cairo Native Support](#installing-starknet-foundry-with-cairo-native-support) + * [LLVM](#llvm) + * [`ld`](#ld) + * [Linux](#linux) + * [MacOS](#macos) + * [Running Tests](#running-tests) + + +## Installing Starknet Foundry With Cairo Native Support + +Cairo Native introduces additional dependencies outside of the Rust ecosystem. + +### LLVM + +LLVM is linked into Starknet Foundry binary, so it doesn't have to be installed separately at the cost an incrased +binary size. + +### `ld` + +Cairo Native crate makes direct calls to `ld`, which must in turn be installed on the system. + +#### Linux + +The package `binutils` contains `ld`, install it with package manager relevant to your distribution or build it +from source. + +#### MacOS + +`ld` is part of the Xcode command line tools. Install it with `xcode-select --install`. + +## Running Tests + +To run tests run `snforge test --run-native`, without the flag, the execution will still run in the VM. + +When running native features that rely on VM trace like test backtrace, profiler, coverage will not work. +Running native enforces the test to run with `sierra-gas` as tracked resource. Tracking vm resources is not possible +with the native execution. \ No newline at end of file diff --git a/NATIVE_INSTALLATION.md b/NATIVE_INSTALLATION.md deleted file mode 100644 index a679b4c6a4..0000000000 --- a/NATIVE_INSTALLATION.md +++ /dev/null @@ -1,21 +0,0 @@ -# Installing Starknet Foundry With Cairo Native Support - -Cairo Native introduces additional dependencies outside of the Rust ecosystem. - -## LLVM - -LLVM is linked into Starknet Foundry binary, so it doesn't have to be installed separately at the cost an incrased -binary size. - -## `ld` - -Cairo Native crate makes direct calls to `ld`, which must in turn be installed on the system. - -### Linux - -The package `binutils` contains `ld`, install it with package manager relevant to your distribution or build it -from source. - -### MacOS - -`ld` is part of the Xcode command line tools. Install it with `xcode-select --install`. From ca256b09f7f96dd06db95cf3505e4b818a7a763e Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Wed, 17 Sep 2025 17:28:31 +0200 Subject: [PATCH 30/45] Update lock --- Cargo.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8437c02034..9bfe7b5094 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1654,7 +1654,7 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "cheatnet" -version = "0.48.1" +version = "0.49.0" dependencies = [ "anyhow", "bimap", @@ -2468,7 +2468,7 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "docs" -version = "0.48.1" +version = "0.49.0" dependencies = [ "anyhow", "camino", @@ -2828,7 +2828,7 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "forge" -version = "0.48.1" +version = "0.49.0" dependencies = [ "anyhow", "assert_fs", @@ -2885,7 +2885,7 @@ dependencies = [ [[package]] name = "forge_runner" -version = "0.48.1" +version = "0.49.0" dependencies = [ "anyhow", "blockifier", @@ -2943,7 +2943,7 @@ dependencies = [ [[package]] name = "foundry-ui" -version = "0.48.1" +version = "0.49.0" dependencies = [ "anyhow", "console", @@ -4180,7 +4180,7 @@ dependencies = [ [[package]] name = "native-api" -version = "0.48.1" +version = "0.49.0" dependencies = [ "cairo-lang-starknet-classes", "cairo-native", @@ -6155,7 +6155,7 @@ dependencies = [ [[package]] name = "sncast" -version = "0.48.1" +version = "0.49.0" dependencies = [ "anyhow", "async-trait", From d6a64d69bdf08c406c98525b2ce265121cc3fdfc Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Thu, 18 Sep 2025 10:32:08 +0200 Subject: [PATCH 31/45] Update smoke test target --- .github/workflows/_test-binaries.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/_test-binaries.yml b/.github/workflows/_test-binaries.yml index 1431a448ac..afa1edd0a9 100644 --- a/.github/workflows/_test-binaries.yml +++ b/.github/workflows/_test-binaries.yml @@ -24,9 +24,13 @@ jobs: - target: x86_64-unknown-linux-gnu os: ubuntu-latest - - target: x86_64-apple-darwin + - target: aarch64-apple-darwin os: macos-latest + - target: x86_64-apple-darwin + # Target macos-latest uses Mac with ARM, macos-14-large is still on Intel + os: macos-14-large + steps: - uses: actions/checkout@v5 - uses: software-mansion/setup-scarb@v1 From 3e7848103602f47ea95d7cde7b6d200afb2b6b40 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Thu, 18 Sep 2025 11:17:12 +0200 Subject: [PATCH 32/45] Disable smoke tests on macos --- .github/workflows/_test-binaries.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/_test-binaries.yml b/.github/workflows/_test-binaries.yml index afa1edd0a9..e2a9f4d64f 100644 --- a/.github/workflows/_test-binaries.yml +++ b/.github/workflows/_test-binaries.yml @@ -24,12 +24,13 @@ jobs: - target: x86_64-unknown-linux-gnu os: ubuntu-latest - - target: aarch64-apple-darwin - os: macos-latest - - - target: x86_64-apple-darwin - # Target macos-latest uses Mac with ARM, macos-14-large is still on Intel - os: macos-14-large +# TODO restore smoke tests on macos +# - target: aarch64-apple-darwin +# os: macos-latest +# +# - target: x86_64-apple-darwin +# # Target macos-latest uses Mac with ARM, macos-14-large is still on Intel +# os: macos-14-large steps: - uses: actions/checkout@v5 From d3e1c8c83ad01f3404c6da3468dd71e80a116cc5 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Mon, 22 Sep 2025 14:40:19 +0200 Subject: [PATCH 33/45] Add more native tracing --- Cargo.lock | 2 ++ crates/forge-runner/src/running/config_run.rs | 1 + crates/forge-runner/src/running/with_config.rs | 1 + crates/scarb-api/Cargo.toml | 1 + crates/scarb-api/src/artifacts.rs | 5 +++++ crates/scarb-api/src/lib.rs | 3 +++ crates/universal-sierra-compiler-api/Cargo.toml | 1 + crates/universal-sierra-compiler-api/src/lib.rs | 1 + 8 files changed, 15 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 9bfe7b5094..c0551d652c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5669,6 +5669,7 @@ dependencies = [ "serde_json", "shared", "thiserror 2.0.16", + "tracing", "universal-sierra-compiler-api", "which", ] @@ -7339,6 +7340,7 @@ dependencies = [ "serde_json", "shared", "tempfile", + "tracing", "which", ] diff --git a/crates/forge-runner/src/running/config_run.rs b/crates/forge-runner/src/running/config_run.rs index e9ee61cd1f..e25afc02ea 100644 --- a/crates/forge-runner/src/running/config_run.rs +++ b/crates/forge-runner/src/running/config_run.rs @@ -58,6 +58,7 @@ impl StateReader for PhantomStateReader { } } +#[tracing::instrument(skip_all, level = "debug")] pub fn run_config_pass( test_details: &TestDetails, casm_program: &AssembledProgramWithDebugInfo, diff --git a/crates/forge-runner/src/running/with_config.rs b/crates/forge-runner/src/running/with_config.rs index 2789fd8588..145881a991 100644 --- a/crates/forge-runner/src/running/with_config.rs +++ b/crates/forge-runner/src/running/with_config.rs @@ -88,6 +88,7 @@ pub fn test_target_with_config( }) } +#[tracing::instrument(skip_all, level = "debug")] fn build_test_details( func: &GenFunction, type_declarations: &HashMap, diff --git a/crates/scarb-api/Cargo.toml b/crates/scarb-api/Cargo.toml index 279045192a..47f8a51551 100644 --- a/crates/scarb-api/Cargo.toml +++ b/crates/scarb-api/Cargo.toml @@ -24,6 +24,7 @@ universal-sierra-compiler-api = { path = "../universal-sierra-compiler-api" } foundry-ui = { path = "../foundry-ui" } cairo-native.workspace = true native-api = { path = "../native-api" } +tracing.workspace = true [dev-dependencies] assert_fs.workspace = true diff --git a/crates/scarb-api/src/artifacts.rs b/crates/scarb-api/src/artifacts.rs index f3c519e021..bd464a7ec6 100644 --- a/crates/scarb-api/src/artifacts.rs +++ b/crates/scarb-api/src/artifacts.rs @@ -54,6 +54,7 @@ impl StarknetArtifactsFiles { self } + #[tracing::instrument(skip_all, level = "debug")] pub(crate) fn load_contracts_artifacts( self, ) -> Result> { @@ -79,6 +80,7 @@ impl StarknetArtifactsFiles { Ok(base_artifacts) } + #[tracing::instrument(skip_all, level = "debug")] fn compile_artifacts( &self, artifacts: Vec<(String, Utf8PathBuf)>, @@ -92,6 +94,7 @@ impl StarknetArtifactsFiles { .collect::>() } + #[tracing::instrument(skip_all, level = "debug")] fn compile_artifact_at_path(&self, path: &Utf8Path) -> Result { let sierra = fs::read_to_string(path)?; @@ -106,6 +109,7 @@ impl StarknetArtifactsFiles { }) } + #[tracing::instrument(skip_all, level = "debug")] fn compile_to_native(&self, sierra: &str) -> Result> { Ok(if self.compile_native { Some(native_api::compile_contract_class(&serde_json::from_str( @@ -117,6 +121,7 @@ impl StarknetArtifactsFiles { } } +#[tracing::instrument(skip_all, level = "debug")] fn unique_artifacts( artifact_representations: Vec, current_artifacts: &HashMap, diff --git a/crates/scarb-api/src/lib.rs b/crates/scarb-api/src/lib.rs index 8c6d173277..d1740d436f 100644 --- a/crates/scarb-api/src/lib.rs +++ b/crates/scarb-api/src/lib.rs @@ -23,6 +23,7 @@ const INTEGRATION_TEST_TYPE: &str = "integration"; /// If artifacts with `test_type` of `INTEGRATION_TEST_TYPE` are present, we use them base path /// and extend with paths to other artifacts. /// If `INTEGRATION_TEST_TYPE` is not present, we take first artifacts found. +#[tracing::instrument(skip_all, level = "debug")] fn get_starknet_artifacts_paths_from_test_targets( target_dir: &Utf8Path, test_targets: &HashMap, @@ -72,6 +73,7 @@ fn get_starknet_artifacts_paths_from_test_targets( /// Try getting the path to `starknet_artifacts.json` related to `starknet-contract` target. This file that is generated by `scarb build` command. /// If the file is not present, `None` is returned. +#[tracing::instrument(skip_all, level = "debug")] fn get_starknet_artifacts_path( target_dir: &Utf8Path, target_name: &str, @@ -100,6 +102,7 @@ pub struct CompilationOpts { } /// Get the map with `StarknetContractArtifacts` for the given package +#[tracing::instrument(skip_all, level = "debug")] pub fn get_contracts_artifacts_and_source_sierra_paths( artifacts_dir: &Utf8Path, package: &PackageMetadata, diff --git a/crates/universal-sierra-compiler-api/Cargo.toml b/crates/universal-sierra-compiler-api/Cargo.toml index 72210181ad..25da1de816 100644 --- a/crates/universal-sierra-compiler-api/Cargo.toml +++ b/crates/universal-sierra-compiler-api/Cargo.toml @@ -13,3 +13,4 @@ tempfile.workspace = true num-bigint.workspace = true cairo-lang-casm.workspace = true camino.workspace = true +tracing.workspace = true \ No newline at end of file diff --git a/crates/universal-sierra-compiler-api/src/lib.rs b/crates/universal-sierra-compiler-api/src/lib.rs index a75f95bb66..eeb61753d2 100644 --- a/crates/universal-sierra-compiler-api/src/lib.rs +++ b/crates/universal-sierra-compiler-api/src/lib.rs @@ -66,6 +66,7 @@ pub fn compile_sierra( ) } +#[tracing::instrument(skip_all, level = "debug")] pub fn compile_sierra_at_path( sierra_file_path: &Utf8Path, sierra_type: &SierraType, From bda40434874d65e43cff762bab7e9f581fe96a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20Micha=C5=82ek?= <52135326+cptartur@users.noreply.github.com> Date: Thu, 2 Oct 2025 17:08:35 +0200 Subject: [PATCH 34/45] Keep cairo native behind a feature flag (#3745) Closes # ## Introduced changes - ## Checklist - [ ] Linked relevant issue - [ ] Updated relevant documentation - [ ] Added relevant tests - [ ] Performed self-review of the code - [ ] Added changes to `CHANGELOG.md` --- .../action.yml | 27 ++- .github/workflows/ci.yml | 169 ++++++++++++------ Cargo.toml | 2 +- crates/cheatnet/Cargo.toml | 7 +- .../execution/entry_point.rs | 3 +- .../cheatcodes/declare.rs | 8 +- crates/cheatnet/src/runtime_extensions/mod.rs | 1 + crates/cheatnet/tests/common/mod.rs | 1 + crates/forge/Cargo.toml | 5 +- crates/forge/src/lib.rs | 11 +- .../forge/src/profile_validation/backtrace.rs | 3 + crates/forge/src/run_tests/package.rs | 1 + crates/forge/test_utils/Cargo.toml | 3 +- crates/forge/test_utils/src/runner.rs | 2 + crates/forge/tests/e2e/backtrace.rs | 1 + crates/forge/tests/e2e/running.rs | 1 + crates/scarb-api/Cargo.toml | 7 +- crates/scarb-api/src/artifacts.rs | 21 ++- crates/scarb-api/src/lib.rs | 9 +- crates/sncast/src/helpers/artifacts.rs | 8 + crates/sncast/src/helpers/mod.rs | 1 + crates/sncast/src/helpers/scarb_utils.rs | 9 +- .../sncast/src/starknet_commands/declare.rs | 4 +- .../src/starknet_commands/script/run.rs | 16 +- .../src/starknet_commands/utils/class_hash.rs | 4 +- .../src/starknet_commands/verify/mod.rs | 6 +- 26 files changed, 226 insertions(+), 104 deletions(-) rename .github/actions/{setup-rust-llvm => setup-tools}/action.yml (54%) create mode 100644 crates/sncast/src/helpers/artifacts.rs diff --git a/.github/actions/setup-rust-llvm/action.yml b/.github/actions/setup-tools/action.yml similarity index 54% rename from .github/actions/setup-rust-llvm/action.yml rename to .github/actions/setup-tools/action.yml index 35f51173b3..2d4e6d6004 100644 --- a/.github/actions/setup-rust-llvm/action.yml +++ b/.github/actions/setup-tools/action.yml @@ -1,5 +1,19 @@ -name: Setup Rust + LLVM 19 -description: Installs Rust and LLVM 19 toolchain +name: Setup Tools +description: Installs Rust and optionally Scarb, Universal Sierra Compiler and LLVM 19 toolchain + +inputs: + install-llvm: + description: 'Whether to install the LLVM 19 toolchain' + required: false + default: 'false' + setup-scarb: + description: 'Whether to setup scarb' + required: false + default: 'true' + setup-usc: + description: 'Whether to setup universal-sierra-compiler' + required: false + default: 'true' runs: using: "composite" @@ -8,8 +22,15 @@ runs: - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: software-mansion/setup-scarb@v1 + if: ${{ inputs.setup-scarb == 'true' }} + + - uses: software-mansion/setup-universal-sierra-compiler@v1 + if: ${{ inputs.setup-usc == 'true' }} + - name: Add LLVM APT repository uses: myci-actions/add-deb-repo@11 + if: ${{ inputs.install-llvm == 'true' }} with: repo: deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main repo-name: llvm-repo @@ -17,6 +38,7 @@ runs: - name: Install LLVM shell: bash + if: ${{ inputs.install-llvm == 'true' }} run: | sudo apt-get update sudo apt-get install -y \ @@ -25,6 +47,7 @@ runs: lld-19 libpolly-19-dev libmlir-19-dev mlir-19-tools - name: Set environment variables + if: ${{ inputs.install-llvm == 'true' }} shell: bash run: | echo "MLIR_SYS_190_PREFIX=/usr/lib/llvm-19/" >> $GITHUB_ENV diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28748ec7b6..9f732f797a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,34 +18,53 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm - - uses: software-mansion/setup-scarb@v1 - - uses: software-mansion/setup-universal-sierra-compiler@v1 + - uses: ./.github/actions/setup-tools - run: cargo test --profile ci --lib -p forge build-test-forge-nextest-archive: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm + - uses: ./.github/actions/setup-tools + with: + setup-scarb: 'false' + setup-usc: 'false' - name: Install nextest uses: taiki-e/install-action@v2 with: tool: nextest@0.9.98 - name: Build and archive tests - # TODO: run native only on scheduled workflow - run: cargo nextest archive --cargo-profile ci -p forge --features skip_plugin_checks,run-native --archive-file 'nextest-archive-${{ runner.os }}.tar.zst' + run: cargo nextest archive --cargo-profile ci -p forge --features skip_plugin_checks --archive-file 'nextest-archive-${{ runner.os }}.tar.zst' - name: Upload archive to workflow uses: actions/upload-artifact@v4 with: name: nextest-archive-${{ runner.os }} path: nextest-archive-${{ runner.os }}.tar.zst + build-test-forge-nextest-archive-native: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - uses: ./.github/actions/setup-tools + with: + install-llvm: 'true' + setup-scarb: 'false' + setup-usc: 'false' + - name: Install nextest + uses: taiki-e/install-action@v2 + with: + tool: nextest@0.9.98 + - name: Build and archive tests + run: cargo nextest archive --cargo-profile ci -p forge --features skip_plugin_checks,run-native --archive-file 'nextest-archive-${{ runner.os }}-native.tar.zst' + - name: Upload archive to workflow + uses: actions/upload-artifact@v4 + with: + name: nextest-archive-${{ runner.os }}-native + path: nextest-archive-${{ runner.os }}-native.tar.zst + test-forge-integration: name: Test Forge / Integration Tests - runs-on: [ubuntu-latest] + runs-on: [ ubuntu-latest ] needs: [ build-test-forge-nextest-archive ] strategy: fail-fast: false @@ -53,10 +72,7 @@ jobs: partition: [ 1, 2, 3 ] steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - - uses: software-mansion/setup-scarb@v1 - - uses: software-mansion/setup-universal-sierra-compiler@v1 + - uses: ./.github/actions/setup-tools - uses: taiki-e/install-action@v2 with: tool: nextest@0.9.98 @@ -103,11 +119,8 @@ jobs: curl -L https://raw.githubusercontent.com/software-mansion/cairo-coverage/main/scripts/install.sh | sh - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - uses: asdf-vm/actions/install@1902764435ca0dd2f3388eea723a4f92a4eb8302 - - uses: software-mansion/setup-scarb@v1 - - uses: software-mansion/setup-universal-sierra-compiler@v1 + - uses: ./.github/actions/setup-tools - uses: taiki-e/install-action@v2 with: tool: nextest@0.9.98 @@ -117,13 +130,62 @@ jobs: - name: nextest partition ${{ matrix.partition }}/3 run: cargo nextest run --no-fail-fast --partition 'count:${{ matrix.partition }}/3' --archive-file 'nextest-archive-${{ runner.os }}.tar.zst' e2e + test-forge-e2e-native: + name: Test Forge / E2E Tests (native) + runs-on: ubuntu-latest + needs: [ build-test-forge-nextest-archive-native ] + strategy: + fail-fast: false + matrix: + partition: [ 1, 2, 3 ] + steps: + - name: Extract branch name + if: github.event_name != 'pull_request' + run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV + shell: bash + + - name: Extract branch name on pull request + if: github.event_name == 'pull_request' + run: echo "BRANCH_NAME=$(echo $GITHUB_HEAD_REF)" >> $GITHUB_ENV + shell: bash + + - name: Extract repo name and owner + if: github.event_name != 'pull_request' + run: echo "REPO_NAME=$(echo ${{ github.repository }}.git)" >> $GITHUB_ENV + shell: bash + + - name: Extract repo name and owner on pull request + if: github.event_name == 'pull_request' + run: echo "REPO_NAME=$(echo ${{ github.event.pull_request.head.repo.full_name }}.git)" >> $GITHUB_ENV + shell: bash + + - name: Install cairo-profiler + run: | + curl -L https://raw.githubusercontent.com/software-mansion/cairo-profiler/main/scripts/install.sh | sh + - name: Install cairo-coverage + run: | + curl -L https://raw.githubusercontent.com/software-mansion/cairo-coverage/main/scripts/install.sh | sh + + - uses: actions/checkout@v5 + - uses: asdf-vm/actions/install@1902764435ca0dd2f3388eea723a4f92a4eb8302 + - uses: ./.github/actions/setup-tools + - uses: taiki-e/install-action@v2 + with: + tool: nextest@0.9.98 + - uses: actions/download-artifact@v4 + with: + name: nextest-archive-${{ runner.os }}-native + - name: nextest partition ${{ matrix.partition }}/3 + run: cargo nextest run --no-fail-fast --partition 'count:${{ matrix.partition }}/3' --archive-file 'nextest-archive-${{ runner.os }}-native.tar.zst' e2e + test-plugin-checks: name: Test plugin across different scarb versions runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm - - uses: software-mansion/setup-universal-sierra-compiler@v1 + - uses: ./.github/actions/setup-tools + with: + setup-scarb: 'false' - uses: asdf-vm/actions/install@1902764435ca0dd2f3388eea723a4f92a4eb8302 - run: cargo test --profile ci --package forge e2e::plugin_versions - run: cargo test --profile ci --package forge e2e::plugin_diagnostics @@ -133,8 +195,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm - - uses: software-mansion/setup-universal-sierra-compiler@v1 + - uses: ./.github/actions/setup-tools + with: + setup-scarb: 'false' - run: cargo test --profile ci --package forge --features no_scarb_installed --lib compatibility_check::tests::failing_tool_not_installed @@ -150,9 +213,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm - - uses: software-mansion/setup-scarb@v1 - - uses: software-mansion/setup-universal-sierra-compiler@v1 + - uses: ./.github/actions/setup-tools - run: cargo test --profile ci --package forge --features interact-with-state --test main integration::interact_with_state - run: cargo test --profile ci --package forge --features interact-with-state --test main e2e::running::test_interact_with_state @@ -162,7 +223,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm + - uses: ./.github/actions/setup-tools - run: cargo test --profile ci -p forge_runner test-cheatnet: @@ -170,19 +231,16 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm - - uses: software-mansion/setup-scarb@v1 - - uses: software-mansion/setup-universal-sierra-compiler@v1 + - uses: ./.github/actions/setup-tools - name: Run Cheatnet tests - # TODO: run native only on scheduled workflow - run: cargo test --profile ci -p cheatnet --features run-native + run: cargo test --profile ci -p cheatnet test-data-transformer: name: Test Data Transformer runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm + - uses: ./.github/actions/setup-tools - name: Run Data Transformer tests run: cargo test --profile ci -p data-transformer @@ -191,22 +249,16 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm - - uses: software-mansion/setup-scarb@v1 - - uses: software-mansion/setup-universal-sierra-compiler@58146c4184fa6ec5e8aaf02309ab85e35f782ed0 # v1.0.0 + - uses: ./.github/actions/setup-tools - name: Run Forge Debugging tests - # TODO: run native only on scheduled workflow - run: cargo test --profile ci -p forge --features debugging,run-native --test main e2e::debugging + run: cargo test --profile ci -p forge --features debugging --test main e2e::debugging test-forge-scarb-plugin: name: Test Forge Scarb Plugin runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - - uses: software-mansion/setup-scarb@v1 - - uses: software-mansion/setup-universal-sierra-compiler@v1 + - uses: ./.github/actions/setup-tools - name: Run Forge Scarb Plugin tests working-directory: crates/snforge-scarb-plugin run: cargo test --profile ci @@ -216,10 +268,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - - uses: software-mansion/setup-scarb@v1 - - uses: software-mansion/setup-universal-sierra-compiler@v1 + - uses: ./.github/actions/setup-tools - name: Run Forge Scarb Plugin tests working-directory: crates/snforge-scarb-plugin-deprecated run: cargo test --profile ci @@ -229,10 +278,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm - uses: asdf-vm/actions/install@05e0d2ed97b598bfce82fd30daf324ae0c4570e6 - - uses: software-mansion/setup-scarb@v1 - - uses: software-mansion/setup-universal-sierra-compiler@v1 + - uses: ./.github/actions/setup-tools - name: Run tests run: cargo test --profile ci -p sncast @@ -241,7 +288,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm + - uses: ./.github/actions/setup-tools + with: + setup-scarb: 'false' + setup-usc: 'false' - name: Run tests run: cargo test --profile ci -p conversions @@ -250,8 +300,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + - uses: ./.github/actions/setup-tools + with: + setup-scarb: 'false' + setup-usc: 'false' - run: cargo test --profile ci -p shared test-scarb-api: @@ -259,11 +311,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm - - uses: software-mansion/setup-scarb@v1 - - uses: software-mansion/setup-universal-sierra-compiler@v1 - # TODO: run native only on scheduled workflow - - run: cargo test --profile ci -p scarb-api --features run-native + - uses: ./.github/actions/setup-tools + - run: cargo test --profile ci -p scarb-api scarbfmt: runs-on: ubuntu-latest @@ -308,7 +357,11 @@ jobs: RUSTFLAGS: "-Dwarnings" steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm + - uses: ./.github/actions/setup-tools + with: + install-llvm: 'true' + setup-scarb: 'false' + setup-usc: 'false' - run: cargo lint - name: Lint snforge-scarb-plugin @@ -326,9 +379,7 @@ jobs: MDBOOK_VERSION: 0.4.52 steps: - uses: actions/checkout@v5 - - uses: ./.github/actions/setup-rust-llvm - - uses: software-mansion/setup-scarb@v1 - - uses: software-mansion/setup-universal-sierra-compiler@v1 + - uses: ./.github/actions/setup-tools - name: Install mdBook run: | cargo install --version ${MDBOOK_VERSION} mdbook diff --git a/Cargo.toml b/Cargo.toml index 2828eb6a6b..0aefc2694e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ license-file = "LICENSE" [workspace.dependencies] # TODO do not use fork here, after apollo-compile-to-native dependency problem is resolved -blockifier = { git = "https://github.com/cptartur/sequencer.git", branch = "main-v0.14.0-fork-fix", features = ["testing", "tracing", "cairo_native"] } +blockifier = { git = "https://github.com/cptartur/sequencer.git", branch = "main-v0.14.0-fork-fix", features = ["testing", "tracing"] } bigdecimal = "0.4.8" starknet_api = { git = "https://github.com/cptartur/sequencer.git", branch = "main-v0.14.0-fork-fix" } cairo-native = { git = "https://github.com/lambdaclass/cairo_native.git", rev = "d7f8352657a4c1228b227807b90aea10f97f746d" } diff --git a/crates/cheatnet/Cargo.toml b/crates/cheatnet/Cargo.toml index acafd8dd4f..81e52e548d 100644 --- a/crates/cheatnet/Cargo.toml +++ b/crates/cheatnet/Cargo.toml @@ -5,7 +5,8 @@ edition.workspace = true [features] testing = [] -run-native = [] +cairo-native = ["dep:cairo-native", "blockifier/cairo_native"] +run-native = ["cairo-native"] [dependencies] anyhow.workspace = true @@ -44,8 +45,8 @@ p256.workspace = true shared.workspace = true rand.workspace = true foundry-ui = { path = "../foundry-ui" } -scarb-oracle-hint-service.workspace = true -cairo-native.workspace = true +scarb-oracle-hint-service.workspace = true +cairo-native = { workspace = true, optional = true } [dev-dependencies] ctor.workspace = true diff --git a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs index d9577edf3b..af262f52e0 100644 --- a/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs +++ b/crates/cheatnet/src/runtime_extensions/call_to_blockifier_runtime_extension/execution/entry_point.rs @@ -8,6 +8,7 @@ use crate::state::CheatStatus; use blockifier::execution::call_info::{CallExecution, Retdata, StorageAccessTracker}; use blockifier::execution::contract_class::{RunnableCompiledClass, TrackedResource}; +#[cfg(feature = "cairo-native")] use crate::runtime_extensions::native::execution::execute_entry_point_call_native; use blockifier::execution::entry_point::{EntryPointRevertInfo, ExecutableCallEntryPoint}; use blockifier::execution::stack_trace::{ @@ -175,6 +176,7 @@ pub fn execute_call_entry_point( cheatnet_state, context, ), + #[cfg(feature = "cairo-native")] RunnableCompiledClass::V1Native(native_compiled_class_v1) => { if context.tracked_resource_stack.last() == Some(&TrackedResource::CairoSteps) { execute_entry_point_call_cairo1( @@ -185,7 +187,6 @@ pub fn execute_call_entry_point( context, ) } else { - println!("Executing native entry point"); execute_entry_point_call_native( &entry_point, &native_compiled_class_v1, diff --git a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs index 010b96193e..2adb08203e 100644 --- a/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs +++ b/crates/cheatnet/src/runtime_extensions/forge_runtime_extension/cheatcodes/declare.rs @@ -5,6 +5,7 @@ use crate::runtime_extensions::forge_runtime_extension::{ }; use anyhow::{Context, Result}; use blockifier::execution::contract_class::{CompiledClassV1, RunnableCompiledClass}; +#[cfg(feature = "cairo-native")] use blockifier::execution::native::contract_class::NativeCompiledClassV1; use blockifier::state::{errors::StateError, state_api::State}; use conversions::IntoConv; @@ -73,11 +74,14 @@ fn get_contract_class(contract_artifact: &StarknetContractArtifacts) -> Runnable ) .expect("Failed to read contract class from json"); - match &contract_artifact.executor { + #[cfg(feature = "cairo-native")] + return match &contract_artifact.executor { None => RunnableCompiledClass::V1(contract_class), Some(executor) => RunnableCompiledClass::V1Native(NativeCompiledClassV1::new( executor.clone(), contract_class, )), - } + }; + #[cfg(not(feature = "cairo-native"))] + RunnableCompiledClass::V1(contract_class) } diff --git a/crates/cheatnet/src/runtime_extensions/mod.rs b/crates/cheatnet/src/runtime_extensions/mod.rs index 3db8b52ecc..ac32c9bdf1 100644 --- a/crates/cheatnet/src/runtime_extensions/mod.rs +++ b/crates/cheatnet/src/runtime_extensions/mod.rs @@ -4,4 +4,5 @@ pub mod common; pub mod deprecated_cheatable_starknet_extension; pub mod forge_config_extension; pub mod forge_runtime_extension; +#[cfg(feature = "cairo-native")] mod native; diff --git a/crates/cheatnet/tests/common/mod.rs b/crates/cheatnet/tests/common/mod.rs index 1398cc9a65..7d3a220079 100644 --- a/crates/cheatnet/tests/common/mod.rs +++ b/crates/cheatnet/tests/common/mod.rs @@ -92,6 +92,7 @@ pub fn get_contracts() -> ContractsData { &ui, CompilationOpts { use_test_target_contracts: false, + #[cfg(feature = "cairo-native")] run_native: cfg!(feature = "run-native"), }, ) diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index b80e804a35..8346dd6baf 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -13,7 +13,8 @@ debugging = [] assert_non_exact_gas = ["test_utils/assert_non_exact_gas"] skip_plugin_checks = [] interact-with-state = [] -run-native = ["test_utils/run-native"] +cairo-native = ["test_utils/cairo-native", "cheatnet/cairo-native", "scarb-api/cairo-native"] +run-native = ["cairo-native"] [dependencies] anyhow.workspace = true @@ -73,4 +74,4 @@ walkdir.workspace = true test-case.workspace = true itertools.workspace = true docs = { workspace = true, features = ["testing"] } -packages_validation = { path = "../testing/packages_validation"} +packages_validation = { path = "../testing/packages_validation" } diff --git a/crates/forge/src/lib.rs b/crates/forge/src/lib.rs index 024cd985ff..79be89e774 100644 --- a/crates/forge/src/lib.rs +++ b/crates/forge/src/lib.rs @@ -146,6 +146,7 @@ pub struct TestArgs { /// /// Note: Only contracts execution through native is supported, test code itself will still run on `cairo-vm`. #[arg(long)] + #[cfg(feature = "cairo-native")] run_native: bool, /// Use exact matches for `test_filter` @@ -187,15 +188,18 @@ pub struct TestArgs { rerun_failed: bool, /// Save execution traces of all test which have passed and are not fuzz tests - #[arg(long, conflicts_with = "run_native")] + #[arg(long)] + #[cfg_attr(feature = "cairo-native", arg(conflicts_with = "run_native"))] save_trace_data: bool, /// Build profiles of all tests which have passed and are not fuzz tests using the cairo-profiler - #[arg(long, conflicts_with_all = ["run_native", "coverage"])] + #[arg(long, conflicts_with_all = ["coverage"])] + #[cfg_attr(feature = "cairo-native", arg(conflicts_with_all = ["run_native", "coverage"]))] build_profile: bool, /// Generate a coverage report for the executed tests which have passed and are not fuzz tests using the cairo-coverage - #[arg(long, conflicts_with_all = ["run_native", "build_profile"])] + #[arg(long, conflicts_with_all = ["build_profile"])] + #[cfg_attr(feature = "cairo-native", arg(conflicts_with_all = ["run_native", "build_profile"]))] coverage: bool, /// Number of maximum steps during a single test. For fuzz tests this value is applied to each subtest separately. @@ -230,6 +234,7 @@ impl TestArgs { pub fn normalize(&mut self) { // Force using `SierraGas` as tracked resource when running with `cairo-native`, // as otherwise it would run on vm. + #[cfg(feature = "cairo-native")] if self.run_native { self.tracked_resource = ForgeTrackedResource::SierraGas; } diff --git a/crates/forge/src/profile_validation/backtrace.rs b/crates/forge/src/profile_validation/backtrace.rs index 068647d65d..80c37e3c37 100644 --- a/crates/forge/src/profile_validation/backtrace.rs +++ b/crates/forge/src/profile_validation/backtrace.rs @@ -7,10 +7,12 @@ use semver::Version; /// Checks if backtrace can be generated based on scarb version, profile settings extracted from /// the provided [`Metadata`] and if native execution is disabled in the provided [`TestArgs`]. +#[allow(unused_variables)] pub fn check_backtrace_compatibility( test_args: &TestArgs, scarb_metadata: &Metadata, ) -> anyhow::Result<()> { + #[cfg(feature = "cairo-native")] check_if_native_disabled(test_args)?; check_scarb_version(scarb_metadata)?; check_profile(scarb_metadata)?; @@ -18,6 +20,7 @@ pub fn check_backtrace_compatibility( } /// Checks if native execution is disabled in the provided [`TestArgs`]. +#[cfg(feature = "cairo-native")] fn check_if_native_disabled(test_args: &TestArgs) -> anyhow::Result<()> { ensure!( !test_args.run_native, diff --git a/crates/forge/src/run_tests/package.rs b/crates/forge/src/run_tests/package.rs index 39344a27ff..8a1d83d375 100644 --- a/crates/forge/src/run_tests/package.rs +++ b/crates/forge/src/run_tests/package.rs @@ -89,6 +89,7 @@ impl RunForPackageArgs { &scarb_metadata.app_version_info.version, args.no_optimization, ), + #[cfg(feature = "cairo-native")] run_native: args.run_native, }, )?; diff --git a/crates/forge/test_utils/Cargo.toml b/crates/forge/test_utils/Cargo.toml index 56c41eee00..dbabfd0cb0 100644 --- a/crates/forge/test_utils/Cargo.toml +++ b/crates/forge/test_utils/Cargo.toml @@ -7,7 +7,8 @@ edition.workspace = true [features] assert_non_exact_gas = [] -run-native = [] +cairo-native = [] +run-native = ["cairo-native"] [dependencies] walkdir.workspace = true diff --git a/crates/forge/test_utils/src/runner.rs b/crates/forge/test_utils/src/runner.rs index 6282c43a7a..d741e3daa0 100644 --- a/crates/forge/test_utils/src/runner.rs +++ b/crates/forge/test_utils/src/runner.rs @@ -111,6 +111,7 @@ impl Contract { ui, CompilationOpts { use_test_target_contracts: false, + #[cfg(feature = "cairo-native")] run_native: cfg!(feature = "run-native"), }, ) @@ -232,6 +233,7 @@ impl<'a> TestCase { StarknetContractArtifacts { sierra, casm, + #[cfg(feature = "cairo-native")] executor: None, }, Utf8PathBuf::default(), diff --git a/crates/forge/tests/e2e/backtrace.rs b/crates/forge/tests/e2e/backtrace.rs index 4d84ba088d..0eb5ce85c6 100644 --- a/crates/forge/tests/e2e/backtrace.rs +++ b/crates/forge/tests/e2e/backtrace.rs @@ -23,6 +23,7 @@ fn test_backtrace_missing_env() { ); } +#[cfg_attr(not(feature = "run-native"), ignore)] #[test] fn test_backtrace_native_execution() { let temp = setup_package("backtrace_vm_error"); diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 907ec7ef8a..b78ce45de6 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -53,6 +53,7 @@ fn simple_package() { ); } +#[cfg_attr(not(feature = "run-native"), ignore)] #[test] fn simple_package_native() { let temp = setup_package("simple_package"); diff --git a/crates/scarb-api/Cargo.toml b/crates/scarb-api/Cargo.toml index 47f8a51551..249684c054 100644 --- a/crates/scarb-api/Cargo.toml +++ b/crates/scarb-api/Cargo.toml @@ -4,7 +4,8 @@ version = "1.0.0" edition.workspace = true [features] -run-native = [] +cairo-native = ["dep:cairo-native", "dep:native-api"] +run-native = ["cairo-native"] [dependencies] anyhow.workspace = true @@ -22,8 +23,8 @@ rayon.workspace = true itertools.workspace = true universal-sierra-compiler-api = { path = "../universal-sierra-compiler-api" } foundry-ui = { path = "../foundry-ui" } -cairo-native.workspace = true -native-api = { path = "../native-api" } +cairo-native = { workspace = true, optional = true } +native-api = { path = "../native-api", optional = true } tracing.workspace = true [dev-dependencies] diff --git a/crates/scarb-api/src/artifacts.rs b/crates/scarb-api/src/artifacts.rs index bd464a7ec6..2a9cb27cd5 100644 --- a/crates/scarb-api/src/artifacts.rs +++ b/crates/scarb-api/src/artifacts.rs @@ -1,6 +1,7 @@ use anyhow::Result; use crate::artifacts::representation::StarknetArtifactsRepresentation; +#[cfg(feature = "cairo-native")] use cairo_native::executor::AotContractExecutor; use camino::{Utf8Path, Utf8PathBuf}; use itertools::Itertools; @@ -20,16 +21,19 @@ pub struct StarknetContractArtifacts { /// Compiled casm code pub casm: String, + #[cfg(feature = "cairo-native")] /// Optional AOT compiled native executor pub executor: Option, } impl PartialEq for StarknetContractArtifacts { fn eq(&self, other: &Self) -> bool { - self.sierra == other.sierra - && self.casm == other.casm - // We only check if both have an executor or not, as the actual executor does not implement PartialEq - && self.executor.is_some() == other.executor.is_some() + let eq = self.sierra == other.sierra && self.casm == other.casm; + + #[cfg(feature = "cairo-native")] + let eq = eq && self.executor.is_some() == other.executor.is_some(); + + eq } } @@ -37,6 +41,7 @@ impl PartialEq for StarknetContractArtifacts { pub(crate) struct StarknetArtifactsFiles { base: Utf8PathBuf, other: Vec, + #[cfg(feature = "cairo-native")] compile_native: bool, } @@ -45,10 +50,12 @@ impl StarknetArtifactsFiles { Self { base: base_file, other: other_files, + #[cfg(feature = "cairo-native")] compile_native: false, } } + #[cfg(feature = "cairo-native")] pub(crate) fn compile_native(mut self, compile_native: bool) -> Self { self.compile_native = compile_native; self @@ -95,20 +102,24 @@ impl StarknetArtifactsFiles { } #[tracing::instrument(skip_all, level = "debug")] + #[cfg_attr(not(feature = "cairo-native"), expect(clippy::unused_self))] fn compile_artifact_at_path(&self, path: &Utf8Path) -> Result { let sierra = fs::read_to_string(path)?; let casm = compile_sierra_at_path(path, &SierraType::Contract)?; + #[cfg(feature = "cairo-native")] let executor = self.compile_to_native(&sierra)?; Ok(StarknetContractArtifacts { sierra, casm, + #[cfg(feature = "cairo-native")] executor, }) } + #[cfg(feature = "cairo-native")] #[tracing::instrument(skip_all, level = "debug")] fn compile_to_native(&self, sierra: &str) -> Result> { Ok(if self.compile_native { @@ -155,6 +166,7 @@ mod tests { StarknetContractArtifacts { sierra: "sierra1".to_string(), casm: "casm1".to_string(), + #[cfg(feature = "cairo-native")] executor: None, }, Utf8PathBuf::from("path1"), @@ -258,6 +270,7 @@ mod tests { } #[test] + #[cfg(feature = "cairo-native")] fn test_load_contracts_artifacts_native() { let (_temp, artifacts_files) = setup(); diff --git a/crates/scarb-api/src/lib.rs b/crates/scarb-api/src/lib.rs index d1740d436f..7aff1dc26b 100644 --- a/crates/scarb-api/src/lib.rs +++ b/crates/scarb-api/src/lib.rs @@ -98,6 +98,7 @@ fn get_starknet_artifacts_path( #[derive(Default)] pub struct CompilationOpts { pub use_test_target_contracts: bool, + #[cfg(feature = "cairo-native")] pub run_native: bool, } @@ -109,6 +110,7 @@ pub fn get_contracts_artifacts_and_source_sierra_paths( ui: &UI, CompilationOpts { use_test_target_contracts, + #[cfg(feature = "cairo-native")] run_native, }: CompilationOpts, ) -> Result> { @@ -127,9 +129,9 @@ pub fn get_contracts_artifacts_and_source_sierra_paths( }; if let Some(starknet_artifact_files) = starknet_artifact_files { - starknet_artifact_files - .compile_native(run_native) - .load_contracts_artifacts() + #[cfg(feature = "cairo-native")] + let starknet_artifact_files = starknet_artifact_files.compile_native(run_native); + starknet_artifact_files.load_contracts_artifacts() } else { Ok(HashMap::default()) } @@ -594,6 +596,7 @@ mod tests { &ui, CompilationOpts { use_test_target_contracts: false, + #[cfg(feature = "cairo-native")] run_native: cfg!(feature = "run-native"), }, ) diff --git a/crates/sncast/src/helpers/artifacts.rs b/crates/sncast/src/helpers/artifacts.rs new file mode 100644 index 0000000000..74d5395582 --- /dev/null +++ b/crates/sncast/src/helpers/artifacts.rs @@ -0,0 +1,8 @@ +/// Contains compiled Starknet artifacts +#[derive(Debug, Clone)] +pub struct CastStarknetContractArtifacts { + /// Compiled sierra code + pub sierra: String, + /// Compiled casm code + pub casm: String, +} diff --git a/crates/sncast/src/helpers/mod.rs b/crates/sncast/src/helpers/mod.rs index 66f7c099c3..6380665463 100644 --- a/crates/sncast/src/helpers/mod.rs +++ b/crates/sncast/src/helpers/mod.rs @@ -1,4 +1,5 @@ pub mod account; +pub mod artifacts; pub mod block_explorer; pub mod braavos; pub mod config; diff --git a/crates/sncast/src/helpers/scarb_utils.rs b/crates/sncast/src/helpers/scarb_utils.rs index 180cbd6761..0f8ab6a6b1 100644 --- a/crates/sncast/src/helpers/scarb_utils.rs +++ b/crates/sncast/src/helpers/scarb_utils.rs @@ -1,8 +1,9 @@ +use crate::helpers::artifacts::CastStarknetContractArtifacts; use anyhow::{Context, Result, anyhow}; use camino::{Utf8Path, Utf8PathBuf}; use foundry_ui::{UI, components::warning::WarningMessage}; use scarb_api::{ - CompilationOpts, ScarbCommand, ScarbCommandError, StarknetContractArtifacts, + CompilationOpts, ScarbCommand, ScarbCommandError, get_contracts_artifacts_and_source_sierra_paths, metadata::{Metadata, MetadataCommand, PackageMetadata}, target_dir_for_workspace, @@ -157,7 +158,7 @@ pub fn build_and_load_artifacts( config: &BuildConfig, build_for_script: bool, ui: &UI, -) -> Result> { +) -> Result> { // TODO (#2042): Remove this logic, always use release as default let default_profile = if build_for_script { "dev" } else { "release" }; build(package, config, default_profile) @@ -175,7 +176,7 @@ pub fn build_and_load_artifacts( CompilationOpts::default() ).context("Failed to load artifacts. Make sure you have enabled sierra code generation in Scarb.toml")? .into_iter() - .map(|(name, (artifacts, _))| (name, artifacts)) + .map(|(name, (artifacts, _))| (name, CastStarknetContractArtifacts { sierra: artifacts.sierra, casm: artifacts.casm })) .collect()) } else { let profile = &config.profile; @@ -190,7 +191,7 @@ pub fn build_and_load_artifacts( CompilationOpts::default(), ).context("Failed to load artifacts. Make sure you have enabled sierra code generation in Scarb.toml")? .into_iter() - .map(|(name, (artifacts, _))| (name, artifacts)) + .map(|(name, (artifacts, _))| (name, CastStarknetContractArtifacts { sierra: artifacts.sierra, casm: artifacts.casm })) .collect()) } } diff --git a/crates/sncast/src/starknet_commands/declare.rs b/crates/sncast/src/starknet_commands/declare.rs index 1c9e850be2..d72bc2fdf9 100644 --- a/crates/sncast/src/starknet_commands/declare.rs +++ b/crates/sncast/src/starknet_commands/declare.rs @@ -3,7 +3,7 @@ use clap::Args; use conversions::IntoConv; use conversions::byte_array::ByteArray; use foundry_ui::UI; -use scarb_api::StarknetContractArtifacts; +use sncast::helpers::artifacts::CastStarknetContractArtifacts; use sncast::helpers::fee::{FeeArgs, FeeSettings}; use sncast::helpers::rpc::RpcArgs; use sncast::response::declare::{ @@ -50,7 +50,7 @@ pub struct Declare { pub async fn declare( declare: Declare, account: &SingleOwnerAccount<&JsonRpcClient, LocalWallet>, - artifacts: &HashMap, + artifacts: &HashMap, wait_config: WaitForTx, skip_on_already_declared: bool, ui: &UI, diff --git a/crates/sncast/src/starknet_commands/script/run.rs b/crates/sncast/src/starknet_commands/script/run.rs index c1189d594a..0031871f38 100644 --- a/crates/sncast/src/starknet_commands/script/run.rs +++ b/crates/sncast/src/starknet_commands/script/run.rs @@ -32,12 +32,13 @@ use runtime::{ CheatcodeHandlingResult, EnhancedHintError, ExtendedRuntime, ExtensionLogic, StarknetRuntime, SyscallHandlingResult, }; -use scarb_api::{StarknetContractArtifacts, package_matches_version_requirement}; +use scarb_api::package_matches_version_requirement; use scarb_metadata::{Metadata, PackageMetadata}; use script_runtime::CastScriptRuntime; use semver::{Comparator, Op, Version, VersionReq}; use shared::utils::build_readable_text; use sncast::get_nonce; +use sncast::helpers::artifacts::CastStarknetContractArtifacts; use sncast::helpers::configuration::CastConfig; use sncast::helpers::constants::SCRIPT_LIB_ARTIFACT_NAME; use sncast::helpers::fee::{FeeArgs, ScriptFeeSettings}; @@ -59,8 +60,6 @@ use tokio::runtime::Runtime; mod script_runtime; -type ScriptStarknetContractArtifacts = StarknetContractArtifacts; - #[derive(Args, Debug)] #[command(about = "Execute a deployment script")] pub struct Run { @@ -84,7 +83,7 @@ pub struct CastScriptExtension<'a> { pub account: Option<&'a SingleOwnerAccount<&'a JsonRpcClient, LocalWallet>>, pub tokio_runtime: Runtime, pub config: &'a CastConfig, - pub artifacts: &'a HashMap, + pub artifacts: &'a HashMap, pub state: StateManager, pub ui: &'a UI, } @@ -282,7 +281,7 @@ pub fn run( module_name: &str, metadata: &Metadata, package_metadata: &PackageMetadata, - artifacts: &mut HashMap, + artifacts: &mut HashMap, provider: &JsonRpcClient, tokio_runtime: Runtime, config: &CastConfig, @@ -446,8 +445,8 @@ fn warn_if_sncast_std_not_compatible(scarb_metadata: &Metadata, ui: &UI) -> Resu fn inject_lib_artifact( metadata: &Metadata, package_metadata: &PackageMetadata, - artifacts: &mut HashMap, -) -> Result> { + artifacts: &mut HashMap, +) -> Result> { let sierra_filename = format!("{}.sierra.json", package_metadata.name); let target_dir = &metadata @@ -457,10 +456,9 @@ fn inject_lib_artifact( // TODO(#2042) let sierra_path = &target_dir.join("dev").join(sierra_filename); - let lib_artifacts = ScriptStarknetContractArtifacts { + let lib_artifacts = CastStarknetContractArtifacts { sierra: fs::read_to_string(sierra_path)?, casm: String::new(), - executor: None, }; artifacts.insert(SCRIPT_LIB_ARTIFACT_NAME.to_string(), lib_artifacts); diff --git a/crates/sncast/src/starknet_commands/utils/class_hash.rs b/crates/sncast/src/starknet_commands/utils/class_hash.rs index ac961d7f8e..76efef613b 100644 --- a/crates/sncast/src/starknet_commands/utils/class_hash.rs +++ b/crates/sncast/src/starknet_commands/utils/class_hash.rs @@ -1,7 +1,7 @@ use anyhow::Context; use clap::Args; use conversions::{IntoConv, byte_array::ByteArray}; -use scarb_api::StarknetContractArtifacts; +use sncast::helpers::artifacts::CastStarknetContractArtifacts; use sncast::{ ErrorData, response::{class_hash::ClassHashResponse, errors::StarknetCommandError}, @@ -24,7 +24,7 @@ pub struct ClassHash { #[allow(clippy::result_large_err)] pub fn get_class_hash( class_hash: &ClassHash, - artifacts: &HashMap, + artifacts: &HashMap, ) -> Result { let contract_artifacts = artifacts.get(&class_hash.contract).ok_or( StarknetCommandError::ContractArtifactsNotFound(ErrorData { diff --git a/crates/sncast/src/starknet_commands/verify/mod.rs b/crates/sncast/src/starknet_commands/verify/mod.rs index 2d18d43aec..a971bba035 100644 --- a/crates/sncast/src/starknet_commands/verify/mod.rs +++ b/crates/sncast/src/starknet_commands/verify/mod.rs @@ -4,7 +4,6 @@ use clap::{ArgGroup, Args, ValueEnum}; use foundry_ui::UI; use foundry_ui::components::warning::WarningMessage; use promptly::prompt; -use scarb_api::StarknetContractArtifacts; use sncast::get_provider; use sncast::helpers::configuration::CastConfig; use sncast::helpers::rpc::FreeProvider; @@ -19,6 +18,7 @@ pub mod walnut; use explorer::ContractIdentifier; use explorer::VerificationInterface; +use sncast::helpers::artifacts::CastStarknetContractArtifacts; use voyager::Voyager; use walnut::WalnutVerificationInterface; @@ -87,7 +87,7 @@ fn display_files_and_confirm( files_to_display: Vec, confirm_verification: bool, ui: &UI, - artifacts: &HashMap, + artifacts: &HashMap, contract_name: &str, ) -> Result<()> { // Display files that will be uploaded @@ -119,7 +119,7 @@ fn display_files_and_confirm( pub async fn verify( args: Verify, manifest_path: &Utf8PathBuf, - artifacts: &HashMap, + artifacts: &HashMap, config: &CastConfig, ui: &UI, ) -> Result { From fcf37c93ed9b46c63fd1db203bb3d9704564077a Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 7 Oct 2025 11:53:20 +0200 Subject: [PATCH 35/45] Fix running native --- crates/forge/Cargo.toml | 2 +- crates/forge/test_utils/Cargo.toml | 2 +- crates/forge/test_utils/src/runner.rs | 23 ++++++----------------- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 8346dd6baf..2bcd81e868 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -14,7 +14,7 @@ assert_non_exact_gas = ["test_utils/assert_non_exact_gas"] skip_plugin_checks = [] interact-with-state = [] cairo-native = ["test_utils/cairo-native", "cheatnet/cairo-native", "scarb-api/cairo-native"] -run-native = ["cairo-native"] +run-native = ["cairo-native", "test_utils/run-native", "cheatnet/run-native", "scarb-api/run-native"] [dependencies] anyhow.workspace = true diff --git a/crates/forge/test_utils/Cargo.toml b/crates/forge/test_utils/Cargo.toml index dbabfd0cb0..c937400486 100644 --- a/crates/forge/test_utils/Cargo.toml +++ b/crates/forge/test_utils/Cargo.toml @@ -7,7 +7,7 @@ edition.workspace = true [features] assert_non_exact_gas = [] -cairo-native = [] +cairo-native = ["scarb-api/cairo-native", "cheatnet/cairo-native"] run-native = ["cairo-native"] [dependencies] diff --git a/crates/forge/test_utils/src/runner.rs b/crates/forge/test_utils/src/runner.rs index 728aabdaa4..a5a428efd9 100644 --- a/crates/forge/test_utils/src/runner.rs +++ b/crates/forge/test_utils/src/runner.rs @@ -62,7 +62,7 @@ impl Contract { }) } - fn generate_sierra_and_casm(self, ui: &UI) -> Result<(String, String)> { + fn generate_contract_artifacts(self, ui: &UI) -> Result { let dir = tempdir_with_tool_versions()?; let contract_path = dir.child("src/lib.cairo"); @@ -105,7 +105,7 @@ impl Contract { .unwrap(); let artifacts_dir = target_dir_for_workspace(&scarb_metadata).join("dev"); - let contract = get_contracts_artifacts_and_source_sierra_paths( + let artifacts = get_contracts_artifacts_and_source_sierra_paths( &artifacts_dir, package, ui, @@ -120,7 +120,7 @@ impl Contract { .ok_or(anyhow!("there is no contract with name {}", self.name))? .0; - Ok((contract.sierra, contract.casm)) + Ok(artifacts) } } @@ -225,20 +225,9 @@ impl<'a> TestCase { .into_iter() .map(|contract| { let name = contract.name.clone(); - let (sierra, casm) = contract.generate_sierra_and_casm(ui)?; - - Ok(( - name, - ( - StarknetContractArtifacts { - sierra, - casm, - #[cfg(feature = "cairo-native")] - executor: None, - }, - Utf8PathBuf::default(), - ), - )) + let artifacts = contract.generate_contract_artifacts(ui)?; + + Ok((name, (artifacts, Utf8PathBuf::default()))) }) .collect() } From 04db38f8bd5571d9142fd2f7576ac6dd29ae9bfb Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 7 Oct 2025 11:53:25 +0200 Subject: [PATCH 36/45] Remove dead code --- crates/forge/test_utils/src/running_tests.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/crates/forge/test_utils/src/running_tests.rs b/crates/forge/test_utils/src/running_tests.rs index c1609ab26c..5047677194 100644 --- a/crates/forge/test_utils/src/running_tests.rs +++ b/crates/forge/test_utils/src/running_tests.rs @@ -45,11 +45,8 @@ pub fn run_test_case( .unwrap(); let rt = Runtime::new().expect("Could not instantiate Runtime"); - let raw_test_targets = if false { - load_test_artifacts(&test.path().unwrap().join("target/release"), package).unwrap() - } else { - load_test_artifacts(&test.path().unwrap().join("target/dev"), package).unwrap() - }; + let raw_test_targets = + load_test_artifacts(&test.path().unwrap().join("target/dev"), package).unwrap(); let ui = Arc::new(UI::default()); rt.block_on(run_for_package( From cff995d92e55971c02c0d5f77a6d014d99614e31 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 7 Oct 2025 11:59:52 +0200 Subject: [PATCH 37/45] Simplify features --- .github/workflows/ci.yml | 2 +- crates/cheatnet/Cargo.toml | 1 - crates/cheatnet/tests/common/mod.rs | 2 +- crates/forge/Cargo.toml | 1 - crates/forge/test_utils/Cargo.toml | 1 - crates/forge/test_utils/src/runner.rs | 2 +- crates/forge/tests/e2e/backtrace.rs | 2 +- crates/forge/tests/e2e/clean.rs | 6 +++--- crates/forge/tests/e2e/common/mod.rs | 4 ++-- crates/forge/tests/e2e/common/runner.rs | 4 ++-- crates/forge/tests/e2e/docs_snippets_validation.rs | 2 +- crates/forge/tests/e2e/mod.rs | 12 ++++++------ crates/forge/tests/e2e/running.rs | 6 +++--- crates/scarb-api/Cargo.toml | 1 - crates/scarb-api/src/lib.rs | 2 +- 15 files changed, 22 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e54776bb5a..d0fa38b02c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,7 +55,7 @@ jobs: with: tool: nextest@0.9.98 - name: Build and archive tests - run: cargo nextest archive --cargo-profile ci -p forge --features skip_plugin_checks,run-native --archive-file 'nextest-archive-${{ runner.os }}-native.tar.zst' + run: cargo nextest archive --cargo-profile ci -p forge --features skip_plugin_checks,cairo-native --archive-file 'nextest-archive-${{ runner.os }}-native.tar.zst' - name: Upload archive to workflow uses: actions/upload-artifact@v4 with: diff --git a/crates/cheatnet/Cargo.toml b/crates/cheatnet/Cargo.toml index 81e52e548d..42ff069953 100644 --- a/crates/cheatnet/Cargo.toml +++ b/crates/cheatnet/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true [features] testing = [] cairo-native = ["dep:cairo-native", "blockifier/cairo_native"] -run-native = ["cairo-native"] [dependencies] anyhow.workspace = true diff --git a/crates/cheatnet/tests/common/mod.rs b/crates/cheatnet/tests/common/mod.rs index 77e29ee6b4..f466c5d66e 100644 --- a/crates/cheatnet/tests/common/mod.rs +++ b/crates/cheatnet/tests/common/mod.rs @@ -93,7 +93,7 @@ pub fn get_contracts() -> ContractsData { CompilationOpts { use_test_target_contracts: false, #[cfg(feature = "cairo-native")] - run_native: cfg!(feature = "run-native"), + run_native: true, }, ) .unwrap(); diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 2bcd81e868..ab2b25924e 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -14,7 +14,6 @@ assert_non_exact_gas = ["test_utils/assert_non_exact_gas"] skip_plugin_checks = [] interact-with-state = [] cairo-native = ["test_utils/cairo-native", "cheatnet/cairo-native", "scarb-api/cairo-native"] -run-native = ["cairo-native", "test_utils/run-native", "cheatnet/run-native", "scarb-api/run-native"] [dependencies] anyhow.workspace = true diff --git a/crates/forge/test_utils/Cargo.toml b/crates/forge/test_utils/Cargo.toml index c937400486..82000b168c 100644 --- a/crates/forge/test_utils/Cargo.toml +++ b/crates/forge/test_utils/Cargo.toml @@ -8,7 +8,6 @@ edition.workspace = true [features] assert_non_exact_gas = [] cairo-native = ["scarb-api/cairo-native", "cheatnet/cairo-native"] -run-native = ["cairo-native"] [dependencies] walkdir.workspace = true diff --git a/crates/forge/test_utils/src/runner.rs b/crates/forge/test_utils/src/runner.rs index a5a428efd9..a826ad8c1c 100644 --- a/crates/forge/test_utils/src/runner.rs +++ b/crates/forge/test_utils/src/runner.rs @@ -112,7 +112,7 @@ impl Contract { CompilationOpts { use_test_target_contracts: false, #[cfg(feature = "cairo-native")] - run_native: cfg!(feature = "run-native"), + run_native: true, }, ) .unwrap() diff --git a/crates/forge/tests/e2e/backtrace.rs b/crates/forge/tests/e2e/backtrace.rs index 0eb5ce85c6..9cf2797898 100644 --- a/crates/forge/tests/e2e/backtrace.rs +++ b/crates/forge/tests/e2e/backtrace.rs @@ -23,7 +23,7 @@ fn test_backtrace_missing_env() { ); } -#[cfg_attr(not(feature = "run-native"), ignore)] +#[cfg_attr(not(feature = "cairo-native"), ignore)] #[test] fn test_backtrace_native_execution() { let temp = setup_package("backtrace_vm_error"); diff --git a/crates/forge/tests/e2e/clean.rs b/crates/forge/tests/e2e/clean.rs index b489643a9c..02cde24625 100644 --- a/crates/forge/tests/e2e/clean.rs +++ b/crates/forge/tests/e2e/clean.rs @@ -21,7 +21,7 @@ struct CleanComponentsState { } #[test] -#[cfg_attr(feature = "run-native", ignore = "Native doesn't support coverage yet")] +#[cfg_attr(feature = "cairo-native", ignore = "Native doesn't support coverage yet")] fn test_clean_coverage() { let temp_dir = setup_package("coverage_project"); @@ -55,7 +55,7 @@ fn test_clean_coverage() { } #[test] -#[cfg_attr(feature = "run-native", ignore = "Native doesn't support profiler yet")] +#[cfg_attr(feature = "cairo-native", ignore = "Native doesn't support profiler yet")] fn test_clean_profile() { let temp_dir = setup_package("coverage_project"); @@ -121,7 +121,7 @@ fn test_clean_cache() { #[test] #[cfg_attr( - feature = "run-native", + feature = "cairo-native", ignore = "Native doesn't support trace, coverage and profiler yet" )] fn test_clean_all() { diff --git a/crates/forge/tests/e2e/common/mod.rs b/crates/forge/tests/e2e/common/mod.rs index 47b17b9107..875c4bb11b 100644 --- a/crates/forge/tests/e2e/common/mod.rs +++ b/crates/forge/tests/e2e/common/mod.rs @@ -1,11 +1,11 @@ -#[cfg(not(feature = "run-native"))] +#[cfg(not(feature = "cairo-native"))] use cairo_annotations::trace_data::{ CallTraceNode as ProfilerCallTraceNode, CallTraceV1 as ProfilerCallTrace, }; pub mod runner; -#[cfg(not(feature = "run-native"))] +#[cfg(not(feature = "cairo-native"))] pub fn get_trace_from_trace_node(trace_node: &ProfilerCallTraceNode) -> &ProfilerCallTrace { if let ProfilerCallTraceNode::EntryPointCall(trace) = trace_node { trace diff --git a/crates/forge/tests/e2e/common/runner.rs b/crates/forge/tests/e2e/common/runner.rs index 6bdec987ed..febf2ee51e 100644 --- a/crates/forge/tests/e2e/common/runner.rs +++ b/crates/forge/tests/e2e/common/runner.rs @@ -32,9 +32,9 @@ pub fn snforge_test_bin_path() -> PathBuf { } /// Returns a command that runs `snforge test` in the given temporary directory. -/// If the `run-native` feature is enabled, it adds the `--run-native` flag. +/// If the `cairo-native` feature is enabled, it adds the `--run-native` flag. pub(crate) fn test_runner>(temp_dir: T) -> SnapboxCommand { - if cfg!(feature = "run-native") { + if cfg!(feature = "cairo-native") { test_runner_native(temp_dir) } else { test_runner_vm(temp_dir) diff --git a/crates/forge/tests/e2e/docs_snippets_validation.rs b/crates/forge/tests/e2e/docs_snippets_validation.rs index b1dd4f62b9..381dcc2e95 100644 --- a/crates/forge/tests/e2e/docs_snippets_validation.rs +++ b/crates/forge/tests/e2e/docs_snippets_validation.rs @@ -12,7 +12,7 @@ use super::common::runner::{runner, setup_package}; #[test] #[cfg_attr( - feature = "run-native", + feature = "cairo-native", ignore = "TODO: Many snippets show vm resources witch cairo native doesn't support" )] fn test_docs_snippets() { diff --git a/crates/forge/tests/e2e/mod.rs b/crates/forge/tests/e2e/mod.rs index 56af0c6501..fb1b05e8cc 100644 --- a/crates/forge/tests/e2e/mod.rs +++ b/crates/forge/tests/e2e/mod.rs @@ -1,8 +1,8 @@ -#[cfg(not(feature = "run-native"))] +#[cfg(not(feature = "cairo-native"))] mod backtrace; -#[cfg(not(feature = "run-native"))] +#[cfg(not(feature = "cairo-native"))] mod build_profile; -#[cfg(not(feature = "run-native"))] +#[cfg(not(feature = "cairo-native"))] mod build_trace_data; mod clean; mod collection; @@ -11,9 +11,9 @@ pub(crate) mod common; mod completions; mod components; mod contract_artifacts; -#[cfg(not(feature = "run-native"))] +#[cfg(not(feature = "cairo-native"))] mod coverage; -#[cfg(all(feature = "debugging", not(feature = "run-native")))] +#[cfg(all(feature = "debugging", not(feature = "cairo-native")))] mod debugging; mod docs_snippets_validation; mod env; @@ -33,6 +33,6 @@ mod steps; mod templates; mod test_case; mod trace_print; -#[cfg(not(feature = "run-native"))] +#[cfg(not(feature = "cairo-native"))] mod trace_resources; mod workspaces; diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 18a8f1f838..c18f9209bc 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -53,7 +53,7 @@ fn simple_package() { ); } -#[cfg_attr(not(feature = "run-native"), ignore)] +#[cfg_attr(not(feature = "cairo-native"), ignore = "Requires cairo-native feature")] #[test] fn simple_package_native() { let temp = setup_package("simple_package"); @@ -1060,7 +1060,7 @@ fn incompatible_snforge_std_version_error() { #[test] #[cfg_attr( - feature = "run-native", + feature = "cairo-native", ignore = "Native runner does not support vm resources tracking" )] fn detailed_resources_flag() { @@ -1259,7 +1259,7 @@ fn exact_printing_mixed() { #[test] #[cfg_attr( - feature = "run-native", + feature = "cairo-native", ignore = "Native runner does not support panic backtrace yet" )] fn dispatchers() { diff --git a/crates/scarb-api/Cargo.toml b/crates/scarb-api/Cargo.toml index 249684c054..223046bd01 100644 --- a/crates/scarb-api/Cargo.toml +++ b/crates/scarb-api/Cargo.toml @@ -5,7 +5,6 @@ edition.workspace = true [features] cairo-native = ["dep:cairo-native", "dep:native-api"] -run-native = ["cairo-native"] [dependencies] anyhow.workspace = true diff --git a/crates/scarb-api/src/lib.rs b/crates/scarb-api/src/lib.rs index 7aff1dc26b..57bf25dbcd 100644 --- a/crates/scarb-api/src/lib.rs +++ b/crates/scarb-api/src/lib.rs @@ -597,7 +597,7 @@ mod tests { CompilationOpts { use_test_target_contracts: false, #[cfg(feature = "cairo-native")] - run_native: cfg!(feature = "run-native"), + run_native: true, }, ) .unwrap(); From dcdd26a45cccd51176fd4f1091f6f333d7311271 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 7 Oct 2025 12:17:37 +0200 Subject: [PATCH 38/45] Update docs --- docs/src/appendix/snforge/test.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/appendix/snforge/test.md b/docs/src/appendix/snforge/test.md index f891279dca..968a830a1a 100644 --- a/docs/src/appendix/snforge/test.md +++ b/docs/src/appendix/snforge/test.md @@ -34,6 +34,8 @@ Available components: ## `--run-native` +_This flag is only available if `snforge` was built with `cairo-native` flag_ + Run contracts on [`cairo-native`](https://github.com/lambdaclass/cairo_native) instead of the default `cairo-vm`. Note: Only contracts execution through native is supported, test code itself will still run on `cairo-vm`. From 041636004988905d319bece4cb5eed0346a5f660 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 7 Oct 2025 12:34:25 +0200 Subject: [PATCH 39/45] Fix test --- crates/forge/tests/integration/too_many_events.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/forge/tests/integration/too_many_events.rs b/crates/forge/tests/integration/too_many_events.rs index abe372e836..590a64d2ae 100644 --- a/crates/forge/tests/integration/too_many_events.rs +++ b/crates/forge/tests/integration/too_many_events.rs @@ -140,21 +140,21 @@ fn too_many_events() { &result, "emit_too_many_events", &format!( - "Got an exception while executing a hint: Exceeded the maximum number of events, number events: {emit_too_many_events}, max number events: {max_n_emitted_events}." + "Exceeded the maximum number of events, number events: {emit_too_many_events}, max number events: {max_n_emitted_events}." ), ); assert_case_output_contains( &result, "emit_too_many_data", &format!( - "Got an exception while executing a hint: Exceeded the maximum data length, data length: {emit_too_many_data}, max data length: {max_data_length}." + "Exceeded the maximum data length, data length: {emit_too_many_data}, max data length: {max_data_length}." ), ); assert_case_output_contains( &result, "emit_too_many_keys", &format!( - "Got an exception while executing a hint: Exceeded the maximum keys length, keys length: {emit_too_many_keys}, max keys length: {max_keys_length}." + "Exceeded the maximum keys length, keys length: {emit_too_many_keys}, max keys length: {max_keys_length}." ), ); } From 81c280798cfe89ddaf1ddb4ec051cc9bea28f22e Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 7 Oct 2025 12:38:16 +0200 Subject: [PATCH 40/45] Run integration tests on native on CI --- .github/workflows/ci.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d0fa38b02c..ad8acd31ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,6 +82,26 @@ jobs: - name: nextest partition ${{ matrix.partition }}/3 run: cargo nextest run --no-fail-fast --partition 'count:${{ matrix.partition }}/3' --archive-file 'nextest-archive-${{ runner.os }}.tar.zst' integration + test-forge-integration-native: + name: Test Forge / Integration Tests (native) + runs-on: [ ubuntu-latest ] + needs: [ build-test-forge-nextest-archive-native ] + strategy: + fail-fast: false + matrix: + partition: [ 1, 2, 3 ] + steps: + - uses: actions/checkout@v5 + - uses: ./.github/actions/setup-tools + - uses: taiki-e/install-action@v2 + with: + tool: nextest@0.9.98 + - uses: actions/download-artifact@v4 + with: + name: nextest-archive-${{ runner.os }}-native + - name: nextest partition ${{ matrix.partition }}/3 + run: cargo nextest run --no-fail-fast --partition 'count:${{ matrix.partition }}/3' --archive-file 'nextest-archive-${{ runner.os }}-native.tar.zst' integration + test-forge-e2e: name: Test Forge / E2E Tests runs-on: ubuntu-latest From 335823fd8080cffa7ed6b6ad3796de3cf1f729b6 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 7 Oct 2025 14:23:49 +0200 Subject: [PATCH 41/45] Format --- crates/forge/tests/e2e/clean.rs | 10 ++++++++-- crates/forge/tests/e2e/running.rs | 5 ++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/crates/forge/tests/e2e/clean.rs b/crates/forge/tests/e2e/clean.rs index 02cde24625..f446ddfdc7 100644 --- a/crates/forge/tests/e2e/clean.rs +++ b/crates/forge/tests/e2e/clean.rs @@ -21,7 +21,10 @@ struct CleanComponentsState { } #[test] -#[cfg_attr(feature = "cairo-native", ignore = "Native doesn't support coverage yet")] +#[cfg_attr( + feature = "cairo-native", + ignore = "Native doesn't support coverage yet" +)] fn test_clean_coverage() { let temp_dir = setup_package("coverage_project"); @@ -55,7 +58,10 @@ fn test_clean_coverage() { } #[test] -#[cfg_attr(feature = "cairo-native", ignore = "Native doesn't support profiler yet")] +#[cfg_attr( + feature = "cairo-native", + ignore = "Native doesn't support profiler yet" +)] fn test_clean_profile() { let temp_dir = setup_package("coverage_project"); diff --git a/crates/forge/tests/e2e/running.rs b/crates/forge/tests/e2e/running.rs index 00da064830..7e5fd0024b 100644 --- a/crates/forge/tests/e2e/running.rs +++ b/crates/forge/tests/e2e/running.rs @@ -53,7 +53,10 @@ fn simple_package() { ); } -#[cfg_attr(not(feature = "cairo-native"), ignore = "Requires cairo-native feature")] +#[cfg_attr( + not(feature = "cairo-native"), + ignore = "Requires cairo-native feature" +)] #[test] fn simple_package_native() { let temp = setup_package("simple_package"); From 0f326e496fa26477a1fb97ae76ea7d9be15dcc76 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 7 Oct 2025 15:14:29 +0200 Subject: [PATCH 42/45] Keep the native build as a separate workflow --- .github/workflows/_build-binaries-native.yml | 111 +++++++++++++++++++ .github/workflows/_build-binaries.yml | 68 +++++------- .github/workflows/_test-binaries.yml | 13 +-- 3 files changed, 143 insertions(+), 49 deletions(-) create mode 100644 .github/workflows/_build-binaries-native.yml diff --git a/.github/workflows/_build-binaries-native.yml b/.github/workflows/_build-binaries-native.yml new file mode 100644 index 0000000000..b77e7e4b4b --- /dev/null +++ b/.github/workflows/_build-binaries-native.yml @@ -0,0 +1,111 @@ +# TODO this is currently unused, integrate into nightly release flow +name: Build Native binaries + +on: + workflow_call: + inputs: + # Specify the version in MAJOR.MINOR.PATCH format, without a leading 'v' + version: + required: true + type: string + ref: + required: false + type: string + +jobs: + build-binaries: + name: Build ${{ matrix.target }} + runs-on: ${{ matrix.os }} + + env: + # Cross-compiled targets will override this to `cross`. + CARGO: cargo + + strategy: + matrix: + include: + - target: x86_64-unknown-linux-gnu + os: ubuntu-latest + system: linux + + - target: x86_64-unknown-linux-musl + os: ubuntu-latest + system: linux + + - target: aarch64-unknown-linux-gnu + os: ubuntu-latest + system: linux + + - target: aarch64-unknown-linux-musl + os: ubuntu-latest + system: linux + + - target: x86_64-apple-darwin + os: macos-14-large + system: macos + + - target: aarch64-apple-darwin + os: macos-latest + system: macos + + steps: + - name: Checkout with ref + if: inputs.ref != '' + uses: actions/checkout@v5 + with: + ref: ${{ inputs.ref }} + + - name: Checkout default + if: inputs.ref == '' + uses: actions/checkout@v5 + + - name: Setup rust + run: | + rustup target add ${{ matrix.target }} + + - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 + + - name: Setup LLVM on MacOS + if: matrix.system == 'macos' + run: | + brew install llvm@19 + brew reinstall zstd + echo "MLIR_SYS_190_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV + echo "LLVM_SYS_191_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV + echo "TABLEGEN_190_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV + echo "LIBRARY_PATH=/opt/homebrew/lib" >> $GITHUB_ENV + + - name: Install cross + if: matrix.system == 'linux' + run: | + cargo install cross --git https://github.com/cross-rs/cross + + - name: Build MacOS + if: matrix.system == 'macos' + run: | + export MLIR_SYS_190_PREFIX=${{ env.MLIR_SYS_190_PREFIX }} + export LLVM_SYS_191_PREFIX=${{ env.LLVM_SYS_191_PREFIX }} + export TABLEGEN_190_PREFIX=${{ env.TABLEGEN_190_PREFIX }} + export LIBRARY_PATH=${{ env.LIBRARY_PATH }} + cargo build --release --locked --target ${{ matrix.target }} + + - name: Build Linux + if: matrix.system == 'linux' + run: | + # Use cross to link oldest GLIBC possible. + cross build --release --locked --target ${{ matrix.target }} + + - name: Package + shell: bash + run: | + set -euxo pipefail + PKG_FULL_NAME="starknet-foundry-v${{ inputs.version }}-${{ matrix.target }}" + echo "PKG_FULL_NAME=$PKG_FULL_NAME" >> $GITHUB_ENV + + ./scripts/package.sh "${{ matrix.target }}" "$PKG_FULL_NAME" + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: build-${{ matrix.target }} + path: ${{ env.PKG_FULL_NAME }}.* diff --git a/.github/workflows/_build-binaries.yml b/.github/workflows/_build-binaries.yml index e5e13541e9..07cab1eca4 100644 --- a/.github/workflows/_build-binaries.yml +++ b/.github/workflows/_build-binaries.yml @@ -25,27 +25,26 @@ jobs: include: - target: x86_64-unknown-linux-gnu os: ubuntu-latest - system: linux -# -# - target: x86_64-unknown-linux-musl -# os: ubuntu-latest -# system: linux -# -# - target: aarch64-unknown-linux-gnu -# os: ubuntu-latest -# system: linux -# -# - target: aarch64-unknown-linux-musl -# os: ubuntu-latest -# system: linux + # Use cross to link oldest GLIBC possible. + cross: true + + - target: x86_64-unknown-linux-musl + os: ubuntu-latest + cross: true + + - target: aarch64-unknown-linux-gnu + os: ubuntu-latest + cross: true + + - target: aarch64-unknown-linux-musl + os: ubuntu-latest + cross: true - target: x86_64-apple-darwin - os: macos-14-large - system: macos + os: macos-latest - target: aarch64-apple-darwin os: macos-latest - system: macos steps: - name: Checkout with ref @@ -63,36 +62,21 @@ jobs: rustup target add ${{ matrix.target }} - uses: Swatinem/rust-cache@9d47c6ad4b02e050fd481d890b2ea34778fd09d6 - - - name: Setup LLVM on MacOS - if: matrix.system == 'macos' - run: | - brew install llvm@19 - brew reinstall zstd - echo "MLIR_SYS_190_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV - echo "LLVM_SYS_191_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV - echo "TABLEGEN_190_PREFIX=$(brew --prefix llvm@19)" >> $GITHUB_ENV - echo "LIBRARY_PATH=/opt/homebrew/lib" >> $GITHUB_ENV + with: + workspaces: starknet-foundry - name: Install cross - if: matrix.system == 'linux' - run: | - cargo install cross --git https://github.com/cross-rs/cross + if: matrix.cross + uses: taiki-e/install-action@cross - - name: Build MacOS - if: matrix.system == 'macos' - run: | - export MLIR_SYS_190_PREFIX=${{ env.MLIR_SYS_190_PREFIX }} - export LLVM_SYS_191_PREFIX=${{ env.LLVM_SYS_191_PREFIX }} - export TABLEGEN_190_PREFIX=${{ env.TABLEGEN_190_PREFIX }} - export LIBRARY_PATH=${{ env.LIBRARY_PATH }} - cargo build --release --locked --target ${{ matrix.target }} - - - name: Build Linux - if: matrix.system == 'linux' + - name: Enable cross-compilation + if: matrix.cross + shell: bash run: | - # Use cross to link oldest GLIBC possible. - cross build --release --locked --target ${{ matrix.target }} + echo "CARGO=cross" >> $GITHUB_ENV + + - name: Build + run: ${{ env.CARGO }} build --release --locked --target ${{ matrix.target }} - name: Package shell: bash diff --git a/.github/workflows/_test-binaries.yml b/.github/workflows/_test-binaries.yml index e2a9f4d64f..afa1edd0a9 100644 --- a/.github/workflows/_test-binaries.yml +++ b/.github/workflows/_test-binaries.yml @@ -24,13 +24,12 @@ jobs: - target: x86_64-unknown-linux-gnu os: ubuntu-latest -# TODO restore smoke tests on macos -# - target: aarch64-apple-darwin -# os: macos-latest -# -# - target: x86_64-apple-darwin -# # Target macos-latest uses Mac with ARM, macos-14-large is still on Intel -# os: macos-14-large + - target: aarch64-apple-darwin + os: macos-latest + + - target: x86_64-apple-darwin + # Target macos-latest uses Mac with ARM, macos-14-large is still on Intel + os: macos-14-large steps: - uses: actions/checkout@v5 From 18f492c5f3e325b224f3b7706a9c79c9b33869f2 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 7 Oct 2025 15:16:32 +0200 Subject: [PATCH 43/45] Newline --- crates/universal-sierra-compiler-api/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/universal-sierra-compiler-api/Cargo.toml b/crates/universal-sierra-compiler-api/Cargo.toml index 25da1de816..dd6ea2715a 100644 --- a/crates/universal-sierra-compiler-api/Cargo.toml +++ b/crates/universal-sierra-compiler-api/Cargo.toml @@ -13,4 +13,4 @@ tempfile.workspace = true num-bigint.workspace = true cairo-lang-casm.workspace = true camino.workspace = true -tracing.workspace = true \ No newline at end of file +tracing.workspace = true From d398b082edd7606963f4ed4670151b47809c0851 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 7 Oct 2025 16:14:34 +0200 Subject: [PATCH 44/45] Link todos --- .../cheatnet/src/runtime_extensions/native/deploy.rs | 2 +- .../src/runtime_extensions/native/execution.rs | 2 +- .../native/native_syscall_handler.rs | 10 +++++----- crates/forge/tests/e2e/docs_snippets_validation.rs | 2 +- crates/sncast/src/helpers/scarb_utils.rs | 2 -- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/crates/cheatnet/src/runtime_extensions/native/deploy.rs b/crates/cheatnet/src/runtime_extensions/native/deploy.rs index 615a693fd5..4ca4b116b4 100644 --- a/crates/cheatnet/src/runtime_extensions/native/deploy.rs +++ b/crates/cheatnet/src/runtime_extensions/native/deploy.rs @@ -145,7 +145,7 @@ pub fn deploy( // .tx_context // .block_context // .versioned_constants; - // TODO support for reject + // TODO(#3790) support for reject // if should_reject_deploy( // versioned_constants.disable_deploy_in_validation_mode, // syscall_handler_base.context.execution_mode, diff --git a/crates/cheatnet/src/runtime_extensions/native/execution.rs b/crates/cheatnet/src/runtime_extensions/native/execution.rs index d587758f62..fe2935d4c6 100644 --- a/crates/cheatnet/src/runtime_extensions/native/execution.rs +++ b/crates/cheatnet/src/runtime_extensions/native/execution.rs @@ -115,7 +115,7 @@ pub fn execute_entry_point_call( let call_result = execution_result.map_err(EntryPointExecutionError::NativeUnexpectedError)?; - // TODO consider modifying this so it doesn't use take internally + // TODO(#3790) consider modifying this so it doesn't use take internally if let Some(error) = syscall_handler.unrecoverable_error() { return Err(EntryPointExecutionError::NativeUnrecoverableError( Box::new(error), diff --git a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs index 399e817a7c..deb43a311d 100644 --- a/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs +++ b/crates/cheatnet/src/runtime_extensions/native/native_syscall_handler.rs @@ -33,7 +33,7 @@ pub struct CheatableNativeSyscallHandler<'a> { pub type BaseSyscallResult = Result; impl CheatableNativeSyscallHandler<'_> { - // TODO consider modifying this so it doesn't use take + // TODO(#3790) consider modifying this so it doesn't use take pub fn unrecoverable_error(&mut self) -> Option { self.native_syscall_handler.unrecoverable_error.take() } @@ -263,7 +263,7 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { .tx_info .nonce .unwrap_or(original_data.tx_info.nonce); - // TODO impl conversions + // TODO(#3790) impl conversions let resource_bounds = cheated_data.tx_info.resource_bounds.map_or( original_data.tx_info.resource_bounds, |rb| { @@ -469,7 +469,7 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { return Err(self.handle_error(remaining_gas, err.into())); } let selector = EntryPointSelector(entry_point_selector); - // TODO restore blocking + // TODO(#3790) restore blocking // self // .native_syscall_handler // .base @@ -544,7 +544,7 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { .native_syscall_handler .base .call - // TODO why we default to code_address?? + // TODO(#3790) why we default to code_address?? .code_address .unwrap_or(self.native_syscall_handler.base.call.storage_address); let event = self @@ -576,7 +576,7 @@ impl StarknetSyscallHandler for &mut CheatableNativeSyscallHandler<'_> { .native_syscall_handler .base .call - // TODO why we default to code_address?? + // TODO(#3790) why we default to code_address?? .code_address .unwrap_or(self.native_syscall_handler.base.call.storage_address); let message = self diff --git a/crates/forge/tests/e2e/docs_snippets_validation.rs b/crates/forge/tests/e2e/docs_snippets_validation.rs index 381dcc2e95..1d5f19a3f2 100644 --- a/crates/forge/tests/e2e/docs_snippets_validation.rs +++ b/crates/forge/tests/e2e/docs_snippets_validation.rs @@ -13,7 +13,7 @@ use super::common::runner::{runner, setup_package}; #[test] #[cfg_attr( feature = "cairo-native", - ignore = "TODO: Many snippets show vm resources witch cairo native doesn't support" + ignore = "TODO(#3790): Many snippets show vm resources witch cairo native doesn't support" )] fn test_docs_snippets() { let root_dir = get_nth_ancestor(2); diff --git a/crates/sncast/src/helpers/scarb_utils.rs b/crates/sncast/src/helpers/scarb_utils.rs index 0f8ab6a6b1..d049da1136 100644 --- a/crates/sncast/src/helpers/scarb_utils.rs +++ b/crates/sncast/src/helpers/scarb_utils.rs @@ -172,7 +172,6 @@ pub fn build_and_load_artifacts( &target_dir.join(&config.profile), package, ui, - // TODO: Reconsider running sncast with native CompilationOpts::default() ).context("Failed to load artifacts. Make sure you have enabled sierra code generation in Scarb.toml")? .into_iter() @@ -187,7 +186,6 @@ pub fn build_and_load_artifacts( &target_dir.join(default_profile), package, ui, - // TODO: Reconsider running sncast with native CompilationOpts::default(), ).context("Failed to load artifacts. Make sure you have enabled sierra code generation in Scarb.toml")? .into_iter() From ca318c37ebfa7045ba38c30c503d065ea7fce5a0 Mon Sep 17 00:00:00 2001 From: Artur Michalek Date: Tue, 7 Oct 2025 16:18:25 +0200 Subject: [PATCH 45/45] Link todo to _build-binaries-native.yml --- .github/workflows/_build-binaries-native.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/_build-binaries-native.yml b/.github/workflows/_build-binaries-native.yml index b77e7e4b4b..101d2aa1b3 100644 --- a/.github/workflows/_build-binaries-native.yml +++ b/.github/workflows/_build-binaries-native.yml @@ -1,4 +1,4 @@ -# TODO this is currently unused, integrate into nightly release flow +# TODO(#3790) this is currently unused, integrate into nightly release flow name: Build Native binaries on: