revm_precompile/
secp256k1.rs

1use crate::{utilities::right_pad, Error, Precompile, PrecompileResult, PrecompileWithAddress};
2use revm_primitives::{alloy_primitives::B512, Bytes, PrecompileOutput, B256};
3
4pub const ECRECOVER: PrecompileWithAddress = PrecompileWithAddress(
5    crate::u64_to_address(1),
6    Precompile::Standard(ec_recover_run),
7);
8
9pub use self::secp256k1::ecrecover;
10
11#[cfg(not(feature = "secp256k1"))]
12#[allow(clippy::module_inception)]
13mod secp256k1 {
14    use k256::ecdsa::{Error, RecoveryId, Signature, VerifyingKey};
15    use revm_primitives::{alloy_primitives::B512, keccak256, B256};
16
17    pub fn ecrecover(sig: &B512, mut recid: u8, msg: &B256) -> Result<B256, Error> {
18        // parse signature
19        let mut sig = Signature::from_slice(sig.as_slice())?;
20
21        // normalize signature and flip recovery id if needed.
22        if let Some(sig_normalized) = sig.normalize_s() {
23            sig = sig_normalized;
24            recid ^= 1;
25        }
26        let recid = RecoveryId::from_byte(recid).expect("recovery ID is valid");
27
28        // recover key
29        let recovered_key = VerifyingKey::recover_from_prehash(&msg[..], &sig, recid)?;
30        // hash it
31        let mut hash = keccak256(
32            &recovered_key
33                .to_encoded_point(/* compress = */ false)
34                .as_bytes()[1..],
35        );
36
37        // truncate to 20 bytes
38        hash[..12].fill(0);
39        Ok(hash)
40    }
41}
42
43#[cfg(feature = "secp256k1")]
44#[allow(clippy::module_inception)]
45mod secp256k1 {
46    use revm_primitives::{alloy_primitives::B512, keccak256, B256};
47    use secp256k1::{
48        ecdsa::{RecoverableSignature, RecoveryId},
49        Message, SECP256K1,
50    };
51
52    // Silence the unused crate dependency warning.
53    use k256 as _;
54
55    pub fn ecrecover(sig: &B512, recid: u8, msg: &B256) -> Result<B256, secp256k1::Error> {
56        let recid = RecoveryId::from_i32(recid as i32).expect("recovery ID is valid");
57        let sig = RecoverableSignature::from_compact(sig.as_slice(), recid)?;
58
59        let msg = Message::from_digest(msg.0);
60        let public = SECP256K1.recover_ecdsa(&msg, &sig)?;
61
62        let mut hash = keccak256(&public.serialize_uncompressed()[1..]);
63        hash[..12].fill(0);
64        Ok(hash)
65    }
66}
67
68pub fn ec_recover_run(input: &Bytes, gas_limit: u64) -> PrecompileResult {
69    const ECRECOVER_BASE: u64 = 3_000;
70
71    if ECRECOVER_BASE > gas_limit {
72        return Err(Error::OutOfGas.into());
73    }
74
75    let input = right_pad::<128>(input);
76
77    // `v` must be a 32-byte big-endian integer equal to 27 or 28.
78    if !(input[32..63].iter().all(|&b| b == 0) && matches!(input[63], 27 | 28)) {
79        return Ok(PrecompileOutput::new(ECRECOVER_BASE, Bytes::new()));
80    }
81
82    let msg = <&B256>::try_from(&input[0..32]).unwrap();
83    let recid = input[63] - 27;
84    let sig = <&B512>::try_from(&input[64..128]).unwrap();
85
86    let out = secp256k1::ecrecover(sig, recid, msg)
87        .map(|o| o.to_vec().into())
88        .unwrap_or_default();
89    Ok(PrecompileOutput::new(ECRECOVER_BASE, out))
90}