referrerpolicy=no-referrer-when-downgrade

pallet_revive/pure_precompiles/
ecrecover.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use super::Precompile;
use crate::{Config, ExecReturnValue, GasMeter, RuntimeCosts};
use pallet_revive_uapi::ReturnFlags;

/// The ecrecover precompile.
pub struct ECRecover;

impl<T: Config> Precompile<T> for ECRecover {
	fn execute(gas_meter: &mut GasMeter<T>, i: &[u8]) -> Result<ExecReturnValue, &'static str> {
		gas_meter.charge(RuntimeCosts::EcdsaRecovery)?;

		let mut input = [0u8; 128];
		let len = i.len().min(128);
		input[..len].copy_from_slice(&i[..len]);

		let mut msg = [0u8; 32];
		let mut sig = [0u8; 65];

		msg[0..32].copy_from_slice(&input[0..32]);
		sig[0..32].copy_from_slice(&input[64..96]); // r
		sig[32..64].copy_from_slice(&input[96..128]); // s
		sig[64] = input[63]; // v

		// v can only be 27 or 28 on the full 32 bytes value.
		// https://github.com/ethereum/go-ethereum/blob/a907d7e81aaeea15d80b2d3209ad8e08e3bf49e0/core/vm/contracts.go#L177
		if input[32..63] != [0u8; 31] || ![27, 28].contains(&input[63]) {
			return Ok(ExecReturnValue { data: [0u8; 0].to_vec(), flags: ReturnFlags::empty() });
		}

		let data = match sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg) {
			Ok(pubkey) => {
				let mut address = sp_io::hashing::keccak_256(&pubkey);
				address[0..12].copy_from_slice(&[0u8; 12]);
				address.to_vec()
			},
			Err(_) => [0u8; 0].to_vec(),
		};

		Ok(ExecReturnValue { data, flags: ReturnFlags::empty() })
	}
}

#[cfg(test)]
mod tests {
	use super::*;
	use crate::pure_precompiles::test::test_precompile_test_vectors;

	#[test]
	fn test_ecrecover() -> Result<(), String> {
		test_precompile_test_vectors::<ECRecover>(include_str!("./testdata/1-ecRecover.json"))?;
		Ok(())
	}
}