fc_rpc/
signer.rs

1// This file is part of Frontier.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use ethereum::{eip2930, legacy, TransactionV3 as EthereumTransaction};
20use ethereum_types::{H160, H256};
21use jsonrpsee::types::ErrorObjectOwned;
22// Substrate
23use sp_core::hashing::keccak_256;
24// Frontier
25use fc_rpc_core::types::TransactionMessage;
26
27use crate::internal_err;
28
29/// A generic Ethereum signer.
30pub trait EthSigner: Send + Sync {
31	/// Available accounts from this signer.
32	fn accounts(&self) -> Vec<H160>;
33	/// Sign a transaction message using the given account in message.
34	fn sign(
35		&self,
36		message: TransactionMessage,
37		address: &H160,
38	) -> Result<EthereumTransaction, ErrorObjectOwned>;
39}
40
41pub struct EthDevSigner {
42	keys: Vec<libsecp256k1::SecretKey>,
43}
44
45impl EthDevSigner {
46	pub fn new() -> Self {
47		Self {
48			keys: vec![libsecp256k1::SecretKey::parse(&[
49				0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
50				0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
51				0x11, 0x11, 0x11, 0x11,
52			])
53			.expect("Test key is valid; qed")],
54		}
55	}
56}
57
58fn secret_key_address(secret: &libsecp256k1::SecretKey) -> H160 {
59	let public = libsecp256k1::PublicKey::from_secret_key(secret);
60	public_key_address(&public)
61}
62
63fn public_key_address(public: &libsecp256k1::PublicKey) -> H160 {
64	let mut res = [0u8; 64];
65	res.copy_from_slice(&public.serialize()[1..65]);
66	H160::from(H256::from(keccak_256(&res)))
67}
68
69impl EthSigner for EthDevSigner {
70	fn accounts(&self) -> Vec<H160> {
71		self.keys.iter().map(secret_key_address).collect()
72	}
73
74	fn sign(
75		&self,
76		message: TransactionMessage,
77		address: &H160,
78	) -> Result<EthereumTransaction, ErrorObjectOwned> {
79		let mut transaction = None;
80
81		for secret in &self.keys {
82			let key_address = secret_key_address(secret);
83
84			if &key_address == address {
85				match message {
86					TransactionMessage::Legacy(m) => {
87						let signing_message = libsecp256k1::Message::parse_slice(&m.hash()[..])
88							.map_err(|_| internal_err("invalid signing message"))?;
89						let (signature, recid) = libsecp256k1::sign(&signing_message, secret);
90						let v = match m.chain_id {
91							None => 27 + recid.serialize() as u64,
92							Some(chain_id) => 2 * chain_id + 35 + recid.serialize() as u64,
93						};
94						let rs = signature.serialize();
95						let r = H256::from_slice(&rs[0..32]);
96						let s = H256::from_slice(&rs[32..64]);
97						transaction =
98							Some(EthereumTransaction::Legacy(ethereum::LegacyTransaction {
99								nonce: m.nonce,
100								gas_price: m.gas_price,
101								gas_limit: m.gas_limit,
102								action: m.action,
103								value: m.value,
104								input: m.input,
105								signature: legacy::TransactionSignature::new(v, r, s).ok_or_else(
106									|| internal_err("signer generated invalid signature"),
107								)?,
108							}));
109					}
110					TransactionMessage::EIP2930(m) => {
111						let signing_message = libsecp256k1::Message::parse_slice(&m.hash()[..])
112							.map_err(|_| internal_err("invalid signing message"))?;
113						let (signature, recid) = libsecp256k1::sign(&signing_message, secret);
114						let rs = signature.serialize();
115						let r = H256::from_slice(&rs[0..32]);
116						let s = H256::from_slice(&rs[32..64]);
117						transaction =
118							Some(EthereumTransaction::EIP2930(ethereum::EIP2930Transaction {
119								chain_id: m.chain_id,
120								nonce: m.nonce,
121								gas_price: m.gas_price,
122								gas_limit: m.gas_limit,
123								action: m.action,
124								value: m.value,
125								input: m.input.clone(),
126								access_list: m.access_list,
127								signature: eip2930::TransactionSignature::new(
128									recid.serialize() != 0,
129									r,
130									s,
131								)
132								.ok_or(internal_err("Invalid transaction signature format"))?,
133							}));
134					}
135					TransactionMessage::EIP1559(m) => {
136						let signing_message = libsecp256k1::Message::parse_slice(&m.hash()[..])
137							.map_err(|_| internal_err("invalid signing message"))?;
138						let (signature, recid) = libsecp256k1::sign(&signing_message, secret);
139						let rs = signature.serialize();
140						let r = H256::from_slice(&rs[0..32]);
141						let s = H256::from_slice(&rs[32..64]);
142						transaction =
143							Some(EthereumTransaction::EIP1559(ethereum::EIP1559Transaction {
144								chain_id: m.chain_id,
145								nonce: m.nonce,
146								max_priority_fee_per_gas: m.max_priority_fee_per_gas,
147								max_fee_per_gas: m.max_fee_per_gas,
148								gas_limit: m.gas_limit,
149								action: m.action,
150								value: m.value,
151								input: m.input.clone(),
152								access_list: m.access_list,
153								signature: eip2930::TransactionSignature::new(
154									recid.serialize() != 0,
155									r,
156									s,
157								)
158								.ok_or(internal_err("Invalid transaction signature format"))?,
159							}));
160					}
161					TransactionMessage::EIP7702(m) => {
162						let signing_message = libsecp256k1::Message::parse_slice(&m.hash()[..])
163							.map_err(|_| internal_err("invalid signing message"))?;
164						let (signature, recid) = libsecp256k1::sign(&signing_message, secret);
165						let rs = signature.serialize();
166						let r = H256::from_slice(&rs[0..32]);
167						let s = H256::from_slice(&rs[32..64]);
168						transaction =
169							Some(EthereumTransaction::EIP7702(ethereum::EIP7702Transaction {
170								chain_id: m.chain_id,
171								nonce: m.nonce,
172								max_priority_fee_per_gas: m.max_priority_fee_per_gas,
173								max_fee_per_gas: m.max_fee_per_gas,
174								gas_limit: m.gas_limit,
175								destination: m.destination,
176								value: m.value,
177								data: m.data.clone(),
178								access_list: m.access_list,
179								authorization_list: m.authorization_list,
180								signature: eip2930::TransactionSignature::new(
181									recid.serialize() != 0,
182									r,
183									s,
184								)
185								.ok_or(internal_err("Invalid transaction signature format"))?,
186							}));
187					}
188				}
189				break;
190			}
191		}
192
193		transaction.ok_or_else(|| internal_err("signer not available"))
194	}
195}