fc_rpc/eth/
state.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::{H160, H256, U256};
20use jsonrpsee::core::RpcResult;
21use scale_codec::Encode;
22// Substrate
23use sc_client_api::backend::{Backend, StorageProvider};
24use sc_transaction_pool_api::{InPoolTransaction, TransactionPool};
25use sp_api::ProvideRuntimeApi;
26use sp_block_builder::BlockBuilder as BlockBuilderApi;
27use sp_blockchain::HeaderBackend;
28use sp_inherents::CreateInherentDataProviders;
29use sp_runtime::traits::Block as BlockT;
30// Frontier
31use fc_rpc_core::types::*;
32use fp_rpc::EthereumRuntimeRPCApi;
33
34use crate::{eth::Eth, frontier_backend_client, internal_err};
35
36impl<B, C, P, CT, BE, CIDP, EC> Eth<B, C, P, CT, BE, CIDP, EC>
37where
38	B: BlockT,
39	C: ProvideRuntimeApi<B>,
40	C::Api: BlockBuilderApi<B> + EthereumRuntimeRPCApi<B>,
41	C: HeaderBackend<B> + StorageProvider<B, BE> + 'static,
42	BE: Backend<B> + 'static,
43	P: TransactionPool<Block = B, Hash = B::Hash> + 'static,
44	CIDP: CreateInherentDataProviders<B, ()> + Send + 'static,
45{
46	pub async fn balance(
47		&self,
48		address: H160,
49		number_or_hash: Option<BlockNumberOrHash>,
50	) -> RpcResult<U256> {
51		let number_or_hash = number_or_hash.unwrap_or(BlockNumberOrHash::Latest);
52		if number_or_hash == BlockNumberOrHash::Pending {
53			let (hash, api) = self
54				.pending_runtime_api()
55				.await
56				.map_err(|err| internal_err(format!("Create pending runtime api error: {err}")))?;
57			Ok(api
58				.account_basic(hash, address)
59				.map_err(|err| internal_err(format!("Fetch account balances failed: {err}")))?
60				.balance)
61		} else if let Ok(Some(id)) = frontier_backend_client::native_block_id::<B, C>(
62			self.client.as_ref(),
63			self.backend.as_ref(),
64			Some(number_or_hash),
65		)
66		.await
67		{
68			let substrate_hash = self
69				.client
70				.expect_block_hash_from_id(&id)
71				.map_err(|_| internal_err(format!("Expect block number from id: {id}")))?;
72
73			Ok(self
74				.client
75				.runtime_api()
76				.account_basic(substrate_hash, address)
77				.map_err(|err| internal_err(format!("Fetch account balances failed: {err:?}")))?
78				.balance)
79		} else {
80			Ok(U256::zero())
81		}
82	}
83
84	pub async fn storage_at(
85		&self,
86		address: H160,
87		index: U256,
88		number_or_hash: Option<BlockNumberOrHash>,
89	) -> RpcResult<H256> {
90		let number_or_hash = number_or_hash.unwrap_or(BlockNumberOrHash::Latest);
91		if number_or_hash == BlockNumberOrHash::Pending {
92			let (hash, api) = self
93				.pending_runtime_api()
94				.await
95				.map_err(|err| internal_err(format!("Create pending runtime api error: {err}")))?;
96			Ok(api.storage_at(hash, address, index).unwrap_or_default())
97		} else if let Ok(Some(id)) = frontier_backend_client::native_block_id::<B, C>(
98			self.client.as_ref(),
99			self.backend.as_ref(),
100			Some(number_or_hash),
101		)
102		.await
103		{
104			let substrate_hash = self
105				.client
106				.expect_block_hash_from_id(&id)
107				.map_err(|_| internal_err(format!("Expect block number from id: {id}")))?;
108			Ok(self
109				.storage_override
110				.account_storage_at(substrate_hash, address, index)
111				.unwrap_or_default())
112		} else {
113			Ok(H256::default())
114		}
115	}
116
117	pub async fn transaction_count(
118		&self,
119		address: H160,
120		number_or_hash: Option<BlockNumberOrHash>,
121	) -> RpcResult<U256> {
122		if let Some(BlockNumberOrHash::Pending) = number_or_hash {
123			let substrate_hash = self.client.info().best_hash;
124
125			let nonce = self
126				.client
127				.runtime_api()
128				.account_basic(substrate_hash, address)
129				.map_err(|err| internal_err(format!("Fetch account nonce failed: {err}")))?
130				.nonce;
131
132			let mut current_nonce = nonce;
133			let mut current_tag = (address, nonce).encode();
134			for tx in self.pool.ready() {
135				// since transactions in `ready()` need to be ordered by nonce
136				// it's fine to continue with current iterator.
137				if tx.provides().first() == Some(&current_tag) {
138					current_nonce = current_nonce.saturating_add(1.into());
139					current_tag = (address, current_nonce).encode();
140				}
141			}
142
143			return Ok(current_nonce);
144		}
145
146		let id = match frontier_backend_client::native_block_id::<B, C>(
147			self.client.as_ref(),
148			self.backend.as_ref(),
149			number_or_hash,
150		)
151		.await?
152		{
153			Some(id) => id,
154			None => return Ok(U256::zero()),
155		};
156
157		let substrate_hash = self
158			.client
159			.expect_block_hash_from_id(&id)
160			.map_err(|_| internal_err(format!("Expect block number from id: {id}")))?;
161
162		Ok(self
163			.client
164			.runtime_api()
165			.account_basic(substrate_hash, address)
166			.map_err(|err| internal_err(format!("Fetch account nonce failed: {err}")))?
167			.nonce)
168	}
169
170	pub async fn code_at(
171		&self,
172		address: H160,
173		number_or_hash: Option<BlockNumberOrHash>,
174	) -> RpcResult<Bytes> {
175		let number_or_hash = number_or_hash.unwrap_or(BlockNumberOrHash::Latest);
176		if number_or_hash == BlockNumberOrHash::Pending {
177			let (hash, api) = self
178				.pending_runtime_api()
179				.await
180				.map_err(|err| internal_err(format!("Create pending runtime api error: {err}")))?;
181			Ok(api
182				.account_code_at(hash, address)
183				.unwrap_or_default()
184				.into())
185		} else if let Ok(Some(id)) = frontier_backend_client::native_block_id::<B, C>(
186			self.client.as_ref(),
187			self.backend.as_ref(),
188			Some(number_or_hash),
189		)
190		.await
191		{
192			let substrate_hash = self
193				.client
194				.expect_block_hash_from_id(&id)
195				.map_err(|_| internal_err(format!("Expect block number from id: {id}")))?;
196			Ok(self
197				.storage_override
198				.account_code_at(substrate_hash, address)
199				.unwrap_or_default()
200				.into())
201		} else {
202			Ok(Bytes(vec![]))
203		}
204	}
205}