@@ -7,13 +7,12 @@ use bls12_381::{
77} ;
88use bytes:: { Buf , Bytes } ;
99use ethrex_common:: H160 ;
10- use ethrex_common:: utils:: { keccak , u256_from_big_endian_const} ;
10+ use ethrex_common:: utils:: u256_from_big_endian_const;
1111use ethrex_common:: {
1212 Address , H256 , U256 , serde_utils:: bool, types:: Fork , types:: Fork :: * ,
1313 utils:: u256_from_big_endian,
1414} ;
1515use ethrex_crypto:: { blake2f:: blake2b_f, kzg:: verify_kzg_proof} ;
16- use k256:: ecdsa:: { RecoveryId , Signature , VerifyingKey } ;
1716use k256:: elliptic_curve:: Field ;
1817use lambdaworks_math:: cyclic_group:: IsGroup ;
1918use lambdaworks_math:: elliptic_curve:: short_weierstrass:: curves:: bn_254:: curve:: {
@@ -53,8 +52,8 @@ use crate::{
5352 gas_cost:: {
5453 self , BLAKE2F_ROUND_COST , BLS12_381_G1_K_DISCOUNT , BLS12_381_G1ADD_COST ,
5554 BLS12_381_G2_K_DISCOUNT , BLS12_381_G2ADD_COST , BLS12_381_MAP_FP_TO_G1_COST ,
56- BLS12_381_MAP_FP2_TO_G2_COST , ECADD_COST , ECMUL_COST , ECRECOVER_COST , G1_MUL_COST ,
57- G2_MUL_COST , POINT_EVALUATION_COST ,
55+ BLS12_381_MAP_FP2_TO_G2_COST , ECADD_COST , ECMUL_COST , G1_MUL_COST , G2_MUL_COST ,
56+ POINT_EVALUATION_COST ,
5857 } ,
5958} ;
6059use lambdaworks_math:: elliptic_curve:: short_weierstrass:: curves:: bls12_381:: curve:: {
@@ -375,6 +374,65 @@ pub(crate) fn fill_with_zeros(calldata: &Bytes, target_len: usize) -> Bytes {
375374 padded_calldata. into ( )
376375}
377376
377+ #[ cfg( all( not( feature = "sp1" ) , not( feature = "risc0" ) ) ) ]
378+ pub fn ecrecover ( calldata : & Bytes , gas_remaining : & mut u64 , _fork : Fork ) -> Result < Bytes , VMError > {
379+ use sha3:: Keccak256 ;
380+
381+ use crate :: gas_cost:: ECRECOVER_COST ;
382+
383+ increase_precompile_consumed_gas ( ECRECOVER_COST , gas_remaining) ?;
384+
385+ const INPUT_LEN : usize = 128 ;
386+ const WORD : usize = 32 ;
387+
388+ let input = fill_with_zeros ( calldata, INPUT_LEN ) ;
389+
390+ // len(raw_hash) == 32, len(raw_v) == 32, len(raw_sig) == 64
391+ let ( raw_hash, tail) = input. split_at ( WORD ) ;
392+ let ( raw_v, raw_sig) = tail. split_at ( WORD ) ;
393+
394+ // EVM expects v ∈ {27, 28}. Anything else is invalid → empty return.
395+ let recovery_id_byte = match u8:: try_from ( u256_from_big_endian ( raw_v) ) {
396+ Ok ( 27 ) => 0_i32 ,
397+ Ok ( 28 ) => 1_i32 ,
398+ _ => return Ok ( Bytes :: new ( ) ) ,
399+ } ;
400+
401+ // Recovery id from the adjusted byte.
402+ let Ok ( recovery_id) = secp256k1:: ecdsa:: RecoveryId :: try_from ( recovery_id_byte) else {
403+ return Ok ( Bytes :: new ( ) ) ;
404+ } ;
405+
406+ let Ok ( recoverable_signature) =
407+ secp256k1:: ecdsa:: RecoverableSignature :: from_compact ( raw_sig, recovery_id)
408+ else {
409+ return Ok ( Bytes :: new ( ) ) ;
410+ } ;
411+
412+ let message = secp256k1:: Message :: from_digest (
413+ raw_hash
414+ . try_into ( )
415+ . map_err ( |_err| InternalError :: msg ( "Invalid message length for ecrecover" ) ) ?,
416+ ) ;
417+
418+ let Ok ( public_key) = recoverable_signature. recover ( & message) else {
419+ return Ok ( Bytes :: new ( ) ) ;
420+ } ;
421+
422+ // We need to take the 64 bytes from the public key (discarding the first pos of the slice)
423+ let public_key_hash = Keccak256 :: digest ( & public_key. serialize_uncompressed ( ) [ 1 ..] ) ;
424+
425+ // Address is the last 20 bytes of the hash.
426+ #[ expect( clippy:: indexing_slicing) ]
427+ let recovered_address_bytes = & public_key_hash[ 12 ..] ;
428+
429+ let mut out = [ 0u8 ; 32 ] ;
430+
431+ out[ 12 ..32 ] . copy_from_slice ( recovered_address_bytes) ;
432+
433+ Ok ( Bytes :: copy_from_slice ( & out) )
434+ }
435+
378436/// ## ECRECOVER precompile.
379437/// Elliptic curve digital signature algorithm (ECDSA) public key recovery function.
380438///
@@ -384,7 +442,13 @@ pub(crate) fn fill_with_zeros(calldata: &Bytes, target_len: usize) -> Bytes {
384442/// [64..128): r||s (64 bytes)
385443///
386444/// Returns the recovered address.
445+ #[ cfg( any( feature = "sp1" , feature = "risc0" ) ) ]
387446pub fn ecrecover ( calldata : & Bytes , gas_remaining : & mut u64 , _fork : Fork ) -> Result < Bytes , VMError > {
447+ use ethrex_common:: utils:: keccak;
448+ use k256:: ecdsa:: { RecoveryId , Signature , VerifyingKey } ;
449+
450+ use crate :: gas_cost:: ECRECOVER_COST ;
451+
388452 increase_precompile_consumed_gas ( ECRECOVER_COST , gas_remaining) ?;
389453
390454 const INPUT_LEN : usize = 128 ;
0 commit comments