diff --git a/.gitmodules b/.gitmodules index c3d9a7e510..8063c86784 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "vendor/cairo"] path = vendor/cairo url = https://github.com/starkware-libs/cairo.git - branch = v2.19.0-rc.3 + branch = ron/deploy-v2/corelib-rc1 diff --git a/Cargo.lock b/Cargo.lock index c8aa13ee71..c885e57594 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -573,8 +573,7 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cairo-lang-casm" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c13d355783b9ff4b54a995a49a639707e32502cac2d886b7ec62b367694c05" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-utils", "indoc", @@ -587,8 +586,7 @@ dependencies = [ [[package]] name = "cairo-lang-compiler" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4332bf28b1ccc82f064ed6ed4f287828391d4c5629f8f49ef6ddc3915b56e381" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "anyhow", "cairo-lang-defs", @@ -614,8 +612,7 @@ dependencies = [ [[package]] name = "cairo-lang-debug" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e58a589f842fd8ae1191530c04b5df490f94fb803bb5ccd96adbfcaba9b3ab1f" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-utils", "id-arena", @@ -625,8 +622,7 @@ dependencies = [ [[package]] name = "cairo-lang-defs" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97ef9dc4dfb938a2585100e452e2b6184662bb9bba7042226411fa6bc07cb91" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-debug", "cairo-lang-diagnostics", @@ -646,8 +642,7 @@ dependencies = [ [[package]] name = "cairo-lang-diagnostics" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0045ae336e51dc4112e1c678591262184ca22e6bda4c759619a6a817a60d967d" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", @@ -660,8 +655,7 @@ dependencies = [ [[package]] name = "cairo-lang-eq-solver" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f93abccc09808fc4f2b611d8c20a390fa03984eddc907f3ad8ce5b58d8a721" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-utils", "good_lp", @@ -670,8 +664,7 @@ dependencies = [ [[package]] name = "cairo-lang-filesystem" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d1e89da58481852b273a455986a035abd7127452f8275fe2bc022783b7f89" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-debug", "cairo-lang-proc-macros", @@ -688,8 +681,7 @@ dependencies = [ [[package]] name = "cairo-lang-formatter" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a80dc0ba2632faf759a25ecda755aaf0cb4324fce392b1feb652e259135081c6" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "anyhow", "cairo-lang-diagnostics", @@ -708,8 +700,7 @@ dependencies = [ [[package]] name = "cairo-lang-lowering" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "422cef0633dcd65a7b7d48a6e8a582830da289441df5bb566d38a7b74e1c51fa" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "assert_matches", "cairo-lang-debug", @@ -738,8 +729,7 @@ dependencies = [ [[package]] name = "cairo-lang-parser" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e89b7b250bf93661f3c57b09428b0feb7e47f69e04636f860b24938e5a9a7c" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-diagnostics", "cairo-lang-filesystem", @@ -758,8 +748,7 @@ dependencies = [ [[package]] name = "cairo-lang-plugins" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4354cb3fe34bf0e1eebcbe3fff5953dcc5389f991caf7cd959086250b54ab55f" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-defs", "cairo-lang-diagnostics", @@ -782,8 +771,7 @@ checksum = "123ac0ecadf31bacae77436d72b88fa9caef2b8e92c89ce63a125ae911a12fae" [[package]] name = "cairo-lang-proc-macros" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c692bbc37b074f9e1c3f7fdcb5bddfa0c31768e0c052fbad882adf2c039aa1" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-debug", "proc-macro2", @@ -795,8 +783,7 @@ dependencies = [ [[package]] name = "cairo-lang-project" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006338f1186ecd5da3b52b4bb86fd4c69f9f2a0f32b5d1055efa0c28c160a004" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-filesystem", "cairo-lang-utils", @@ -808,8 +795,7 @@ dependencies = [ [[package]] name = "cairo-lang-runnable-utils" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5003a11d12e7b299960ab94d7f1da03bfb8f85c2d4858ee5f2d88f0d007676f6" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-casm", "cairo-lang-sierra", @@ -825,8 +811,7 @@ dependencies = [ [[package]] name = "cairo-lang-runner" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf6a5b109faa35f59a1ef1c2fc04cd0c9086778f41857cec8e0bcd7dc513517f" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "ark-ff 0.6.0", "ark-secp256k1 0.6.0", @@ -857,8 +842,7 @@ dependencies = [ [[package]] name = "cairo-lang-semantic" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a2fb4eed7bfc001f7fe028927bb4abeafa513764472e0f8b80cfd6ec19e07d" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -887,8 +871,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c07bb85ab735c71630540736044bc5f771b1a336dd0636a1f4708186746f40e" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "anyhow", "cairo-lang-utils", @@ -913,8 +896,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-ap-change" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c291f44590a7c8e7e00b5b8adfa5ff7d4afab9e9b8cbd427c5757035e19b45" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -929,8 +911,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-gas" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c967cbee6e5e5a5c69fba183bb85e8906dba79cf12673c79b535e89b99fbd683" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-eq-solver", "cairo-lang-sierra", @@ -945,8 +926,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-generator" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8081833a3e5b483b7faff351b9606fb2d486be55c8fff467efaf383c193b0890" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-debug", "cairo-lang-defs", @@ -970,8 +950,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-to-casm" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b09e28e8a8a702516a898e48a933cf3136b92019c4bd3ccae00707e883b9367" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "assert_matches", "cairo-lang-casm", @@ -991,8 +970,7 @@ dependencies = [ [[package]] name = "cairo-lang-sierra-type-size" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c2b692133e92cf42ffc493fd8f6957a3a35b75b908fc53f287edb35f06aac9" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-sierra", "cairo-lang-utils", @@ -1001,8 +979,7 @@ dependencies = [ [[package]] name = "cairo-lang-starknet" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c27166eada9c2c812a2ff4db8f20ca4d6fead8a63390c02b93ea7f27da205120" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -1034,8 +1011,7 @@ dependencies = [ [[package]] name = "cairo-lang-starknet-classes" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10faa91e4be6065bd08f2da8d923721e25615ca94f50fd870faf5e52691b55b3" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-casm", "cairo-lang-sierra", @@ -1058,8 +1034,7 @@ dependencies = [ [[package]] name = "cairo-lang-syntax" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0a90a23f458132951b20c267e2410d4eaf307e214bbcb5dabfd7ae0739b903" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-debug", "cairo-lang-filesystem", @@ -1077,8 +1052,7 @@ dependencies = [ [[package]] name = "cairo-lang-syntax-codegen" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d63421b7a7204b8c57f15d488a09520959cd6e6fef48dbbd3a37071577873d15" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "genco", "xshell", @@ -1087,8 +1061,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-plugin" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaceca861261038c5162af5da78152dea4cc59a1197c32c742bee98e8d41b574" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -1116,8 +1089,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-runner" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0adb95e7b34619cbea9268b7014b8b36af1f8811c5bf1b2372a11c0ca07089" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "anyhow", "cairo-lang-compiler", @@ -1140,8 +1112,7 @@ dependencies = [ [[package]] name = "cairo-lang-test-utils" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6e37ea508d269cfa17331db6395f445ca3a5c9cbf8565990edcb2a17d9d466" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "cairo-lang-formatter", "cairo-lang-proc-macros", @@ -1154,8 +1125,7 @@ dependencies = [ [[package]] name = "cairo-lang-utils" version = "2.19.0-rc.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a994da2cbd8f7be81e9951d20b1f98e220e03c61e18e6cbde0bb4cc911b90c8d" +source = "git+https://github.com/starkware-libs/cairo?branch=ron%2Fdeploy-v2%2Fcompiler-rc3#304746ed85b48ba73daf8fd7554bcf168c631ef0" dependencies = [ "hashbrown 0.17.1", "indexmap", diff --git a/Cargo.toml b/Cargo.toml index c27793006f..7ae485c566 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -252,3 +252,30 @@ required-features = [ "testing" ] [[example]] name = "easy_api" required-features = [ "testing" ] + +# --- TEMPORARY dev override (deploy_v2) --- +# The `deploy_v2` syscall needs the `DeployV2` Sierra libfunc variant, which is not yet in a +# published cairo-lang-* release. Until one is, build against `ron/deploy-v2/compiler-rc3`: the +# compiler PR (https://github.com/starkware-libs/cairo/pull/10214) applied to this repo's pinned +# 2.19.0-rc.3, so the override adds the libfunc and nothing else. `vendor/cairo` is pinned the same +# way, to `ron/deploy-v2/corelib-rc1` (the submodule's own commit plus `deploy_v2_syscall`). Drop +# both once a release carries the variant. +[patch.crates-io] +cairo-lang-casm = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-compiler = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-defs = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-filesystem = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-lowering = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-runner = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-semantic = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-sierra = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-sierra-ap-change = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-sierra-gas = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-sierra-generator = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-sierra-to-casm = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-sierra-type-size = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-starknet = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-starknet-classes = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-test-plugin = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-test-runner = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } +cairo-lang-utils = { git = "https://github.com/starkware-libs/cairo", branch = "ron/deploy-v2/compiler-rc3" } diff --git a/cairo-starknet-syscalls/src/lib.rs b/cairo-starknet-syscalls/src/lib.rs index 04b1694c15..d577b1123f 100644 --- a/cairo-starknet-syscalls/src/lib.rs +++ b/cairo-starknet-syscalls/src/lib.rs @@ -235,6 +235,17 @@ pub trait StarknetSyscallHandler { remaining_gas: &mut u64, ) -> SyscallResult<(Felt, Vec)>; + /// Deploys a contract, deriving its address with the Blake-escaped derivation instead of + /// Pedersen (the `deploy_v2` syscall). Same request/response as [`Self::deploy`]. + fn deploy_v2( + &mut self, + class_hash: Felt, + contract_address_salt: Felt, + calldata: &[Felt], + deploy_from_zero: bool, + remaining_gas: &mut u64, + ) -> SyscallResult<(Felt, Vec)>; + fn replace_class(&mut self, class_hash: Felt, remaining_gas: &mut u64) -> SyscallResult<()>; fn library_call( diff --git a/debug_utils/sierra-emu/programs/syscalls.cairo b/debug_utils/sierra-emu/programs/syscalls.cairo index e8e23cbcfe..a8b9e647de 100644 --- a/debug_utils/sierra-emu/programs/syscalls.cairo +++ b/debug_utils/sierra-emu/programs/syscalls.cairo @@ -1,7 +1,7 @@ use starknet::ClassHash; use starknet::{ call_contract_syscall, ContractAddress, - deploy_syscall, emit_event_syscall, ExecutionInfo, get_block_hash_syscall, + deploy_syscall, deploy_v2_syscall, emit_event_syscall, ExecutionInfo, get_block_hash_syscall, keccak_syscall, library_call_syscall, replace_class_syscall, send_message_to_l1_syscall, storage_address_try_from_felt252, storage_read_syscall, storage_write_syscall, SyscallResult, @@ -29,6 +29,10 @@ fn deploy() -> SyscallResult<(ContractAddress, Span)> { deploy_syscall(ZERO_CLASS_HASH, 0, array![].span(), false) } +fn deploy_v2() -> SyscallResult<(ContractAddress, Span)> { + deploy_v2_syscall(ZERO_CLASS_HASH, 0, array![].span(), false) +} + fn replace_class() -> SyscallResult<()> { replace_class_syscall(ZERO_CLASS_HASH) } diff --git a/debug_utils/sierra-emu/src/debug.rs b/debug_utils/sierra-emu/src/debug.rs index 1c2e7efcfd..da921763fc 100644 --- a/debug_utils/sierra-emu/src/debug.rs +++ b/debug_utils/sierra-emu/src/debug.rs @@ -354,6 +354,7 @@ pub fn libfunc_to_name(value: &CoreConcreteLibfunc) -> &'static str { StarknetConcreteLibfunc::GetExecutionInfo(_) => "get_exec_info_v1", StarknetConcreteLibfunc::GetExecutionInfoV2(_) => "get_exec_info_v2", StarknetConcreteLibfunc::Deploy(_) => "deploy", + StarknetConcreteLibfunc::DeployV2(_) => "deploy_v2", StarknetConcreteLibfunc::Keccak(_) => "keccak", StarknetConcreteLibfunc::LibraryCall(_) => "library_call", StarknetConcreteLibfunc::ReplaceClass(_) => "replace_class", diff --git a/debug_utils/sierra-emu/src/starknet.rs b/debug_utils/sierra-emu/src/starknet.rs index be6f41da49..15db21e02a 100644 --- a/debug_utils/sierra-emu/src/starknet.rs +++ b/debug_utils/sierra-emu/src/starknet.rs @@ -159,6 +159,18 @@ impl StarknetSyscallHandler for StubSyscallHandler { unimplemented!() } + fn deploy_v2( + &mut self, + _class_hash: Felt, + _contract_address_salt: Felt, + _calldata: &[Felt], + _deploy_from_zero: bool, + _remaining_gas: &mut u64, + ) -> SyscallResult<(Felt, Vec)> { + // Deployment requires constructor execution, which the stub doesn't model. + unimplemented!() + } + fn replace_class(&mut self, _class_hash: Felt, _remaining_gas: &mut u64) -> SyscallResult<()> { // Class replacement updates a registry the stub doesn't track. unimplemented!() diff --git a/debug_utils/sierra-emu/src/vm/starknet.rs b/debug_utils/sierra-emu/src/vm/starknet.rs index 8c9a883490..60a3b76cfc 100644 --- a/debug_utils/sierra-emu/src/vm/starknet.rs +++ b/debug_utils/sierra-emu/src/vm/starknet.rs @@ -94,6 +94,9 @@ pub fn eval( eval_get_execution_info_v2(registry, info, args, syscall_handler) } StarknetConcreteLibfunc::Deploy(info) => eval_deploy(registry, info, args, syscall_handler), + StarknetConcreteLibfunc::DeployV2(info) => { + eval_deploy_v2(registry, info, args, syscall_handler) + } StarknetConcreteLibfunc::Keccak(info) => eval_keccak(registry, info, args, syscall_handler), StarknetConcreteLibfunc::Sha256ProcessBlock(info) => { eval_sha256_process_block(registry, info, args, syscall_handler) @@ -1094,6 +1097,90 @@ fn eval_deploy( } } +/// Same request/response layout as [`eval_deploy`], but dispatches to the handler's `deploy_v2` +/// method (Blake-escaped address derivation). +fn eval_deploy_v2( + registry: &ProgramRegistry, + info: &SignatureOnlyConcreteLibfunc, + args: Vec, + syscall_handler: &mut impl StarknetSyscallHandler, +) -> EvalAction { + let [Value::U64(mut gas), system, Value::Felt(class_hash), Value::Felt(contract_address_salt), Value::Struct(calldata), Value::Enum { + self_ty: _, + index: deploy_from_zero, + payload: _, + }]: [Value; 6] = args.try_into().unwrap() + else { + panic!() + }; + + let deploy_from_zero = deploy_from_zero != 0; + + let [Value::Array { + ty: _, + data: calldata, + }]: [Value; 1] = calldata.try_into().unwrap() + else { + panic!() + }; + + let calldata = calldata + .into_iter() + .map(|x| match x { + Value::Felt(x) => x, + _ => unreachable!(), + }) + .collect::>(); + + // get felt type from the error branch array + let felt_ty = { + match registry + .get_type(&info.branch_signatures()[1].vars[2].ty) + .unwrap() + { + CoreTypeConcrete::Array(info) => info.ty.clone(), + _ => unreachable!(), + } + }; + + let result = syscall_handler.deploy_v2( + class_hash, + contract_address_salt, + &calldata, + deploy_from_zero, + &mut gas, + ); + + match result { + Ok((contract_address, return_values)) => EvalAction::NormalBranch( + 0, + smallvec![ + Value::U64(gas), + system, + Value::Felt(contract_address), + Value::Struct(vec![Value::Array { + ty: felt_ty, + data: return_values + .into_iter() + .map(Value::Felt) + .collect::>(), + }]) + ], + ), + Err(e) => EvalAction::NormalBranch( + 1, + smallvec![ + Value::U64(gas), + system, + Value::Array { + ty: felt_ty, + data: e.into_iter().map(Value::Felt).collect::>(), + } + ], + ), + } +} + fn eval_keccak( registry: &ProgramRegistry, info: &SignatureOnlyConcreteLibfunc, diff --git a/examples/erc20.rs b/examples/erc20.rs index b23c4fbfd1..550a0b5a1f 100644 --- a/examples/erc20.rs +++ b/examples/erc20.rs @@ -108,6 +108,21 @@ impl StarknetSyscallHandler for SyscallHandler { )) } + fn deploy_v2( + &mut self, + class_hash: Felt, + contract_address_salt: Felt, + calldata: &[Felt], + deploy_from_zero: bool, + _gas: &mut u64, + ) -> SyscallResult<(Felt, Vec)> { + println!("Called `deploy_v2({class_hash}, {contract_address_salt}, {calldata:?}, {deploy_from_zero})` from MLIR."); + Ok(( + class_hash + contract_address_salt, + calldata.iter().map(|x| x + Felt::ONE).collect(), + )) + } + fn replace_class(&mut self, class_hash: Felt, _gas: &mut u64) -> SyscallResult<()> { println!("Called `replace_class({class_hash})` from MLIR."); Ok(()) diff --git a/examples/starknet.rs b/examples/starknet.rs index 4d06dc6900..64ef0901b0 100644 --- a/examples/starknet.rs +++ b/examples/starknet.rs @@ -150,6 +150,21 @@ impl StarknetSyscallHandler for SyscallHandler { )) } + fn deploy_v2( + &mut self, + class_hash: Felt, + contract_address_salt: Felt, + calldata: &[Felt], + deploy_from_zero: bool, + _gas: &mut u64, + ) -> SyscallResult<(Felt, Vec)> { + println!("Called `deploy_v2({class_hash}, {contract_address_salt}, {calldata:?}, {deploy_from_zero})` from MLIR."); + Ok(( + class_hash + contract_address_salt, + calldata.iter().map(|x| x + Felt::ONE).collect(), + )) + } + fn replace_class(&mut self, class_hash: Felt, _gas: &mut u64) -> SyscallResult<()> { println!("Called `replace_class({class_hash})` from MLIR."); Ok(()) diff --git a/src/debug.rs b/src/debug.rs index f68fcde3be..ec2a1eb4f8 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -354,6 +354,7 @@ pub const fn libfunc_to_name(value: &CoreConcreteLibfunc) -> &'static str { StarknetConcreteLibfunc::GetExecutionInfoV2(_) => "get_exec_info_v2", StarknetConcreteLibfunc::GetExecutionInfoV3(_) => "get_exec_info_v3", StarknetConcreteLibfunc::Deploy(_) => "deploy", + StarknetConcreteLibfunc::DeployV2(_) => "deploy_v2", StarknetConcreteLibfunc::Keccak(_) => "keccak", StarknetConcreteLibfunc::LibraryCall(_) => "library_call", StarknetConcreteLibfunc::ReplaceClass(_) => "replace_class", diff --git a/src/libfuncs/starknet.rs b/src/libfuncs/starknet.rs index d3b9c477f9..1daa8356bd 100644 --- a/src/libfuncs/starknet.rs +++ b/src/libfuncs/starknet.rs @@ -127,6 +127,9 @@ pub fn build<'ctx, 'this>( StarknetConcreteLibfunc::Deploy(info) => { build_deploy(context, registry, entry, location, helper, metadata, info) } + StarknetConcreteLibfunc::DeployV2(info) => { + build_deploy_v2(context, registry, entry, location, helper, metadata, info) + } StarknetConcreteLibfunc::Keccak(info) => { build_keccak(context, registry, entry, location, helper, metadata, info) } @@ -1177,6 +1180,54 @@ pub fn build_deploy<'ctx, 'this>( helper: &LibfuncHelper<'ctx, 'this>, metadata: &mut MetadataStorage, info: &SignatureOnlyConcreteLibfunc, +) -> Result<()> { + build_deploy_impl( + context, + registry, + entry, + location, + helper, + metadata, + info, + StarknetSyscallHandlerCallbacks::<()>::DEPLOY, + ) +} + +/// Same request/response layout as [`build_deploy`], but dispatches to the handler's `deploy_v2` +/// method (Blake-escaped address derivation) via its vtable offset. +pub fn build_deploy_v2<'ctx, 'this>( + context: &'ctx Context, + registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + metadata: &mut MetadataStorage, + info: &SignatureOnlyConcreteLibfunc, +) -> Result<()> { + build_deploy_impl( + context, + registry, + entry, + location, + helper, + metadata, + info, + StarknetSyscallHandlerCallbacks::<()>::DEPLOY_V2, + ) +} + +/// Shared body of [`build_deploy`] and [`build_deploy_v2`]; `callback_offset` selects which handler +/// vtable entry (`DEPLOY` or `DEPLOY_V2`) the syscall dispatches to. +#[allow(clippy::too_many_arguments)] +fn build_deploy_impl<'ctx, 'this>( + context: &'ctx Context, + registry: &ProgramRegistry, + entry: &'this Block<'ctx>, + location: Location<'ctx>, + helper: &LibfuncHelper<'ctx, 'this>, + metadata: &mut MetadataStorage, + info: &SignatureOnlyConcreteLibfunc, + callback_offset: usize, ) -> Result<()> { // Extract self pointer. let ptr = entry.load( @@ -1314,9 +1365,7 @@ pub fn build_deploy<'ctx, 'this>( context, location, entry.arg(1)?, - &[GepIndex::Const( - StarknetSyscallHandlerCallbacks::<()>::DEPLOY.try_into()?, - )], + &[GepIndex::Const(callback_offset.try_into()?)], pointer(context, 0), )?; let fn_ptr = entry.load(context, location, fn_ptr, llvm::r#type::pointer(context, 0))?; diff --git a/src/starknet.rs b/src/starknet.rs index 0a6051195f..93a83055aa 100644 --- a/src/starknet.rs +++ b/src/starknet.rs @@ -94,6 +94,17 @@ impl StarknetSyscallHandler for DummySyscallHandler { unimplemented!() } + fn deploy_v2( + &mut self, + _class_hash: Felt, + _contract_address_salt: Felt, + _calldata: &[Felt], + _deploy_from_zero: bool, + _remaining_gas: &mut u64, + ) -> SyscallResult<(Felt, Vec)> { + unimplemented!() + } + fn replace_class(&mut self, _class_hash: Felt, _remaining_gas: &mut u64) -> SyscallResult<()> { unimplemented!() } @@ -456,6 +467,15 @@ pub(crate) mod handler { calldata: &ArrayAbi, deploy_from_zero: bool, ), + deploy_v2: extern "C" fn( + result_ptr: &mut SyscallResultAbi<(Felt252Abi, ArrayAbi)>, + ptr: &mut T, + gas: &mut u64, + class_hash: &Felt252Abi, + contract_address_salt: &Felt252Abi, + calldata: &ArrayAbi, + deploy_from_zero: bool, + ), replace_class: extern "C" fn( result_ptr: &mut SyscallResultAbi<()>, ptr: &mut T, @@ -631,6 +651,7 @@ pub(crate) mod handler { // Callback field indices. pub const CALL_CONTRACT: usize = field_offset!(Self, call_contract) >> 3; pub const DEPLOY: usize = field_offset!(Self, deploy) >> 3; + pub const DEPLOY_V2: usize = field_offset!(Self, deploy_v2) >> 3; pub const EMIT_EVENT: usize = field_offset!(Self, emit_event) >> 3; pub const GET_BLOCK_HASH: usize = field_offset!(Self, get_block_hash) >> 3; pub const GET_EXECUTION_INFO: usize = field_offset!(Self, get_execution_info) >> 3; @@ -677,6 +698,7 @@ pub(crate) mod handler { get_execution_info_v2: Self::wrap_get_execution_info_v2, get_execution_info_v3: Self::wrap_get_execution_info_v3, deploy: Self::wrap_deploy, + deploy_v2: Self::wrap_deploy_v2, replace_class: Self::wrap_replace_class, library_call: Self::wrap_library_call, call_contract: Self::wrap_call_contract, @@ -1067,6 +1089,43 @@ pub(crate) mod handler { }; } + extern "C" fn wrap_deploy_v2( + result_ptr: &mut SyscallResultAbi<(Felt252Abi, ArrayAbi)>, + ptr: &mut T, + gas: &mut u64, + class_hash: &Felt252Abi, + contract_address_salt: &Felt252Abi, + calldata: &ArrayAbi, + deploy_from_zero: bool, + ) { + let class_hash = Felt::from(class_hash); + let contract_address_salt = Felt::from(contract_address_salt); + + let calldata_vec: Vec<_> = calldata.into(); + + let result = ptr.deploy_v2( + class_hash, + contract_address_salt, + &calldata_vec, + deploy_from_zero, + gas, + ); + + *result_ptr = match result { + Ok(x) => { + let felts: Vec<_> = x.1.iter().map(|x| Felt252Abi(x.to_bytes_le())).collect(); + let felts_ptr = unsafe { Self::alloc_mlir_array(&felts) }; + SyscallResultAbi { + ok: ManuallyDrop::new(SyscallResultAbiOk { + tag: 0u8, + payload: ManuallyDrop::new((Felt252Abi(x.0.to_bytes_le()), felts_ptr)), + }), + } + } + Err(e) => Self::wrap_error(&e), + }; + } + extern "C" fn wrap_replace_class( result_ptr: &mut SyscallResultAbi<()>, ptr: &mut T, diff --git a/src/starknet_stub.rs b/src/starknet_stub.rs index 0009315244..1667958a26 100644 --- a/src/starknet_stub.rs +++ b/src/starknet_stub.rs @@ -32,7 +32,7 @@ use num_traits::Zero; use sha2::digest::generic_array::GenericArray; use starknet_types_core::{ felt::{Felt, NonZeroFelt}, - hash::{Pedersen, StarkHash}, + hash::{Blake2Felt252, Pedersen, StarkHash}, }; use tracing::instrument; @@ -326,6 +326,61 @@ impl StubSyscallHandler { self.execution_info.contract_address = old_contract_address; self.execution_info.caller_address = old_caller_address; } + + /// The deployer address a deploy syscall derives with: zero when `deploy_from_zero` is set, + /// the calling contract otherwise. + fn deployer_address(&self, deploy_from_zero: bool) -> Felt { + if deploy_from_zero { + Felt::zero() + } else { + self.execution_info.contract_address + } + } + + /// Registers the deployed contract and runs its constructor. Shared by `deploy` and + /// `deploy_v2`, which differ only in how they derive the address. + fn deploy_at( + &mut self, + deployed_contract_address: Felt, + deployer_address: Felt, + class_hash: Felt, + calldata: &[Felt], + remaining_gas: &mut u64, + ) -> crate::starknet::SyscallResult<(Felt, Vec)> { + let Some(contract_info) = self.contracts_info.get(&class_hash) else { + return Err(vec![Felt::from_bytes_be_slice(b"CLASS_HASH_NOT_FOUND")]); + }; + + if self + .deployed_contracts + .insert(deployed_contract_address, class_hash) + .is_some() + { + return Err(vec![Felt::from_bytes_be_slice( + b"CONTRACT_ALREADY_DEPLOYED", + )]); + } + + if let Some(constructor) = contract_info.constructor.clone() { + let old_addrs = self.open_caller_context((deployed_contract_address, deployer_address)); + let res = self.call_entry_point(remaining_gas, &constructor, calldata); + self.close_caller_context(old_addrs); + match res { + Ok(res) => Ok((deployed_contract_address, res)), + Err(mut res) => { + res.push(Felt::from_bytes_be_slice(b"CONSTRUCTOR_FAILED")); + Err(res) + } + } + } else if calldata.is_empty() { + Ok((deployed_contract_address, vec![])) + } else { + // Remove the contract from the deployed contracts, + // since it failed to deploy. + self.deployed_contracts.remove(&deployed_contract_address); + Err(vec![Felt::from_bytes_be_slice(b"INVALID_CALLDATA_LEN")]) + } + } } /// Creates a `RunResultValue` from a contract entrypoint result. @@ -503,65 +558,54 @@ impl StarknetSyscallHandler for &mut StubSyscallHandler { tracing::debug!("called"); deduct_gas(remaining_gas, gas_costs::DEPLOY)?; - /// Max value for a contract address: 2**251 - 256. - const CONTRACT_ADDRESS_BOUND: NonZeroFelt = - NonZeroFelt::from_felt_unchecked(Felt::from_hex_unchecked( - "0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00", - )); - /// Cairo string for "STARKNET_CONTRACT_ADDRESS" - const CONTRACT_ADDRESS_PREFIX: Felt = - Felt::from_hex_unchecked("0x535441524b4e45545f434f4e54524143545f41444452455353"); - - let deployer_address = if deploy_from_zero { - Felt::zero() - } else { - self.execution_info.contract_address - }; - let deployed_contract_address = { - let constructor_calldata_hash = Pedersen::hash_array(calldata); - Pedersen::hash_array(&[ - CONTRACT_ADDRESS_PREFIX, - deployer_address, - contract_address_salt, - class_hash, - constructor_calldata_hash, - ]) - .mod_floor(&CONTRACT_ADDRESS_BOUND) - }; - - let Some(contract_info) = self.contracts_info.get(&class_hash) else { - return Err(vec![Felt::from_bytes_be_slice(b"CLASS_HASH_NOT_FOUND")]); - }; + let deployer_address = self.deployer_address(deploy_from_zero); + let deployed_contract_address = Pedersen::hash_array(&[ + CONTRACT_ADDRESS_PREFIX, + deployer_address, + contract_address_salt, + class_hash, + Pedersen::hash_array(calldata), + ]) + .mod_floor(&CONTRACT_ADDRESS_BOUND); + + self.deploy_at( + deployed_contract_address, + deployer_address, + class_hash, + calldata, + remaining_gas, + ) + } - if self - .deployed_contracts - .insert(deployed_contract_address, class_hash) - .is_some() - { - return Err(vec![Felt::from_bytes_be_slice( - b"CONTRACT_ALREADY_DEPLOYED", - )]); - } + /// Identical to [`Self::deploy`] except the address is derived with + /// [`calculate_contract_address_blake_escaped`] instead of Pedersen. + #[instrument(skip(self))] + fn deploy_v2( + &mut self, + class_hash: Felt, + contract_address_salt: Felt, + calldata: &[Felt], + deploy_from_zero: bool, + remaining_gas: &mut u64, + ) -> crate::starknet::SyscallResult<(Felt, Vec)> { + tracing::debug!("called"); + deduct_gas(remaining_gas, gas_costs::DEPLOY_V2)?; + + let deployer_address = self.deployer_address(deploy_from_zero); + let deployed_contract_address = calculate_contract_address_blake_escaped( + contract_address_salt, + class_hash, + calldata, + deployer_address, + ); - if let Some(constructor) = contract_info.constructor.clone() { - let old_addrs = self.open_caller_context((deployed_contract_address, deployer_address)); - let res = self.call_entry_point(remaining_gas, &constructor, calldata); - self.close_caller_context(old_addrs); - match res { - Ok(res) => Ok((deployed_contract_address, res)), - Err(mut res) => { - res.push(Felt::from_bytes_be_slice(b"CONSTRUCTOR_FAILED")); - Err(res) - } - } - } else if calldata.is_empty() { - Ok((deployed_contract_address, vec![])) - } else { - // Remove the contract from the deployed contracts, - // since it failed to deploy. - self.deployed_contracts.remove(&deployed_contract_address); - Err(vec![Felt::from_bytes_be_slice(b"INVALID_CALLDATA_LEN")]) - } + self.deploy_at( + deployed_contract_address, + deployer_address, + class_hash, + calldata, + remaining_gas, + ) } #[instrument(skip(self))] @@ -1055,6 +1099,85 @@ pub fn deduct_gas(gas: &mut u64, price: u64) -> Result<(), Vec> { } } +/// Max value for a contract address: `2**251 - 256`. +const CONTRACT_ADDRESS_BOUND_FELT: Felt = + Felt::from_hex_unchecked("0x7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"); +const CONTRACT_ADDRESS_BOUND: NonZeroFelt = + NonZeroFelt::from_felt_unchecked(CONTRACT_ADDRESS_BOUND_FELT); +/// Cairo string for "STARKNET_CONTRACT_ADDRESS". +const CONTRACT_ADDRESS_PREFIX: Felt = + Felt::from_hex_unchecked("0x535441524b4e45545f434f4e54524143545f41444452455353"); +/// The field prime minus [`CONTRACT_ADDRESS_BOUND_FELT`]. Addresses below this bound have a second +/// lift into the field: both `address` and `address + CONTRACT_ADDRESS_BOUND_FELT` are below `p`. +const SECOND_LIFT_BOUND: Felt = + Felt::from_hex_unchecked("0x11000000000000000000000000000000000000000000000101"); +/// The STARK curve is `y^2 = x^3 + ALPHA * x + BETA`. +const STARK_CURVE_ALPHA: Felt = Felt::ONE; +const STARK_CURVE_BETA: Felt = + Felt::from_hex_unchecked("0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89"); + +/// Calculates the address of a Starknet contract with the Blake-escaped derivation used by the +/// `deploy_v2` syscall (and `deploy_account` v4). +/// +/// The raw address is the BLAKE2s felt-array hash of the same preimage `deploy` hashes with +/// Pedersen, reduced modulo [`CONTRACT_ADDRESS_BOUND_FELT`]. It is then incremented (wrapping, +/// skipping the reserved `0x0`/`0x1`) until it is provably unreachable by any Pedersen derivation, +/// so the two schemes' address spaces are disjoint. Must match the sequencer's +/// `starknet_api::core::calculate_contract_address` for the `Blake2` arm. +fn calculate_contract_address_blake_escaped( + salt: Felt, + class_hash: Felt, + constructor_calldata: &[Felt], + deployer_address: Felt, +) -> Felt { + let constructor_calldata_hash = + Blake2Felt252::encode_felt252_data_and_calc_blake_hash(constructor_calldata); + let raw_address = Blake2Felt252::encode_felt252_data_and_calc_blake_hash(&[ + CONTRACT_ADDRESS_PREFIX, + deployer_address, + salt, + class_hash, + constructor_calldata_hash, + ]) + .mod_floor(&CONTRACT_ADDRESS_BOUND); + escape_pedersen_image(raw_address) +} + +/// Returns `value^3 + STARK_CURVE_ALPHA * value + STARK_CURVE_BETA` — a square in the field iff +/// `value` is the x-coordinate of a STARK curve point. +fn stark_curve_cubic(value: Felt) -> Felt { + value * value * value + STARK_CURVE_ALPHA * value + STARK_CURVE_BETA +} + +/// Returns whether `value` is the x-coordinate of a STARK curve point (`stark_curve_cubic(value)` +/// is a quadratic residue, i.e. `t == 0` or `legendre(t) == 1`). +fn is_stark_curve_x_coordinate(value: Felt) -> bool { + stark_curve_cubic(value).sqrt().is_some() +} + +/// Returns whether some Pedersen hash output reduces (mod [`CONTRACT_ADDRESS_BOUND_FELT`]) to +/// `address`. A Pedersen output is the x-coordinate of a STARK curve point, so `address` is +/// reachable iff one of its lifts into the field is a curve x-coordinate. +fn is_pedersen_reachable_address(address: Felt) -> bool { + is_stark_curve_x_coordinate(address) + || (address < SECOND_LIFT_BOUND + && is_stark_curve_x_coordinate(address + CONTRACT_ADDRESS_BOUND_FELT)) +} + +/// Increments `raw_address` (wrapping mod [`CONTRACT_ADDRESS_BOUND_FELT`], skipping the reserved +/// `0x0`/`0x1`) until no Pedersen derivation can reach it. Expected ~1 increment; each step costs +/// one residuosity check, never a re-hash. +fn escape_pedersen_image(raw_address: Felt) -> Felt { + let mut address = raw_address; + while address < Felt::TWO || is_pedersen_reachable_address(address) { + address += Felt::ONE; + if address == CONTRACT_ADDRESS_BOUND_FELT { + address = Felt::ZERO; + } + } + address +} + /// Gas costs for syscalls. /// /// Taken from cairo-lang-runner syscall handler implementation. @@ -1069,6 +1192,8 @@ mod gas_costs { // Gas cost for each syscall, minus the precharged base amount. pub const CALL_CONTRACT: u64 = 10 * STEP + ENTRY_POINT; pub const DEPLOY: u64 = 200 * STEP + ENTRY_POINT; + // Provisional: same profile as `DEPLOY` until a measured one lands (sequencer-side). + pub const DEPLOY_V2: u64 = 200 * STEP + ENTRY_POINT; pub const EMIT_EVENT: u64 = 10 * STEP; pub const GET_BLOCK_HASH: u64 = 50 * STEP; pub const GET_EXECUTION_INFO: u64 = 10 * STEP; @@ -1098,6 +1223,84 @@ mod gas_costs { mod tests { use super::*; + /// The five frozen Blake-escaped derivation vectors (0/1/2/3/7 escape steps), shared with the + /// sequencer and the cairo runner. `deploy_from_zero = true` (deployer = 0), + /// `class_hash = 0x4242`, `calldata = [42, 2 ** 63, 1337]`. + #[test] + fn test_calculate_contract_address_blake_escaped() { + let deployer_address = Felt::ZERO; + let class_hash = Felt::from(0x4242); + let calldata = [Felt::from(42), Felt::TWO.pow(63_u32), Felt::from(1337)]; + + for (salt, expected) in [ + ( + 777, + "0x781e95f4b806dfe5b550756620c77a108d974a5b5d1198b1d45901ac1f89e9f", + ), + ( + 771, + "0x566c3e328f3fd5a311267250cadc3c1c4de799db54180fcf862fe90b622571d", + ), + ( + 776, + "0x1cd7f5c31ef1b147b816048b025a6cc345e7e023aa8ed97222a883e31dc8435", + ), + ( + 775, + "0x4f7ba32369d7f68c42a7619242a52a5be9e803459f5afae1772d9377b161c4c", + ), + ( + 774, + "0x47d0c1ff356a1d540cd9f2efa122b60168b1007ef0603af0857bc6100e4b8e8", + ), + ] { + let salt = Felt::from(salt); + let address = calculate_contract_address_blake_escaped( + salt, + class_hash, + &calldata, + deployer_address, + ); + assert_eq!( + address, + Felt::from_hex(expected).unwrap(), + "wrong Blake-escaped address for salt {salt}", + ); + + // The escape puts the address outside the Pedersen image, so `deploy` cannot reach it. + assert!(!is_pedersen_reachable_address(address)); + let pedersen_address = Pedersen::hash_array(&[ + CONTRACT_ADDRESS_PREFIX, + deployer_address, + salt, + class_hash, + Pedersen::hash_array(&calldata), + ]) + .mod_floor(&CONTRACT_ADDRESS_BOUND); + assert_ne!( + address, pedersen_address, + "salt {salt} collides with Pedersen" + ); + } + } + + /// The frozen `pedersen_reachable` truth table, shared with the sequencer and the cairo runner. + #[test] + fn test_is_pedersen_reachable_address() { + for (address, expected) in [ + (0x1_u64, true), + (0x2_u64, true), + (0x5_u64, false), + (0x1234567890abcdef_u64, true), + ] { + assert_eq!( + is_pedersen_reachable_address(Felt::from(address)), + expected, + "wrong pedersen_reachable for {address:#x}", + ); + } + } + #[test] fn test_secp256k1_get_xy() { let p = Secp256k1Point { diff --git a/test_data/programs/starknet/syscalls.cairo b/test_data/programs/starknet/syscalls.cairo index 58276344a4..a24bf7ab01 100644 --- a/test_data/programs/starknet/syscalls.cairo +++ b/test_data/programs/starknet/syscalls.cairo @@ -1,6 +1,6 @@ use starknet::{ call_contract_syscall, ContractAddress, - deploy_syscall, emit_event_syscall, ExecutionInfo, get_block_hash_syscall, + deploy_syscall, deploy_v2_syscall, emit_event_syscall, ExecutionInfo, get_block_hash_syscall, keccak_syscall, library_call_syscall, replace_class_syscall, send_message_to_l1_syscall, storage_address_try_from_felt252, storage_read_syscall, storage_write_syscall, SyscallResult, @@ -43,6 +43,10 @@ fn deploy() -> SyscallResult<(ContractAddress, Span)> { deploy_syscall(ZERO_CLASS_HASH, 0, array![].span(), false) } +fn deploy_v2() -> SyscallResult<(ContractAddress, Span)> { + deploy_v2_syscall(ZERO_CLASS_HASH, 0, array![].span(), false) +} + fn replace_class() -> SyscallResult<()> { replace_class_syscall(ZERO_CLASS_HASH) } diff --git a/tests/tests/starknet/box_arena.rs b/tests/tests/starknet/box_arena.rs index 6bdf6fbc17..e5f64601a8 100644 --- a/tests/tests/starknet/box_arena.rs +++ b/tests/tests/starknet/box_arena.rs @@ -107,6 +107,17 @@ impl StarknetSyscallHandler for &mut MultiContractHandler { ) -> SyscallResult<(Felt, Vec)> { unimplemented!() } + + fn deploy_v2( + &mut self, + _class_hash: Felt, + _contract_address_salt: Felt, + _calldata: &[Felt], + _deploy_from_zero: bool, + _remaining_gas: &mut u64, + ) -> SyscallResult<(Felt, Vec)> { + unimplemented!() + } fn replace_class(&mut self, _class_hash: Felt, _remaining_gas: &mut u64) -> SyscallResult<()> { unimplemented!() } diff --git a/tests/tests/starknet/secp256.rs b/tests/tests/starknet/secp256.rs index 6cc9e4ac28..61c3b7b073 100644 --- a/tests/tests/starknet/secp256.rs +++ b/tests/tests/starknet/secp256.rs @@ -70,6 +70,17 @@ impl StarknetSyscallHandler for &mut SyscallHandler { unimplemented!() } + fn deploy_v2( + &mut self, + _class_hash: Felt, + _contract_address_salt: Felt, + _calldata: &[Felt], + _deploy_from_zero: bool, + _remaining_gas: &mut u64, + ) -> SyscallResult<(Felt, Vec)> { + unimplemented!() + } + fn replace_class(&mut self, _class_hash: Felt, _remaining_gas: &mut u64) -> SyscallResult<()> { unimplemented!() } diff --git a/tests/tests/starknet/syscalls.rs b/tests/tests/starknet/syscalls.rs index 33d8d7bd62..358cfb8b4e 100644 --- a/tests/tests/starknet/syscalls.rs +++ b/tests/tests/starknet/syscalls.rs @@ -254,6 +254,23 @@ impl StarknetSyscallHandler for SyscallHandler { )) } + fn deploy_v2( + &mut self, + _class_hash: Felt, + _contract_address_salt: Felt, + _calldata: &[Felt], + _deploy_from_zero: bool, + _remaining_gas: &mut u64, + ) -> SyscallResult<(Felt, Vec)> { + // A distinct value from `deploy` (the salt-777 §3.1 frozen vector), so the JIT test + // confirms `deploy_v2_syscall` routes to this method and not to `deploy`. + Ok(( + Felt::from_hex("0x781e95f4b806dfe5b550756620c77a108d974a5b5d1198b1d45901ac1f89e9f") + .unwrap(), + Vec::new(), + )) + } + fn replace_class(&mut self, _class_hash: Felt, _remaining_gas: &mut u64) -> SyscallResult<()> { Ok(()) } @@ -934,6 +951,44 @@ fn deploy() { ) } +#[test] +fn deploy_v2() { + let program = load_program_and_runner("programs/starknet/syscalls"); + let result = run_native_program( + &program, + "deploy_v2", + &[], + Some(u64::MAX), + Some(SyscallHandler::new()), + ); + + // The salt-777 §3.1 frozen vector returned by the stub's `deploy_v2` — distinct from `deploy`'s + // return, confirming `deploy_v2_syscall` dispatches (via the DEPLOY_V2 vtable offset) to the + // handler's `deploy_v2` method. + assert_eq_sorted!( + result.return_value, + Value::Enum { + tag: 0, + value: Box::new(Value::Struct { + fields: vec![ + Value::Felt252( + Felt::from_hex( + "0x781e95f4b806dfe5b550756620c77a108d974a5b5d1198b1d45901ac1f89e9f", + ) + .unwrap() + ), + Value::Struct { + fields: vec![Value::Array(Vec::new())], + debug_name: None, + }, + ], + debug_name: None, + }), + debug_name: None, + }, + ) +} + #[test] fn replace_class() { let program = load_program_and_runner("programs/starknet/syscalls"); diff --git a/vendor/cairo b/vendor/cairo index 079cb24e76..476e82ec61 160000 --- a/vendor/cairo +++ b/vendor/cairo @@ -1 +1 @@ -Subproject commit 079cb24e76d995f05d16248691e26e5a77cb8254 +Subproject commit 476e82ec61bbff69e64c81a325e4d3a99d094304