diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 170b94cae1..7c071d3cec 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -15,11 +15,13 @@ frontier-rpc-core = { path = "core" } frontier-rpc-primitives = { path = "primitives" } sp-runtime = { path = "../vendor/substrate/primitives/runtime" } sp-api = { path = "../vendor/substrate/primitives/api" } +sp-io = { path = "../vendor/substrate/primitives/io" } sp-consensus = { path = "../vendor/substrate/primitives/consensus/common" } sp-transaction-pool = { path = "../vendor/substrate/primitives/transaction-pool" } sp-storage = { path = "../vendor/substrate/primitives/storage" } sc-service = { path = "../vendor/substrate/client/service" } sc-client-api = { path = "../vendor/substrate/client/api" } +sp-blockchain = { path = "../vendor/substrate/primitives/blockchain" } ethereum = { version = "0.2", features = ["codec"] } codec = { package = "parity-scale-codec", version = "1.0.0" } rlp = "0.4" diff --git a/rpc/primitives/src/lib.rs b/rpc/primitives/src/lib.rs index 4ea881fd57..f9f974c911 100644 --- a/rpc/primitives/src/lib.rs +++ b/rpc/primitives/src/lib.rs @@ -21,6 +21,7 @@ use ethereum::{Log, Block as EthereumBlock, Transaction as EthereumTransaction}; use ethereum_types::Bloom; use codec::{Encode, Decode}; use sp_std::vec::Vec; +pub use pallet_evm::{Account as EVMAccount}; #[derive(Eq, PartialEq, Clone, Encode, Decode, sp_runtime::RuntimeDebug)] pub struct TransactionStatus { @@ -37,7 +38,7 @@ sp_api::decl_runtime_apis! { /// API necessary for Ethereum-compatibility layer. pub trait EthereumRuntimeApi { fn chain_id() -> u64; - fn account_basic(address: H160) -> pallet_evm::Account; + fn account_basic(address: H160) -> EVMAccount; fn transaction_status(hash: H256) -> Option; fn gas_price() -> U256; fn account_code_at(address: H160) -> Vec; diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 6c011253dc..e131ea7a37 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -23,6 +23,9 @@ use futures::future::TryFutureExt; use sp_runtime::traits::{Block as BlockT, Header as _, UniqueSaturatedInto}; use sp_runtime::transaction_validity::TransactionSource; use sp_api::{ProvideRuntimeApi, BlockId}; +use sp_io::hashing::{twox_128, blake2_128}; +use sp_blockchain::{Error as BlockChainError, HeaderMetadata, HeaderBackend}; +use sp_storage::StorageKey; use sp_consensus::SelectChain; use sp_transaction_pool::TransactionPool; use sc_client_api::backend::{StorageProvider, Backend, StateBackend}; @@ -33,8 +36,10 @@ use frontier_rpc_core::types::{ BlockNumber, Bytes, CallRequest, EthAccount, Filter, Index, Log, Receipt, RichBlock, SyncStatus, Transaction, Work, Rich, Block, BlockTransactions }; -use frontier_rpc_primitives::{EthereumRuntimeApi, ConvertTransaction, TransactionStatus}; - +use frontier_rpc_primitives::{ + EthereumRuntimeApi, ConvertTransaction, TransactionStatus, EVMAccount +}; +use codec::Decode; pub use frontier_rpc_core::EthApiServer; fn internal_err(message: &str) -> Error { @@ -131,8 +136,13 @@ fn transaction_build( } } +fn storage_prefix_build(module: &[u8], storage: &[u8]) -> Vec { + [twox_128(module), twox_128(storage)].concat().to_vec() +} + impl EthApiT for EthApi where - C: ProvideRuntimeApi + StorageProvider, + C: ProvideRuntimeApi + StorageProvider + + HeaderBackend + HeaderMetadata, C::Api: EthereumRuntimeApi, BE: Backend + 'static, BE::State: StateBackend, @@ -206,22 +216,49 @@ impl EthApiT for EthApi where } fn balance(&self, address: H160, number: Option) -> Result { + let header = self.select_chain.best_chain() + .map_err(|_| internal_err("fetch header failed"))?; + + let number_param: u32; + if let Some(number) = number { - if number != BlockNumber::Latest { - unimplemented!("fetch nonce for past blocks is not yet supported"); + if let Some(block_number) = number.to_min_block_num() { + number_param = block_number.unique_saturated_into(); + } else if number == BlockNumber::Latest { + number_param = header.number().clone().unique_saturated_into() as u32; + } else { + unimplemented!("only latest or block number are supported"); } + } else { + number_param = header.number().clone().unique_saturated_into() as u32; } - let header = self - .select_chain - .best_chain() - .map_err(|_| internal_err("fetch header failed"))?; - Ok( - self.client - .runtime_api() - .account_basic(&BlockId::Hash(header.hash()), address) - .map_err(|_| internal_err("fetch runtime chain id failed"))? - .balance.into(), - ) + + let mut block_hash = None; + if let Ok(result) = self.client.header(BlockId::Number(number_param.into())) { + if let Some(header) = result { + block_hash = Some(header.hash()); + } + } + + if let Some(block_hash) = block_hash { + // StorageProvider prefix + let mut prefix = storage_prefix_build(b"EVM", b"Accounts"); + // StorageMap blake2_128_concat key + let mut storage_key = blake2_128(address.as_bytes()).to_vec(); + storage_key.extend_from_slice(address.as_bytes()); + // Module and Storage prefix + StorageMap concat + prefix.extend(storage_key.clone()); + + let key = StorageKey(prefix); + + if let Ok(Some(data)) = self.client.storage( + &BlockId::Hash(block_hash), + &key) { + let account: EVMAccount = Decode::decode(&mut &data.0[..]).unwrap(); + return Ok(account.balance.into()); + } + } + Ok(U256::zero()) } fn proof(&self, _: H160, _: Vec, _: Option) -> BoxFuture {