fc_rpc/eth/
submit.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_types::H256;
20use futures::future::TryFutureExt;
21use jsonrpsee::core::RpcResult;
22// Substrate
23use sc_client_api::backend::{Backend, StorageProvider};
24use sc_transaction_pool_api::{InPoolTransaction, TransactionPool};
25use sp_api::{ApiExt, ProvideRuntimeApi};
26use sp_block_builder::BlockBuilder as BlockBuilderApi;
27use sp_blockchain::HeaderBackend;
28use sp_core::H160;
29use sp_inherents::CreateInherentDataProviders;
30use sp_runtime::{traits::Block as BlockT, transaction_validity::TransactionSource};
31// Frontier
32use fc_rpc_core::types::*;
33use fp_rpc::{ConvertTransaction, ConvertTransactionRuntimeApi, EthereumRuntimeRPCApi};
34
35use crate::{
36	eth::{format, Eth},
37	internal_err, public_key,
38};
39
40impl<B, C, P, CT, BE, CIDP, EC> Eth<B, C, P, CT, BE, CIDP, EC>
41where
42	B: BlockT,
43	C: ProvideRuntimeApi<B>,
44	C::Api: BlockBuilderApi<B> + ConvertTransactionRuntimeApi<B> + EthereumRuntimeRPCApi<B>,
45	C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
46	BE: Backend<B> + 'static,
47	P: TransactionPool<Block = B, Hash = B::Hash> + 'static,
48	CT: ConvertTransaction<<B as BlockT>::Extrinsic> + 'static,
49	CIDP: CreateInherentDataProviders<B, ()> + Send + 'static,
50{
51	pub async fn send_transaction(&self, request: TransactionRequest) -> RpcResult<H256> {
52		let from = match request.from {
53			Some(from) => from,
54			None => {
55				let accounts = match self.accounts() {
56					Ok(accounts) => accounts,
57					Err(e) => return Err(e),
58				};
59
60				match accounts.first() {
61					Some(account) => *account,
62					None => return Err(internal_err("no signer available")),
63				}
64			}
65		};
66
67		let nonce = match request.nonce {
68			Some(nonce) => nonce,
69			None => match self.transaction_count(from, None).await {
70				Ok(nonce) => nonce,
71				Err(e) => return Err(e),
72			},
73		};
74
75		let chain_id = match (request.chain_id, self.chain_id()) {
76			(Some(id), Ok(Some(chain_id))) if id != chain_id => {
77				return Err(internal_err("chain id is mismatch"))
78			}
79			(_, Ok(Some(chain_id))) => chain_id.as_u64(),
80			(_, Ok(None)) => return Err(internal_err("chain id not available")),
81			(_, Err(e)) => return Err(e),
82		};
83
84		let block_hash = self.client.info().best_hash;
85
86		let gas_price = request.gas_price;
87		let gas_limit = match request.gas {
88			Some(gas_limit) => gas_limit,
89			None => {
90				if let Ok(Some(block)) = self.client.runtime_api().current_block(block_hash) {
91					block.header.gas_limit
92				} else {
93					return Err(internal_err("block unavailable, cannot query gas limit"));
94				}
95			}
96		};
97
98		let max_fee_per_gas = request.max_fee_per_gas;
99		let message: Option<TransactionMessage> = request.into();
100		let message = match message {
101			Some(TransactionMessage::Legacy(mut m)) => {
102				m.nonce = nonce;
103				m.chain_id = Some(chain_id);
104				m.gas_limit = gas_limit;
105				if gas_price.is_none() {
106					m.gas_price = self.gas_price().unwrap_or_default();
107				}
108				TransactionMessage::Legacy(m)
109			}
110			Some(TransactionMessage::EIP2930(mut m)) => {
111				m.nonce = nonce;
112				m.chain_id = chain_id;
113				m.gas_limit = gas_limit;
114				if gas_price.is_none() {
115					m.gas_price = self.gas_price().unwrap_or_default();
116				}
117				TransactionMessage::EIP2930(m)
118			}
119			Some(TransactionMessage::EIP1559(mut m)) => {
120				m.nonce = nonce;
121				m.chain_id = chain_id;
122				m.gas_limit = gas_limit;
123				if max_fee_per_gas.is_none() {
124					m.max_fee_per_gas = self.gas_price().unwrap_or_default();
125				}
126				TransactionMessage::EIP1559(m)
127			}
128			_ => return Err(internal_err("invalid transaction parameters")),
129		};
130
131		let mut transaction = None;
132		for signer in &self.signers {
133			if signer.accounts().contains(&from) {
134				match signer.sign(message, &from) {
135					Ok(t) => transaction = Some(t),
136					Err(e) => return Err(e),
137				}
138				break;
139			}
140		}
141
142		let transaction = match transaction {
143			Some(transaction) => transaction,
144			None => return Err(internal_err("no signer available")),
145		};
146		let transaction_hash = transaction.hash();
147
148		let extrinsic = self.convert_transaction(block_hash, transaction)?;
149
150		self.pool
151			.submit_one(block_hash, TransactionSource::Local, extrinsic)
152			.map_ok(move |_| transaction_hash)
153			.map_err(|err| internal_err(format::Geth::pool_error(err)))
154			.await
155	}
156
157	pub async fn send_raw_transaction(&self, bytes: Bytes) -> RpcResult<H256> {
158		let bytes = bytes.into_vec();
159		if bytes.is_empty() {
160			return Err(internal_err("transaction data is empty"));
161		}
162
163		let transaction: ethereum::TransactionV3 =
164			match ethereum::EnvelopedDecodable::decode(&bytes) {
165				Ok(transaction) => transaction,
166				Err(_) => return Err(internal_err("decode transaction failed")),
167			};
168		let transaction_hash = transaction.hash();
169
170		let block_hash = self.client.info().best_hash;
171		let extrinsic = self.convert_transaction(block_hash, transaction)?;
172
173		self.pool
174			.submit_one(block_hash, TransactionSource::Local, extrinsic)
175			.map_ok(move |_| transaction_hash)
176			.map_err(|err| internal_err(format::Geth::pool_error(err)))
177			.await
178	}
179
180	pub async fn pending_transactions(&self) -> RpcResult<Vec<Transaction>> {
181		let ready = self
182			.graph
183			.ready()
184			.map(|in_pool_tx| in_pool_tx.data().as_ref().clone())
185			.collect::<Vec<_>>();
186
187		let future = self
188			.graph
189			.futures()
190			.iter()
191			.map(|in_pool_tx| in_pool_tx.data().as_ref().clone())
192			.collect::<Vec<_>>();
193
194		let all_extrinsics = ready
195			.iter()
196			.chain(future.iter())
197			.cloned()
198			.collect::<Vec<_>>();
199
200		let best_block = self.client.info().best_hash;
201		let api = self.client.runtime_api();
202
203		let api_version = api
204			.api_version::<dyn EthereumRuntimeRPCApi<B>>(best_block)
205			.map_err(|err| internal_err(format!("Failed to get API version: {err}")))?
206			.ok_or_else(|| internal_err("Failed to get API version"))?;
207
208		let ethereum_txs = if api_version > 1 {
209			api.extrinsic_filter(best_block, all_extrinsics)
210				.map_err(|err| internal_err(format!("Runtime call failed: {err}")))?
211		} else {
212			#[allow(deprecated)]
213			let legacy = api
214				.extrinsic_filter_before_version_2(best_block, all_extrinsics)
215				.map_err(|err| internal_err(format!("Runtime call failed: {err}")))?;
216			legacy.into_iter().map(|tx| tx.into()).collect()
217		};
218
219		let transactions = ethereum_txs
220			.into_iter()
221			.filter_map(|tx| {
222				let pubkey = match public_key(&tx) {
223					Ok(pk) => H160::from(H256::from(sp_core::hashing::keccak_256(&pk))),
224					Err(_err) => {
225						// Skip transactions with invalid public keys
226						return None;
227					}
228				};
229
230				Some(Transaction::build_from(pubkey, &tx))
231			})
232			.collect();
233
234		Ok(transactions)
235	}
236
237	fn convert_transaction(
238		&self,
239		block_hash: B::Hash,
240		transaction: ethereum::TransactionV3,
241	) -> RpcResult<B::Extrinsic> {
242		let api_version = match self
243			.client
244			.runtime_api()
245			.api_version::<dyn ConvertTransactionRuntimeApi<B>>(block_hash)
246		{
247			Ok(api_version) => api_version,
248			_ => return Err(internal_err("cannot access `ConvertTransactionRuntimeApi`")),
249		};
250
251		match api_version {
252			Some(2) => match self
253				.client
254				.runtime_api()
255				.convert_transaction(block_hash, transaction)
256			{
257				Ok(extrinsic) => Ok(extrinsic),
258				Err(_) => Err(internal_err("cannot access `ConvertTransactionRuntimeApi`")),
259			},
260			Some(1) => {
261				if let ethereum::TransactionV3::Legacy(legacy_transaction) = transaction {
262					// To be compatible with runtimes that do not support transactions v2
263					#[allow(deprecated)]
264					match self
265						.client
266						.runtime_api()
267						.convert_transaction_before_version_2(block_hash, legacy_transaction)
268					{
269						Ok(extrinsic) => Ok(extrinsic),
270						Err(_) => Err(internal_err("cannot access `ConvertTransactionRuntimeApi`")),
271					}
272				} else {
273					Err(internal_err(
274						"Ethereum transactions v2 is not supported by the runtime",
275					))
276				}
277			}
278			None => {
279				if let Some(ref convert_transaction) = self.convert_transaction {
280					Ok(convert_transaction.convert_transaction(transaction.clone()))
281				} else {
282					Err(internal_err(
283						"`ConvertTransactionRuntimeApi` is not found and no `TransactionConverter` is provided"
284					))
285				}
286			}
287			_ => Err(internal_err(
288				"`ConvertTransactionRuntimeApi` is not supported",
289			)),
290		}
291	}
292}