referrerpolicy=no-referrer-when-downgrade

pallet_revive_eth_rpc/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17//! The [`EthRpcServer`] RPC server implementation
18#![cfg_attr(docsrs, feature(doc_cfg))]
19
20use client::ClientError;
21use jsonrpsee::{
22	core::{async_trait, RpcResult},
23	types::{ErrorCode, ErrorObjectOwned},
24};
25use pallet_revive::evm::*;
26use sp_arithmetic::Permill;
27use sp_core::{keccak_256, H160, H256, U256};
28use thiserror::Error;
29
30pub mod cli;
31pub mod client;
32pub mod example;
33pub mod subxt_client;
34
35#[cfg(test)]
36mod tests;
37
38mod block_info_provider;
39pub use block_info_provider::*;
40
41mod receipt_provider;
42pub use receipt_provider::*;
43
44mod fee_history_provider;
45pub use fee_history_provider::*;
46
47mod receipt_extractor;
48pub use receipt_extractor::*;
49
50mod apis;
51pub use apis::*;
52
53pub const LOG_TARGET: &str = "eth-rpc";
54
55/// An EVM RPC server implementation.
56pub struct EthRpcServerImpl {
57	/// The client used to interact with the substrate node.
58	client: client::Client,
59
60	/// The accounts managed by the server.
61	accounts: Vec<Account>,
62}
63
64impl EthRpcServerImpl {
65	/// Creates a new [`EthRpcServerImpl`].
66	pub fn new(client: client::Client) -> Self {
67		Self { client, accounts: vec![] }
68	}
69
70	/// Sets the accounts managed by the server.
71	pub fn with_accounts(mut self, accounts: Vec<Account>) -> Self {
72		self.accounts = accounts;
73		self
74	}
75}
76
77/// The error type for the EVM RPC server.
78#[derive(Error, Debug)]
79pub enum EthRpcError {
80	/// A [`ClientError`] wrapper error.
81	#[error("Client error: {0}")]
82	ClientError(#[from] ClientError),
83	/// A [`rlp::DecoderError`] wrapper error.
84	#[error("Decoding error: {0}")]
85	RlpError(#[from] rlp::DecoderError),
86	/// A Decimals conversion error.
87	#[error("Conversion error")]
88	ConversionError,
89	/// An invalid signature error.
90	#[error("Invalid signature")]
91	InvalidSignature,
92	/// The account was not found at the given address
93	#[error("Account not found for address {0:?}")]
94	AccountNotFound(H160),
95	/// Received an invalid transaction
96	#[error("Invalid transaction")]
97	InvalidTransaction,
98	/// Received an invalid transaction
99	#[error("Invalid transaction {0:?}")]
100	TransactionTypeNotSupported(Byte),
101}
102
103// TODO use https://eips.ethereum.org/EIPS/eip-1474#error-codes
104impl From<EthRpcError> for ErrorObjectOwned {
105	fn from(value: EthRpcError) -> Self {
106		match value {
107			EthRpcError::ClientError(err) => Self::from(err),
108			_ => Self::owned::<String>(ErrorCode::InvalidRequest.code(), value.to_string(), None),
109		}
110	}
111}
112
113#[async_trait]
114impl EthRpcServer for EthRpcServerImpl {
115	async fn net_version(&self) -> RpcResult<String> {
116		Ok(self.client.chain_id().to_string())
117	}
118
119	async fn net_listening(&self) -> RpcResult<bool> {
120		let syncing = self.client.syncing().await?;
121		let listening = matches!(syncing, SyncingStatus::Bool(false));
122		Ok(listening)
123	}
124
125	async fn syncing(&self) -> RpcResult<SyncingStatus> {
126		Ok(self.client.syncing().await?)
127	}
128
129	async fn block_number(&self) -> RpcResult<U256> {
130		let number = self.client.block_number().await?;
131		Ok(number.into())
132	}
133
134	async fn get_transaction_receipt(
135		&self,
136		transaction_hash: H256,
137	) -> RpcResult<Option<ReceiptInfo>> {
138		let receipt = self.client.receipt(&transaction_hash).await;
139		Ok(receipt)
140	}
141
142	async fn estimate_gas(
143		&self,
144		transaction: GenericTransaction,
145		block: Option<BlockNumberOrTag>,
146	) -> RpcResult<U256> {
147		let hash = self.client.block_hash_for_tag(block.unwrap_or_default().into()).await?;
148		let runtime_api = self.client.runtime_api(hash);
149		let dry_run = runtime_api.dry_run(transaction).await?;
150		Ok(dry_run.eth_gas)
151	}
152
153	async fn call(
154		&self,
155		transaction: GenericTransaction,
156		block: Option<BlockNumberOrTagOrHash>,
157	) -> RpcResult<Bytes> {
158		let hash = self.client.block_hash_for_tag(block.unwrap_or_default()).await?;
159		let runtime_api = self.client.runtime_api(hash);
160		let dry_run = runtime_api.dry_run(transaction).await?;
161		Ok(dry_run.data.into())
162	}
163
164	async fn send_raw_transaction(&self, transaction: Bytes) -> RpcResult<H256> {
165		let hash = H256(keccak_256(&transaction.0));
166		let call = subxt_client::tx().revive().eth_transact(transaction.0);
167		self.client.submit(call).await.map_err(|err| {
168			log::debug!(target: LOG_TARGET, "submit call failed: {err:?}");
169			err
170		})?;
171
172		log::debug!(target: LOG_TARGET, "send_raw_transaction hash: {hash:?}");
173		Ok(hash)
174	}
175
176	async fn send_transaction(&self, mut transaction: GenericTransaction) -> RpcResult<H256> {
177		log::debug!(target: LOG_TARGET, "{transaction:#?}");
178
179		let Some(from) = transaction.from else {
180			log::debug!(target: LOG_TARGET, "Transaction must have a sender");
181			return Err(EthRpcError::InvalidTransaction.into());
182		};
183
184		let account = self
185			.accounts
186			.iter()
187			.find(|account| account.address() == from)
188			.ok_or(EthRpcError::AccountNotFound(from))?;
189
190		if transaction.gas.is_none() {
191			transaction.gas = Some(self.estimate_gas(transaction.clone(), None).await?);
192		}
193
194		if transaction.gas_price.is_none() {
195			transaction.gas_price = Some(self.gas_price().await?);
196		}
197
198		if transaction.nonce.is_none() {
199			transaction.nonce =
200				Some(self.get_transaction_count(from, BlockTag::Latest.into()).await?);
201		}
202
203		if transaction.chain_id.is_none() {
204			transaction.chain_id = Some(self.chain_id().await?);
205		}
206
207		let tx = transaction.try_into_unsigned().map_err(|_| EthRpcError::InvalidTransaction)?;
208		let payload = account.sign_transaction(tx).signed_payload();
209		self.send_raw_transaction(Bytes(payload)).await
210	}
211
212	async fn get_block_by_hash(
213		&self,
214		block_hash: H256,
215		hydrated_transactions: bool,
216	) -> RpcResult<Option<Block>> {
217		let Some(block) = self.client.block_by_hash(&block_hash).await? else {
218			return Ok(None);
219		};
220		let block = self.client.evm_block(block, hydrated_transactions).await;
221		Ok(Some(block))
222	}
223
224	async fn get_balance(&self, address: H160, block: BlockNumberOrTagOrHash) -> RpcResult<U256> {
225		let hash = self.client.block_hash_for_tag(block).await?;
226		let runtime_api = self.client.runtime_api(hash);
227		let balance = runtime_api.balance(address).await?;
228		Ok(balance)
229	}
230
231	async fn chain_id(&self) -> RpcResult<U256> {
232		Ok(self.client.chain_id().into())
233	}
234
235	async fn gas_price(&self) -> RpcResult<U256> {
236		let hash = self.client.block_hash_for_tag(BlockTag::Latest.into()).await?;
237		let runtime_api = self.client.runtime_api(hash);
238		Ok(runtime_api.gas_price().await?)
239	}
240
241	async fn max_priority_fee_per_gas(&self) -> RpcResult<U256> {
242		// TODO: Provide better estimation
243		let gas_price = self.gas_price().await?;
244		Ok(Permill::from_percent(20).mul_ceil(gas_price))
245	}
246
247	async fn get_code(&self, address: H160, block: BlockNumberOrTagOrHash) -> RpcResult<Bytes> {
248		let hash = self.client.block_hash_for_tag(block).await?;
249		let code = self.client.runtime_api(hash).code(address).await?;
250		Ok(code.into())
251	}
252
253	async fn accounts(&self) -> RpcResult<Vec<H160>> {
254		Ok(self.accounts.iter().map(|account| account.address()).collect())
255	}
256
257	async fn get_block_by_number(
258		&self,
259		block_number: BlockNumberOrTag,
260		hydrated_transactions: bool,
261	) -> RpcResult<Option<Block>> {
262		let Some(block) = self.client.block_by_number_or_tag(&block_number).await? else {
263			return Ok(None);
264		};
265		let block = self.client.evm_block(block, hydrated_transactions).await;
266		Ok(Some(block))
267	}
268
269	async fn get_block_transaction_count_by_hash(
270		&self,
271		block_hash: Option<H256>,
272	) -> RpcResult<Option<U256>> {
273		let block_hash = if let Some(block_hash) = block_hash {
274			block_hash
275		} else {
276			self.client.latest_block().await.hash()
277		};
278		Ok(self.client.receipts_count_per_block(&block_hash).await.map(U256::from))
279	}
280
281	async fn get_block_transaction_count_by_number(
282		&self,
283		block: Option<BlockNumberOrTag>,
284	) -> RpcResult<Option<U256>> {
285		let Some(block) = self
286			.get_block_by_number(block.unwrap_or_else(|| BlockTag::Latest.into()), false)
287			.await?
288		else {
289			return Ok(None);
290		};
291
292		Ok(self.client.receipts_count_per_block(&block.hash).await.map(U256::from))
293	}
294
295	async fn get_logs(&self, filter: Option<Filter>) -> RpcResult<FilterResults> {
296		let logs = self.client.logs(filter).await?;
297		Ok(FilterResults::Logs(logs))
298	}
299
300	async fn get_storage_at(
301		&self,
302		address: H160,
303		storage_slot: U256,
304		block: BlockNumberOrTagOrHash,
305	) -> RpcResult<Bytes> {
306		let hash = self.client.block_hash_for_tag(block).await?;
307		let runtime_api = self.client.runtime_api(hash);
308		let bytes = runtime_api.get_storage(address, storage_slot.to_big_endian()).await?;
309		Ok(bytes.unwrap_or_default().into())
310	}
311
312	async fn get_transaction_by_block_hash_and_index(
313		&self,
314		block_hash: H256,
315		transaction_index: U256,
316	) -> RpcResult<Option<TransactionInfo>> {
317		let Some(receipt) = self
318			.client
319			.receipt_by_hash_and_index(
320				&block_hash,
321				transaction_index.try_into().map_err(|_| EthRpcError::ConversionError)?,
322			)
323			.await
324		else {
325			return Ok(None);
326		};
327
328		let Some(signed_tx) = self.client.signed_tx_by_hash(&receipt.transaction_hash).await else {
329			return Ok(None);
330		};
331
332		Ok(Some(TransactionInfo::new(&receipt, signed_tx)))
333	}
334
335	async fn get_transaction_by_block_number_and_index(
336		&self,
337		block: BlockNumberOrTag,
338		transaction_index: U256,
339	) -> RpcResult<Option<TransactionInfo>> {
340		let Some(block) = self.client.block_by_number_or_tag(&block).await? else {
341			return Ok(None);
342		};
343		self.get_transaction_by_block_hash_and_index(block.hash(), transaction_index)
344			.await
345	}
346
347	async fn get_transaction_by_hash(
348		&self,
349		transaction_hash: H256,
350	) -> RpcResult<Option<TransactionInfo>> {
351		let receipt = self.client.receipt(&transaction_hash).await;
352		let signed_tx = self.client.signed_tx_by_hash(&transaction_hash).await;
353		if let (Some(receipt), Some(signed_tx)) = (receipt, signed_tx) {
354			return Ok(Some(TransactionInfo::new(&receipt, signed_tx)));
355		}
356
357		Ok(None)
358	}
359
360	async fn get_transaction_count(
361		&self,
362		address: H160,
363		block: BlockNumberOrTagOrHash,
364	) -> RpcResult<U256> {
365		let hash = self.client.block_hash_for_tag(block).await?;
366		let runtime_api = self.client.runtime_api(hash);
367		let nonce = runtime_api.nonce(address).await?;
368		Ok(nonce)
369	}
370
371	async fn web3_client_version(&self) -> RpcResult<String> {
372		let git_revision = env!("GIT_REVISION");
373		let rustc_version = env!("RUSTC_VERSION");
374		let target = env!("TARGET");
375		Ok(format!("eth-rpc/{git_revision}/{target}/{rustc_version}"))
376	}
377
378	async fn fee_history(
379		&self,
380		block_count: U256,
381		newest_block: BlockNumberOrTag,
382		reward_percentiles: Option<Vec<f64>>,
383	) -> RpcResult<FeeHistoryResult> {
384		let block_count: u32 = block_count.try_into().map_err(|_| EthRpcError::ConversionError)?;
385		let result = self.client.fee_history(block_count, newest_block, reward_percentiles).await?;
386		Ok(result)
387	}
388}