aurora_engine_precompiles/
secp256k1.rs

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