From 5132b8ca7933ff4b8d4f8b9b3540f2d0bbdf58ed Mon Sep 17 00:00:00 2001 From: Oba Date: Thu, 5 Sep 2024 15:05:05 +0200 Subject: [PATCH] feat: ecAdd / ecMul (#880) * feat: ecAdd / ecMul * fix: gas cost ecMul 6000 * fix: returndata on wrong precompile input * fmt * remove custom clone impls in snforge_utils --------- Co-authored-by: enitrat --- .tool-versions | 2 +- Scarb.lock | 6 +- crates/alexandria_data_structures/Scarb.toml | 2 +- crates/contracts/Scarb.toml | 2 +- crates/evm/Scarb.toml | 2 +- crates/evm/src/precompiles.cairo | 18 ++---- crates/evm/src/precompiles/ec_add.cairo | 45 +++++++++++++++ crates/evm/src/precompiles/ec_mul.cairo | 41 ++++++++++++- crates/openzeppelin/Scarb.toml | 2 +- crates/snforge_utils/Scarb.toml | 2 +- crates/snforge_utils/src/lib.cairo | 61 -------------------- crates/utils/Scarb.toml | 2 +- 12 files changed, 98 insertions(+), 87 deletions(-) diff --git a/.tool-versions b/.tool-versions index f5e9048bd..30efda798 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ scarb 2.7.1 -starknet-foundry 0.28.0 +starknet-foundry 0.30.0 diff --git a/Scarb.lock b/Scarb.lock index cdb62e294..72eb246ac 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -46,12 +46,12 @@ dependencies = [ [[package]] name = "snforge_scarb_plugin" version = "0.1.0" -source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.28.0#4dfe39d96690ed6b3d56971512700de3f58288ea" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.30.0#196f06b251926697c3d66800f2a93ae595e76496" [[package]] name = "snforge_std" -version = "0.28.0" -source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.28.0#4dfe39d96690ed6b3d56971512700de3f58288ea" +version = "0.30.0" +source = "git+https://github.com/foundry-rs/starknet-foundry.git?tag=v0.30.0#196f06b251926697c3d66800f2a93ae595e76496" dependencies = [ "snforge_scarb_plugin", ] diff --git a/crates/alexandria_data_structures/Scarb.toml b/crates/alexandria_data_structures/Scarb.toml index 31f1107b2..4f8f3c192 100644 --- a/crates/alexandria_data_structures/Scarb.toml +++ b/crates/alexandria_data_structures/Scarb.toml @@ -7,7 +7,7 @@ version = "0.1.0" [dependencies] [dev-dependencies] -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.28.0" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.30.0" } [scripts] test = "snforge test --max-n-steps 4294967295" diff --git a/crates/contracts/Scarb.toml b/crates/contracts/Scarb.toml index 1f14dd65d..4b2416282 100644 --- a/crates/contracts/Scarb.toml +++ b/crates/contracts/Scarb.toml @@ -23,7 +23,7 @@ build-external-contracts = ["openzeppelin::token::erc20::erc20::ERC20"] name = "contracts" [dev-dependencies] -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.28.0" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.30.0" } assert_macros = "0.1.0" snforge_utils = { path = "../snforge_utils" } diff --git a/crates/evm/Scarb.toml b/crates/evm/Scarb.toml index 8fe56505a..a88b0d56e 100644 --- a/crates/evm/Scarb.toml +++ b/crates/evm/Scarb.toml @@ -13,7 +13,7 @@ openzeppelin = { path = "../openzeppelin" } garaga = { git = "https://github.com/keep-starknet-strange/garaga.git" } [dev-dependencies] -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.28.0" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.30.0" } snforge_utils = { path = "../snforge_utils" } assert_macros = "0.1.0" diff --git a/crates/evm/src/precompiles.cairo b/crates/evm/src/precompiles.cairo index 88c640e50..de389953e 100644 --- a/crates/evm/src/precompiles.cairo +++ b/crates/evm/src/precompiles.cairo @@ -12,6 +12,8 @@ use evm::errors::EVMError; use evm::model::vm::VM; use evm::model::vm::VMTrait; use evm::precompiles::blake2f::Blake2f; +use evm::precompiles::ec_add::EcAdd; +use evm::precompiles::ec_mul::EcMul; use evm::precompiles::ec_recover::EcRecover; use evm::precompiles::identity::Identity; use evm::precompiles::modexp::ModExp; @@ -67,20 +69,8 @@ impl PrecompilesImpl of Precompiles { }, 0x04 => { Identity::exec(input)? }, 0x05 => { ModExp::exec(input)? }, - 0x06 => { - // we should never reach this branch! - panic!( - "pre-compile at address {} isn't implemented yet", - precompile_address.address - ) - }, - 0x07 => { - // we should never reach this branch! - panic!( - "pre-compile at address {} isn't implemented yet", - precompile_address.address - ) - }, + 0x06 => { EcAdd::exec(input)? }, + 0x07 => { EcMul::exec(input)? }, 0x08 => { // we should never reach this branch! panic!( diff --git a/crates/evm/src/precompiles/ec_add.cairo b/crates/evm/src/precompiles/ec_add.cairo index 21caa5808..1bbac18ec 100644 --- a/crates/evm/src/precompiles/ec_add.cairo +++ b/crates/evm/src/precompiles/ec_add.cairo @@ -18,6 +18,51 @@ use evm::precompiles::Precompile; use garaga::core::circuit::AddInputResultTrait2; use garaga::utils::u384_eq_zero; use utils::helpers::{U256Trait, ToBytes, FromBytes}; +use utils::helpers::{load_word, u256_to_bytes_array}; + + +const BASE_COST: u128 = 150; +const U256_BYTES_LEN: usize = 32; + +impl EcAdd of Precompile { + #[inline(always)] + fn address() -> EthAddress { + EthAddress { address: 0x6 } + } + + fn exec(mut input: Span) -> Result<(u128, Span), EVMError> { + let gas = BASE_COST; + + let x1_bytes = *(input.multi_pop_front::<32>().unwrap()); + let x1: u256 = load_word(U256_BYTES_LEN, x1_bytes.unbox().span()); + + let y1_bytes = *(input.multi_pop_front::<32>().unwrap()); + let y1: u256 = load_word(U256_BYTES_LEN, y1_bytes.unbox().span()); + + let x2_bytes = *(input.multi_pop_front::<32>().unwrap()); + let x2: u256 = load_word(U256_BYTES_LEN, x2_bytes.unbox().span()); + + let y2_bytes = *(input.multi_pop_front::<32>().unwrap()); + let y2: u256 = load_word(U256_BYTES_LEN, y2_bytes.unbox().span()); + + let (x, y) = match ec_add(x1, y1, x2, y2) { + Option::Some((x, y)) => { (x, y) }, + Option::None => { + return Result::Err(EVMError::InvalidParameter('invalid ec_add parameters')); + }, + }; + + let mut result_bytes = array![]; + // Append x to the result bytes. + let x_bytes = x.to_be_bytes_padded(); + result_bytes.append_span(x_bytes); + // Append y to the result bytes. + let y_bytes = y.to_be_bytes_padded(); + result_bytes.append_span(y_bytes); + + return Result::Ok((gas, result_bytes.span())); + } +} fn ec_add(x1: u256, y1: u256, x2: u256, y2: u256) -> Option<(u256, u256)> { diff --git a/crates/evm/src/precompiles/ec_mul.cairo b/crates/evm/src/precompiles/ec_mul.cairo index 92e4652fd..c3f495982 100644 --- a/crates/evm/src/precompiles/ec_mul.cairo +++ b/crates/evm/src/precompiles/ec_mul.cairo @@ -18,10 +18,48 @@ use evm::precompiles::ec_add::{ }; use garaga::core::circuit::AddInputResultTrait2; use garaga::utils::u384_eq_zero; -use utils::helpers::{U256Trait, ToBytes, FromBytes}; +use utils::helpers::{load_word, u256_to_bytes_array, U256Trait, ToBytes, FromBytes}; // const BN254_ORDER: u256 = 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001; +const BASE_COST: u128 = 6000; +const U256_BYTES_LEN: usize = 32; + +impl EcMul of Precompile { + fn address() -> EthAddress { + EthAddress { address: 0x7 } + } + + fn exec(mut input: Span) -> Result<(u128, Span), EVMError> { + let gas = BASE_COST; + + let x1_bytes = *(input.multi_pop_front::<32>().unwrap()); + let x1: u256 = load_word(U256_BYTES_LEN, x1_bytes.unbox().span()); + + let y1_bytes = *(input.multi_pop_front::<32>().unwrap()); + let y1: u256 = load_word(U256_BYTES_LEN, y1_bytes.unbox().span()); + + let s_bytes = *(input.multi_pop_front::<32>().unwrap()); + let s: u256 = load_word(U256_BYTES_LEN, s_bytes.unbox().span()); + + let (x, y) = match ec_mul(x1, y1, s) { + Option::Some((x, y)) => { (x, y) }, + Option::None => { + return Result::Err(EVMError::InvalidParameter('invalid ec_mul parameters')); + }, + }; + + // Append x and y to the result bytes. + let mut result_bytes = array![]; + let x_bytes = x.to_be_bytes_padded(); + result_bytes.append_span(x_bytes); + let y_bytes = y.to_be_bytes_padded(); + result_bytes.append_span(y_bytes); + + return Result::Ok((gas, result_bytes.span())); + } +} + // Returns Option::None in case of error. fn ec_mul(x1: u256, y1: u256, s: u256) -> Option<(u256, u256)> { if x1 == 0 && y1 == 0 { @@ -100,4 +138,3 @@ fn ec_mul_inner(pt: (u384, u384), mut bits: Array) -> Option<(u384, u38 pt } - diff --git a/crates/openzeppelin/Scarb.toml b/crates/openzeppelin/Scarb.toml index fb120f377..5e139c1e0 100644 --- a/crates/openzeppelin/Scarb.toml +++ b/crates/openzeppelin/Scarb.toml @@ -24,7 +24,7 @@ starknet.workspace = true fmt.workspace = true [dev-dependencies] -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.28.0" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.30.0" } [scripts] test = "snforge test --max-n-steps 4294967295" diff --git a/crates/snforge_utils/Scarb.toml b/crates/snforge_utils/Scarb.toml index 9efe533f8..0c9437456 100644 --- a/crates/snforge_utils/Scarb.toml +++ b/crates/snforge_utils/Scarb.toml @@ -10,7 +10,7 @@ starknet = "2.7.1" evm = { path = "../evm" } [dev-dependencies] -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.28.0" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.30.0" } [[target.starknet-contract]] sierra = true diff --git a/crates/snforge_utils/src/lib.cairo b/crates/snforge_utils/src/lib.cairo index 16507e6f1..c3aa04e76 100644 --- a/crates/snforge_utils/src/lib.cairo +++ b/crates/snforge_utils/src/lib.cairo @@ -18,67 +18,6 @@ pub mod snforge_utils { get_call_trace, CallTrace, CallEntryPoint, CallResult, EntryPointType, CallType, CallFailure }; - impl CloneEntryPointType of Clone { - fn clone(self: @EntryPointType) -> EntryPointType { - match self { - EntryPointType::Constructor => EntryPointType::Constructor, - EntryPointType::External => EntryPointType::External, - EntryPointType::L1Handler => EntryPointType::L1Handler, - } - } - } - - impl CloneCallEntryPoint of Clone { - fn clone(self: @CallEntryPoint) -> CallEntryPoint { - CallEntryPoint { - entry_point_type: self.entry_point_type.clone(), - entry_point_selector: self.entry_point_selector.clone(), - calldata: self.calldata.clone(), - contract_address: self.contract_address.clone(), - caller_address: self.caller_address.clone(), - call_type: self.call_type.clone(), - } - } - } - - impl CloneCallType of Clone { - fn clone(self: @CallType) -> CallType { - match self { - CallType::Call => CallType::Call, - CallType::Delegate => CallType::Delegate, - } - } - } - - impl CloneCallResult of Clone { - fn clone(self: @CallResult) -> CallResult { - match self { - CallResult::Success(val) => CallResult::Success(val.clone()), - CallResult::Failure(val) => CallResult::Failure(val.clone()), - } - } - } - - impl CloneCallFailure of Clone { - fn clone(self: @CallFailure) -> CallFailure { - match self { - CallFailure::Panic(val) => CallFailure::Panic(val.clone()), - CallFailure::Error(val) => CallFailure::Error(val.clone()), - } - } - } - - - impl CloneCallTrace of Clone { - fn clone(self: @CallTrace) -> CallTrace { - CallTrace { - entry_point: self.entry_point.clone(), - nested_calls: self.nested_calls.clone(), - result: self.result.clone(), - } - } - } - pub fn is_called(contract_address: ContractAddress, selector: felt252) -> bool { let call_trace = get_call_trace(); diff --git a/crates/utils/Scarb.toml b/crates/utils/Scarb.toml index ad856c94a..86f7c3d06 100644 --- a/crates/utils/Scarb.toml +++ b/crates/utils/Scarb.toml @@ -13,7 +13,7 @@ alexandria_data_structures = { path = "../alexandria_data_structures" } fmt.workspace = true [dev-dependencies] -snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.28.0" } +snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry.git", tag = "v0.30.0" } [scripts] test = "snforge test --max-n-steps 4294967295"