diff --git a/crates/evm/src/call_helpers.cairo b/crates/evm/src/call_helpers.cairo index 4ea732d09..4f63d90e6 100644 --- a/crates/evm/src/call_helpers.cairo +++ b/crates/evm/src/call_helpers.cairo @@ -121,5 +121,5 @@ impl CallHelpersImpl of CallHelpers { /// Check whether an address for a call-family opcode is a precompile. fn is_precompile(self: EthAddress) -> bool { let self: felt252 = self.into(); - return (self != 0 && self.into() < 10_u256); + return (self != 0 && (self.into() < 10_u256 || self.into() == 256_u256)); } diff --git a/crates/evm/src/precompiles.cairo b/crates/evm/src/precompiles.cairo index 81108f0a1..ef18d4ce9 100644 --- a/crates/evm/src/precompiles.cairo +++ b/crates/evm/src/precompiles.cairo @@ -2,6 +2,7 @@ mod blake2f; mod ec_recover; mod identity; mod modexp; +mod p256verify; mod sha256; use core::traits::Into; @@ -12,6 +13,7 @@ use evm::precompiles::blake2f::Blake2f; use evm::precompiles::ec_recover::EcRecover; use evm::precompiles::identity::Identity; use evm::precompiles::modexp::ModExp; +use evm::precompiles::p256verify::P256Verify; use evm::precompiles::sha256::Sha256; use starknet::EthAddress; @@ -27,43 +29,51 @@ impl PrecompilesImpl of Precompiles { let precompile_address = vm.message.target.evm; let input = vm.message().data; - let (gas, result) = match precompile_address.address { - 0 => { - // we should never reach this branch! - panic!("pre-compile address can't be 0") - }, - 1 => { EcRecover::exec(input)? }, - 2 => { Sha256::exec(input)? }, - 3 => { - // we should never reach this branch! - panic!( - "pre-compile at address {} isn't implemented yet", precompile_address.address - ) - }, - 4 => { Identity::exec(input)? }, - 5 => { ModExp::exec(input)? }, - 6 => { - // we should never reach this branch! - panic!( - "pre-compile at address {} isn't implemented yet", precompile_address.address - ) - }, - 7 => { - // we should never reach this branch! - panic!( - "pre-compile at address {} isn't implemented yet", precompile_address.address - ) - }, - 8 => { - // we should never reach this branch! - panic!( - "pre-compile at address {} isn't implemented yet", precompile_address.address - ) - }, - 9 => { Blake2f::exec(input)? }, - _ => { - // we should never reach this branch! - panic!("address {} isn't a pre-compile", precompile_address.address) + let (gas, result) = if precompile_address.address == 0x100 { + P256Verify::exec(input)? + } else { + match precompile_address.address { + 0 => { + // we should never reach this branch! + panic!("pre-compile address can't be 0") + }, + 1 => { EcRecover::exec(input)? }, + 2 => { Sha256::exec(input)? }, + 3 => { + // we should never reach this branch! + panic!( + "pre-compile at address {} isn't implemented yet", + precompile_address.address + ) + }, + 4 => { Identity::exec(input)? }, + 5 => { ModExp::exec(input)? }, + 6 => { + // we should never reach this branch! + panic!( + "pre-compile at address {} isn't implemented yet", + precompile_address.address + ) + }, + 7 => { + // we should never reach this branch! + panic!( + "pre-compile at address {} isn't implemented yet", + precompile_address.address + ) + }, + 8 => { + // we should never reach this branch! + panic!( + "pre-compile at address {} isn't implemented yet", + precompile_address.address + ) + }, + 9 => { Blake2f::exec(input)? }, + _ => { + // we should never reach this branch! + panic!("address {} isn't a pre-compile", precompile_address.address) + } } }; diff --git a/crates/evm/src/precompiles/p256verify.cairo b/crates/evm/src/precompiles/p256verify.cairo new file mode 100644 index 000000000..76cf12ff5 --- /dev/null +++ b/crates/evm/src/precompiles/p256verify.cairo @@ -0,0 +1,67 @@ +use core::starknet::SyscallResultTrait; +use evm::errors::{EVMError}; +use evm::precompiles::Precompile; +use starknet::{ + EthAddress, eth_signature::{recover_public_key, public_key_point_to_eth_address, Signature}, + secp256r1::{Secp256r1Point, secp256r1_new_syscall}, secp256_trait::is_valid_signature +}; +use utils::helpers::{U256Trait, ToBytes, FromBytes}; + +const P256VERIFY_PRECOMPILE_GAS_COST: u128 = 3450; + +impl P256Verify of Precompile { + #[inline(always)] + fn address() -> EthAddress { + EthAddress { address: 0x100 } + } + + fn exec(input: Span) -> Result<(u128, Span), EVMError> { + let gas: u128 = P256VERIFY_PRECOMPILE_GAS_COST; + + if input.len() != 160 { + return Result::Ok((gas, array![0].span())); + } + + let message_hash = input.slice(0, 32); + let message_hash = match message_hash.from_be_bytes() { + Option::Some(message_hash) => message_hash, + Option::None => { return Result::Ok((gas, array![].span())); } + }; + + let r: Option = input.slice(32, 32).from_be_bytes(); + let r = match r { + Option::Some(r) => r, + Option::None => { return Result::Ok((gas, array![].span())); } + }; + + let s: Option = input.slice(64, 32).from_be_bytes(); + let s = match s { + Option::Some(s) => s, + Option::None => { return Result::Ok((gas, array![].span())); } + }; + + let x: Option = input.slice(96, 32).from_be_bytes(); + let x = match x { + Option::Some(x) => x, + Option::None => { return Result::Ok((gas, array![].span())); } + }; + + let y: Option = input.slice(128, 32).from_be_bytes(); + let y = match y { + Option::Some(y) => y, + Option::None => { return Result::Ok((gas, array![].span())); } + }; + + let public_key: Option = secp256r1_new_syscall(x, y).unwrap_syscall(); + let public_key = match public_key { + Option::Some(public_key) => public_key, + Option::None => { return Result::Ok((gas, array![].span())); } + }; + + if !is_valid_signature(message_hash, r, s, public_key) { + return Result::Ok((gas, array![0].span())); + } + + return Result::Ok((gas, array![1].span())); + } +} diff --git a/crates/evm/src/tests/test_instructions/test_system_operations.cairo b/crates/evm/src/tests/test_instructions/test_system_operations.cairo index c66bf573d..00c5d0771 100644 --- a/crates/evm/src/tests/test_instructions/test_system_operations.cairo +++ b/crates/evm/src/tests/test_instructions/test_system_operations.cairo @@ -95,7 +95,7 @@ fn test_exec_call() { kakarot_core.deploy_externally_owned_account(evm_address); // Set vm bytecode - // (call 0xffffff 0x100 0 0 0 0 1) + // (call 0xffffff 0xabfa740ccd 0 0 0 0 1) let bytecode = array![ 0x60, 0x01, @@ -107,9 +107,12 @@ fn test_exec_call() { 0x00, 0x60, 0x00, - 0x61, - 0x01, - 0x00, + 0x64, + 0xab, + 0xfa, + 0x74, + 0x0c, + 0xcd, 0x62, 0xff, 0xff, @@ -122,13 +125,13 @@ fn test_exec_call() { let mut vm = VMBuilderTrait::new_with_presets().with_bytecode(bytecode).build(); - // Deploy bytecode at 0x100 + // Deploy bytecode at 0xabfa740ccd // ret (+ 0x1 0x1) let deployed_bytecode = array![ 0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x53, 0x60, 0x20, 0x60, 0x00, 0xf3 ] .span(); - let eth_address: EthAddress = 0x100_u256.into(); + let eth_address: EthAddress = 0xabfa740ccd_u256.into(); initialize_contract_account(eth_address, deployed_bytecode, Default::default().span()) .expect('set code failed'); @@ -150,7 +153,7 @@ fn test_exec_call_no_return() { kakarot_core.deploy_externally_owned_account(evm_address); // Set vm bytecode - // (call 0xffffff 0x100 0 0 0 0 1) + // (call 0xffffff 0xabfa740ccd 0 0 0 0 1) let bytecode = array![ 0x60, 0x01, @@ -162,9 +165,12 @@ fn test_exec_call_no_return() { 0x00, 0x60, 0x00, - 0x61, - 0x01, - 0x00, + 0x64, + 0xab, + 0xfa, + 0x74, + 0x0c, + 0xcd, 0x62, 0xff, 0xff, @@ -177,10 +183,10 @@ fn test_exec_call_no_return() { let mut vm = VMBuilderTrait::new_with_presets().with_bytecode(bytecode).build(); - // Deploy bytecode at 0x100 + // Deploy bytecode at 0xabfa740ccd // (+ 0x1 0x1) let deployed_bytecode = array![0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x53, 0x00].span(); - let eth_address: EthAddress = 0x100_u256.into(); + let eth_address: EthAddress = 0xabfa740ccd_u256.into(); initialize_contract_account(eth_address, deployed_bytecode, Default::default().span()) .expect('set code failed'); @@ -202,7 +208,7 @@ fn test_exec_staticcall() { kakarot_core.deploy_externally_owned_account(evm_address); // Set vm bytecode - // (call 0xffffff 0x100 0 0 0 0 1) + // (call 0xffffff 0xabfa740ccd 0 0 0 0 1) let bytecode = array![ 0x60, 0x01, @@ -212,9 +218,12 @@ fn test_exec_staticcall() { 0x00, 0x60, 0x00, - 0x61, - 0x01, - 0x00, + 0x64, + 0xab, + 0xfa, + 0x74, + 0x0c, + 0xcd, 0x62, 0xff, 0xff, @@ -226,13 +235,13 @@ fn test_exec_staticcall() { .span(); let mut vm = VMBuilderTrait::new_with_presets().with_bytecode(bytecode).build(); - // Deploy bytecode at 0x100 + // Deploy bytecode at 0xabfa740ccd // ret (+ 0x1 0x1) let deployed_bytecode = array![ 0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x53, 0x60, 0x20, 0x60, 0x00, 0xf3 ] .span(); - let eth_address: EthAddress = 0x100_u256.into(); + let eth_address: EthAddress = 0xabfa740ccd_u256.into(); initialize_contract_account(eth_address, deployed_bytecode, Default::default().span()) .expect('set code failed'); @@ -254,7 +263,7 @@ fn test_exec_staticcall_no_return() { kakarot_core.deploy_externally_owned_account(evm_address); // Set vm bytecode - // (call 0xffffff 0x100 0 0 0 0 1) + // (call 0xffffff 0xabfa740ccd 0 0 0 0 1) let bytecode = array![ 0x60, 0x01, @@ -266,9 +275,12 @@ fn test_exec_staticcall_no_return() { 0x00, 0x60, 0x00, - 0x61, - 0x01, - 0x00, + 0x64, + 0xab, + 0xfa, + 0x74, + 0x0c, + 0xcd, 0x62, 0xff, 0xff, @@ -281,10 +293,10 @@ fn test_exec_staticcall_no_return() { let mut vm = VMBuilderTrait::new_with_presets().with_bytecode(bytecode).build(); - // Deploy bytecode at 0x100 + // Deploy bytecode at 0xabfa740ccd // (+ 0x1 0x1) let deployed_bytecode = array![0x60, 0x01, 0x60, 0x01, 0x01, 0x60, 0x00, 0x53, 0x00].span(); - let eth_address: EthAddress = 0x100_u256.into(); + let eth_address: EthAddress = 0xabfa740ccd_u256.into(); initialize_contract_account(eth_address, deployed_bytecode, Default::default().span()) .expect('set code failed'); diff --git a/crates/evm/src/tests/test_precompiles.cairo b/crates/evm/src/tests/test_precompiles.cairo index d9bcc897b..2e25cb16a 100644 --- a/crates/evm/src/tests/test_precompiles.cairo +++ b/crates/evm/src/tests/test_precompiles.cairo @@ -3,4 +3,5 @@ mod test_data; mod test_ec_recover; mod test_identity; mod test_modexp; +mod test_p256verify; mod test_sha256; diff --git a/crates/evm/src/tests/test_precompiles/test_p256verify.cairo b/crates/evm/src/tests/test_precompiles/test_p256verify.cairo new file mode 100644 index 000000000..eafb659c3 --- /dev/null +++ b/crates/evm/src/tests/test_precompiles/test_p256verify.cairo @@ -0,0 +1,121 @@ +use contracts::tests::test_utils::setup_contracts_for_testing; +use core::array::ArrayTrait; +use evm::instructions::system_operations::SystemOperationsTrait; +use evm::memory::InternalMemoryTrait; +use evm::memory::MemoryTrait; + +use evm::precompiles::p256verify::P256Verify; +use evm::stack::StackTrait; +use evm::tests::test_utils::{VMBuilderTrait}; +use utils::helpers::{U256Trait, ToBytes, FromBytes}; + + +// source: +#[test] +fn test_p256verify_precompile() { + let msg_hash = 0x4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4d_u256 + .to_be_bytes_padded(); + let r = 0xa73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac_u256 + .to_be_bytes_padded(); + let s = 0x36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d60_u256 + .to_be_bytes_padded(); + let x = 0x4aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff3_u256 + .to_be_bytes_padded(); + let y = 0x7618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e_u256 + .to_be_bytes_padded(); + + let mut calldata = array![]; + calldata.append_span(msg_hash); + calldata.append_span(r); + calldata.append_span(s); + calldata.append_span(x); + calldata.append_span(y); + + let (gas, result) = P256Verify::exec(calldata.span()).unwrap(); + + let result: u256 = result.from_be_bytes().unwrap(); + assert_eq!(result, 0x01); + assert_eq!(gas, 3450); +} + +// source: +#[test] +fn test_p256verify_precompile_static_call() { + let (_, _) = setup_contracts_for_testing(); + + let mut vm = VMBuilderTrait::new_with_presets().build(); + + vm + .memory + .store(0x4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4d, 0x0); // msg_hash + vm.memory.store(0xa73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac, 0x20); // r + vm.memory.store(0x36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d60, 0x40); // s + vm.memory.store(0x4aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff3, 0x60); // x + vm.memory.store(0x7618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e, 0x80); // y + + vm.stack.push(0x01).unwrap(); // retSize + vm.stack.push(0xa0).unwrap(); // retOffset + vm.stack.push(0xa0).unwrap(); // argsSize + vm.stack.push(0x0).unwrap(); // argsOffset + vm.stack.push(0x100).unwrap(); // address + vm.stack.push(0xFFFFFFFF).unwrap(); // gas + + vm.exec_staticcall().unwrap(); + + let mut result = Default::default(); + vm.memory.load_n(0x1, ref result, 0xa0); + + assert_eq!(result, array![0x01]); +} + +#[test] +fn test_p256verify_precompile_input_too_short() { + let msg_hash = 0x4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4d_u256 + .to_be_bytes_padded(); + let r = 0xa73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac_u256 + .to_be_bytes_padded(); + let s = 0x36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d60_u256 + .to_be_bytes_padded(); + let x = 0x4aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff3_u256 + .to_be_bytes_padded(); + + let mut calldata = array![]; + calldata.append_span(msg_hash); + calldata.append_span(r); + calldata.append_span(s); + calldata.append_span(x); + + let (gas, result) = P256Verify::exec(calldata.span()).unwrap(); + + let result: u256 = result.from_be_bytes().unwrap(); + assert_eq!(result, 0x00); + assert_eq!(gas, 3450); +} + +#[test] +fn test_p256verify_precompile_input_too_short_static_call() { + let (_, _) = setup_contracts_for_testing(); + + let mut vm = VMBuilderTrait::new_with_presets().build(); + + vm + .memory + .store(0x4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4d, 0x0); // msg_hash + vm.memory.store(0xa73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac, 0x20); // r + vm.memory.store(0x36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d60, 0x40); // s + vm.memory.store(0x4aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff3, 0x60); // x + + vm.stack.push(0x01).unwrap(); // retSize + vm.stack.push(0x80).unwrap(); // retOffset + vm.stack.push(0x80).unwrap(); // argsSize + vm.stack.push(0x0).unwrap(); // argsOffset + vm.stack.push(0x100).unwrap(); // address + vm.stack.push(0xFFFFFFFF).unwrap(); // gas + + vm.exec_staticcall().unwrap(); + + let mut result = Default::default(); + vm.memory.load_n(0x1, ref result, 0x80); + + assert_eq!(result, array![0x00]); +}