Skip to content
This repository has been archived by the owner on Jan 8, 2025. It is now read-only.

Feat: add RIP-7212 to the list of precompiles Cairo1Helpers module #779

Merged
merged 7 commits into from
Apr 19, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/evm/src/call_helpers.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -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() < 257_u256);
}
84 changes: 47 additions & 37 deletions crates/evm/src/precompiles.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ mod blake2f;
mod ec_recover;
mod identity;
mod modexp;
mod p256verify;
mod sha256;

use core::traits::Into;
Expand All @@ -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;

Expand All @@ -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)
}
}
};

Expand Down
67 changes: 67 additions & 0 deletions crates/evm/src/precompiles/p256verify.cairo
Original file line number Diff line number Diff line change
@@ -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<u8>) -> Result<(u128, Span<u8>), 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<u256> = 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<u256> = 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<u256> = 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<u256> = 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<Secp256r1Point> = 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()));
}
}
1 change: 1 addition & 0 deletions crates/evm/src/tests/test_precompiles.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ mod test_data;
mod test_ec_recover;
mod test_identity;
mod test_modexp;
mod test_p256verify;
mod test_sha256;
121 changes: 121 additions & 0 deletions crates/evm/src/tests/test_precompiles/test_p256verify.cairo
Original file line number Diff line number Diff line change
@@ -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: <https://github.com/ethereum/go-ethereum/pull/27540/files#diff-3548292e7ee4a75fc8146397c6baf5c969f6fe6cd9355df322cdb4f11103e004>
#[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: <https://github.com/ethereum/go-ethereum/pull/27540/files#diff-3548292e7ee4a75fc8146397c6baf5c969f6fe6cd9355df322cdb4f11103e004>
#[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]);
}
Loading