aurora_engine_precompiles/
secp256k1.rs

1use crate::prelude::types::{make_address, Address, EthGas};
2use crate::prelude::{sdk, vec::Vec, Borrowed, H256};
3use crate::{EvmPrecompileResult, Precompile, PrecompileOutput};
4use aurora_evm::{Context, ExitError};
5
6mod costs {
7    use crate::prelude::types::EthGas;
8
9    pub(super) const ECRECOVER_BASE: EthGas = EthGas::new(3_000);
10}
11
12mod consts {
13    pub(super) const INPUT_LEN: usize = 128;
14    pub(super) const SIGNATURE_LENGTH: usize = 65;
15}
16
17/// See: `https://ethereum.github.io/yellowpaper/paper.pdf`
18/// See: `https://docs.soliditylang.org/en/develop/units-and-global-variables.html#mathematical-and-cryptographic-functions`
19/// See: `https://etherscan.io/address/0000000000000000000000000000000000000001`
20// Quite a few library methods rely on this and that should be changed. This
21// should only be for precompiles.
22pub fn ecrecover(
23    hash: H256,
24    signature: &[u8; consts::SIGNATURE_LENGTH],
25) -> Result<Address, ExitError> {
26    sdk::ecrecover(hash, signature).map_err(|e| ExitError::Other(Borrowed(e.as_str())))
27}
28
29pub struct ECRecover;
30
31impl ECRecover {
32    pub const ADDRESS: Address = make_address(0, 1);
33}
34
35impl Precompile for ECRecover {
36    fn required_gas(_input: &[u8]) -> Result<EthGas, ExitError> {
37        Ok(costs::ECRECOVER_BASE)
38    }
39
40    fn run(
41        &self,
42        input: &[u8],
43        target_gas: Option<EthGas>,
44        _context: &Context,
45        _is_static: bool,
46    ) -> EvmPrecompileResult {
47        let cost = Self::required_gas(input)?;
48        if let Some(target_gas) = target_gas {
49            if cost > target_gas {
50                return Err(ExitError::OutOfGas);
51            }
52        }
53
54        let mut input = input.to_vec();
55        input.resize(consts::INPUT_LEN, 0);
56
57        let mut hash = [0; 32];
58        hash.copy_from_slice(&input[0..32]);
59
60        let mut v = [0; 32];
61        v.copy_from_slice(&input[32..64]);
62
63        let mut signature = [0; consts::SIGNATURE_LENGTH]; // signature is (r, s, v), typed (uint256, uint256, uint8)
64        signature[0..32].copy_from_slice(&input[64..96]); // r
65        signature[32..64].copy_from_slice(&input[96..128]); // s
66
67        let v_bit = match v[31] {
68            27 | 28 if v[..31] == [0; 31] => v[31] - 27,
69            _ => {
70                return Ok(PrecompileOutput::without_logs(cost, Vec::new()));
71            }
72        };
73        signature[64] = v_bit; // v
74
75        let address_res = ecrecover(H256::from_slice(&hash), &signature);
76        let output = address_res
77            .map(|a| {
78                let mut output = [0u8; 32];
79                output[12..32].copy_from_slice(a.as_bytes());
80                output.to_vec()
81            })
82            .unwrap_or_default();
83
84        Ok(PrecompileOutput::without_logs(cost, output))
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use crate::utils::new_context;
92
93    #[test]
94    fn test_ecrecover() {
95        let input = hex::decode("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
96        let expected =
97            hex::decode("000000000000000000000000c08b5542d177ac6686946920409741463a15dddb")
98                .unwrap();
99
100        let res = ECRecover
101            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
102            .unwrap()
103            .output;
104        assert_eq!(res, expected);
105
106        // out of gas
107        let input = hex::decode("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
108
109        let res = ECRecover.run(&input, Some(EthGas::new(2_999)), &new_context(), false);
110        assert!(matches!(res, Err(ExitError::OutOfGas)));
111
112        // bad inputs
113        let input = hex::decode("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001a650acf9d3f5f0a2c799776a1254355d5f4061762a237396a99a0e0e3fc2bcd6729514a0dacb2e623ac4abd157cb18163ff942280db4d5caad66ddf941ba12e03").unwrap();
114        let expected: Vec<u8> = Vec::new();
115
116        let res = ECRecover
117            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
118            .unwrap()
119            .output;
120        assert_eq!(res, expected);
121
122        let input = hex::decode("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000").unwrap();
123        let expected: Vec<u8> = Vec::new();
124
125        let res = ECRecover
126            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
127            .unwrap()
128            .output;
129        assert_eq!(res, expected);
130
131        let input = hex::decode("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b").unwrap();
132        let expected: Vec<u8> = Vec::new();
133
134        let res = ECRecover
135            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
136            .unwrap()
137            .output;
138        assert_eq!(res, expected);
139
140        let input = hex::decode("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000000000000000000000000000000000000000001b").unwrap();
141        let expected: Vec<u8> = Vec::new();
142
143        let res = ECRecover
144            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
145            .unwrap()
146            .output;
147        assert_eq!(res, expected);
148
149        let input = hex::decode("47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad000000000000000000000000000000000000000000000000000000000000001b000000000000000000000000000000000000000000000000000000000000001bffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").unwrap();
150        let expected: Vec<u8> = Vec::new();
151
152        let res = ECRecover
153            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
154            .unwrap()
155            .output;
156        assert_eq!(res, expected);
157    }
158
159    #[test]
160    fn test_ecrecover_geth_tests() {
161        let input = hex::decode("a8b53bdf3306a35a7103ab5504a0c9b492295564b6202b1942a84ef300107281000000000000000000000000000000000000000000000000000000000000001b307835653165303366353363653138623737326363623030393366663731663366353366356337356237346463623331613835616138623838393262346538621122334455667788991011121314151617181920212223242526272829303132").unwrap();
162        let expected: Vec<u8> = Vec::new();
163        let res = ECRecover
164            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
165            .unwrap()
166            .output;
167        assert_eq!(res, expected);
168
169        let input = hex::decode("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").unwrap();
170        let expected =
171            hex::decode("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")
172                .unwrap();
173        let res = ECRecover
174            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
175            .unwrap()
176            .output;
177        assert_eq!(res, expected);
178
179        let input = hex::decode("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c100000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").unwrap();
180        let expected: Vec<u8> = Vec::new();
181        let res = ECRecover
182            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
183            .unwrap()
184            .output;
185        assert_eq!(res, expected);
186
187        let input = hex::decode("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000001000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").unwrap();
188        let expected: Vec<u8> = Vec::new();
189        let res = ECRecover
190            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
191            .unwrap()
192            .output;
193        assert_eq!(res, expected);
194
195        let input = hex::decode("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000001000000000000000000000011c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").unwrap();
196        let expected: Vec<u8> = Vec::new();
197        let res = ECRecover
198            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
199            .unwrap()
200            .output;
201        assert_eq!(res, expected);
202    }
203
204    #[test]
205    fn test_extra_input_length() {
206        let input = hex::decode("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549aabbccddeeff").unwrap();
207        let expected =
208            hex::decode("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b")
209                .unwrap();
210        let res = ECRecover
211            .run(&input, Some(EthGas::new(3_000)), &new_context(), false)
212            .unwrap()
213            .output;
214        assert_eq!(res, expected);
215    }
216}