From 654966afa4bad2d2ee52d4cc5650e84d8cc6eacf Mon Sep 17 00:00:00 2001 From: Oba Date: Wed, 28 Aug 2024 13:35:11 +0200 Subject: [PATCH 1/5] feat: ecAdd / ecMul --- crates/evm/src/precompiles.cairo | 24 ++++---------- crates/evm/src/precompiles/ec_add.cairo | 44 +++++++++++++++++++++++++ crates/evm/src/precompiles/ec_mul.cairo | 42 +++++++++++++++++++++-- 3 files changed, 91 insertions(+), 19 deletions(-) diff --git a/crates/evm/src/precompiles.cairo b/crates/evm/src/precompiles.cairo index 88c640e50..bad273661 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; @@ -65,23 +67,11 @@ impl PrecompilesImpl of Precompiles { precompile_address.address ) }, - 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 - ) - }, - 0x08 => { + 4 => { Identity::exec(input)? }, + 5 => { ModExp::exec(input)? }, + 6 => { EcAdd::exec(input)? }, + 7 => { EcMul::exec(input)? }, + 8 => { // we should never reach this branch! panic!( "pre-compile at address {} isn't implemented yet", diff --git a/crates/evm/src/precompiles/ec_add.cairo b/crates/evm/src/precompiles/ec_add.cairo index 21caa5808..de1c393a7 100644 --- a/crates/evm/src/precompiles/ec_add.cairo +++ b/crates/evm/src/precompiles/ec_add.cairo @@ -18,6 +18,50 @@ 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; + + // Load x1 + let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); + let x1: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); + // Load y1 + let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); + let y1: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); + // Load x2 + let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); + let x2: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); + // Load y2 + let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); + let y2: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); + + let (x, y) = match ec_add(x1, y1, x2, y2) { + Option::Some((x, y)) => { (x, y) }, + Option::None => (0, 0), + }; + + 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..46a0bd085 100644 --- a/crates/evm/src/precompiles/ec_mul.cairo +++ b/crates/evm/src/precompiles/ec_mul.cairo @@ -18,10 +18,49 @@ 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 = 150; +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; + + // from_be_bytes should be used + + // Load x + let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); + let x: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); + // Load y + let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); + let y: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); + // Load s + let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); + let s: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); + + let (x, y) = match ec_mul(x, y, s) { + Option::Some((x, y)) => { (x, y) }, + Option::None => (0, 0), + }; + + // 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 +139,3 @@ fn ec_mul_inner(pt: (u384, u384), mut bits: Array) -> Option<(u384, u38 pt } - From ee2c0399ce3d8c0047ce8a165d7bfb8c6157c882 Mon Sep 17 00:00:00 2001 From: Oba Date: Wed, 28 Aug 2024 13:40:42 +0200 Subject: [PATCH 2/5] fix: gas cost ecMul 6000 --- crates/evm/src/precompiles/ec_mul.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/evm/src/precompiles/ec_mul.cairo b/crates/evm/src/precompiles/ec_mul.cairo index 46a0bd085..b6650597f 100644 --- a/crates/evm/src/precompiles/ec_mul.cairo +++ b/crates/evm/src/precompiles/ec_mul.cairo @@ -22,7 +22,7 @@ use utils::helpers::{load_word, u256_to_bytes_array, U256Trait, ToBytes, FromByt // const BN254_ORDER: u256 = 0x30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001; -const BASE_COST: u128 = 150; +const BASE_COST: u128 = 6000; const U256_BYTES_LEN: usize = 32; impl EcMul of Precompile { From 6c8dac1ab17f425a55052a8896afd93a452c42b6 Mon Sep 17 00:00:00 2001 From: enitrat Date: Thu, 5 Sep 2024 10:45:43 +0200 Subject: [PATCH 3/5] fix: returndata on wrong precompile input --- .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/ec_add.cairo | 27 ++++++++++---------- crates/evm/src/precompiles/ec_mul.cairo | 27 ++++++++++---------- crates/openzeppelin/Scarb.toml | 2 +- crates/snforge_utils/Scarb.toml | 2 +- crates/utils/Scarb.toml | 2 +- 10 files changed, 37 insertions(+), 37 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/ec_add.cairo b/crates/evm/src/precompiles/ec_add.cairo index de1c393a7..1bbac18ec 100644 --- a/crates/evm/src/precompiles/ec_add.cairo +++ b/crates/evm/src/precompiles/ec_add.cairo @@ -33,22 +33,23 @@ impl EcAdd of Precompile { fn exec(mut input: Span) -> Result<(u128, Span), EVMError> { let gas = BASE_COST; - // Load x1 - let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); - let x1: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); - // Load y1 - let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); - let y1: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); - // Load x2 - let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); - let x2: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); - // Load y2 - let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); - let y2: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); + 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 => (0, 0), + Option::None => { + return Result::Err(EVMError::InvalidParameter('invalid ec_add parameters')); + }, }; let mut result_bytes = array![]; diff --git a/crates/evm/src/precompiles/ec_mul.cairo b/crates/evm/src/precompiles/ec_mul.cairo index b6650597f..c3f495982 100644 --- a/crates/evm/src/precompiles/ec_mul.cairo +++ b/crates/evm/src/precompiles/ec_mul.cairo @@ -33,21 +33,20 @@ impl EcMul of Precompile { fn exec(mut input: Span) -> Result<(u128, Span), EVMError> { let gas = BASE_COST; - // from_be_bytes should be used - - // Load x - let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); - let x: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); - // Load y - let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); - let y: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); - // Load s - let bytes_32 = *(input.multi_pop_front::<32>().unwrap()); - let s: u256 = load_word(U256_BYTES_LEN, bytes_32.unbox().span()); - - let (x, y) = match ec_mul(x, y, s) { + 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 => (0, 0), + Option::None => { + return Result::Err(EVMError::InvalidParameter('invalid ec_mul parameters')); + }, }; // Append x and y to the result bytes. 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/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" From bb796989d4b2f898c786550d02261fd30a9d2ae0 Mon Sep 17 00:00:00 2001 From: enitrat Date: Thu, 5 Sep 2024 10:49:09 +0200 Subject: [PATCH 4/5] fmt --- crates/evm/src/precompiles.cairo | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/evm/src/precompiles.cairo b/crates/evm/src/precompiles.cairo index bad273661..de389953e 100644 --- a/crates/evm/src/precompiles.cairo +++ b/crates/evm/src/precompiles.cairo @@ -67,11 +67,11 @@ impl PrecompilesImpl of Precompiles { precompile_address.address ) }, - 4 => { Identity::exec(input)? }, - 5 => { ModExp::exec(input)? }, - 6 => { EcAdd::exec(input)? }, - 7 => { EcMul::exec(input)? }, - 8 => { + 0x04 => { Identity::exec(input)? }, + 0x05 => { ModExp::exec(input)? }, + 0x06 => { EcAdd::exec(input)? }, + 0x07 => { EcMul::exec(input)? }, + 0x08 => { // we should never reach this branch! panic!( "pre-compile at address {} isn't implemented yet", From e0350c35ed2aa29a56d3261bbfee20fdf1a9e1f4 Mon Sep 17 00:00:00 2001 From: enitrat Date: Thu, 5 Sep 2024 11:54:27 +0200 Subject: [PATCH 5/5] remove custom clone impls in snforge_utils --- crates/snforge_utils/src/lib.cairo | 61 ------------------------------ 1 file changed, 61 deletions(-) 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();