From 56477a71a5a961afa999733b3dedf6170d84cd97 Mon Sep 17 00:00:00 2001 From: Qinxuan Chen Date: Wed, 13 Sep 2023 17:50:59 +0800 Subject: [PATCH] fix: pending runtime api for pending state (#1160) (#1195) * fix: pending runtime api for pending state (#1160) * enable integration tests about pending api --- Cargo.lock | 5 + client/rpc/Cargo.toml | 23 ++- client/rpc/src/eth/block.rs | 5 +- client/rpc/src/eth/client.rs | 4 +- client/rpc/src/eth/execute.rs | 114 ++++++------ client/rpc/src/eth/fee.rs | 4 +- client/rpc/src/eth/filter.rs | 2 +- client/rpc/src/eth/mining.rs | 7 +- client/rpc/src/eth/mod.rs | 76 +++----- client/rpc/src/eth/pending.rs | 221 ++++++++++++++++++++++++ client/rpc/src/eth/state.rs | 50 +++--- client/rpc/src/eth/submit.rs | 7 +- client/rpc/src/eth/transaction.rs | 5 +- client/rpc/src/lib.rs | 5 +- template/node/src/rpc/eth.rs | 58 +++---- template/node/src/rpc/mod.rs | 23 +-- template/node/src/service.rs | 84 +++++---- ts-tests/tests/test-balance.ts | 4 +- ts-tests/tests/test-block.ts | 2 - ts-tests/tests/test-contract-storage.ts | 2 - ts-tests/tests/test-contract.ts | 2 - ts-tests/tests/test-nonce.ts | 4 +- ts-tests/tests/test-pending-pool.ts | 10 -- 23 files changed, 480 insertions(+), 237 deletions(-) create mode 100644 client/rpc/src/eth/pending.rs diff --git a/Cargo.lock b/Cargo.lock index 98026e0afc..bd11a72935 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2430,6 +2430,7 @@ dependencies = [ "sc-block-builder", "sc-client-api", "sc-client-db", + "sc-consensus-aura", "sc-network", "sc-network-common", "sc-network-sync", @@ -2444,14 +2445,18 @@ dependencies = [ "sp-block-builder", "sp-blockchain", "sp-consensus", + "sp-consensus-aura", "sp-core", + "sp-inherents", "sp-io", "sp-runtime", "sp-state-machine", "sp-storage", + "sp-timestamp", "substrate-prometheus-endpoint", "substrate-test-runtime-client", "tempfile", + "thiserror", "tokio", ] diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index 34d10f7a6a..6fa01d3f67 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -25,11 +25,13 @@ rlp = { workspace = true } scale-codec = { package = "parity-scale-codec", workspace = true } schnellru = "0.2.1" serde = { workspace = true } +thiserror = { workspace = true } tokio = { workspace = true, features = ["sync"] } # Substrate prometheus-endpoint = { workspace = true } sc-client-api = { workspace = true } +sc-consensus-aura = { workspace = true } sc-network = { workspace = true } sc-network-common = { workspace = true } sc-network-sync = { workspace = true } @@ -38,24 +40,27 @@ sc-service = { workspace = true } sc-transaction-pool = { workspace = true } sc-transaction-pool-api = { workspace = true } sc-utils = { workspace = true } -sp-api = { workspace = true } -sp-block-builder = { workspace = true } +sp-api = { workspace = true, features = ["default"] } +sp-block-builder = { workspace = true, features = ["default"] } sp-blockchain = { workspace = true } sp-consensus = { workspace = true } -sp-core = { workspace = true } -sp-io = { workspace = true } -sp-runtime = { workspace = true } -sp-state-machine = { workspace = true } -sp-storage = { workspace = true } +sp-consensus-aura = { workspace = true, features = ["default"] } +sp-core = { workspace = true, features = ["default"] } +sp-inherents = { workspace = true, features = ["default"] } +sp-io = { workspace = true, features = ["default"] } +sp-runtime = { workspace = true, features = ["default"] } +sp-state-machine = { workspace = true, features = ["default"] } +sp-storage = { workspace = true, features = ["default"] } +sp-timestamp = { workspace = true, features = ["default"] } # Frontier fc-api = { workspace = true } fc-mapping-sync = { workspace = true } fc-rpc-core = { workspace = true } fc-storage = { workspace = true } -fp-evm = { workspace = true } +fp-evm = { workspace = true, features = ["default"] } fp-rpc = { workspace = true, features = ["default"] } fp-storage = { workspace = true, features = ["default"] } -pallet-evm = { workspace = true } +pallet-evm = { workspace = true, features = ["default"] } [dev-dependencies] tempfile = "3.8.0" diff --git a/client/rpc/src/eth/block.rs b/client/rpc/src/eth/block.rs index 1e64d6020f..b2b91583e8 100644 --- a/client/rpc/src/eth/block.rs +++ b/client/rpc/src/eth/block.rs @@ -37,14 +37,15 @@ use crate::{ frontier_backend_client, internal_err, }; -impl> Eth +impl Eth where B: BlockT, C: ProvideRuntimeApi, C::Api: EthereumRuntimeRPCApi, C: HeaderBackend + StorageProvider + 'static, BE: Backend + 'static, - A: ChainApi + 'static, + A: ChainApi, + EC: EthConfig, { pub async fn block_by_hash(&self, hash: H256, full: bool) -> RpcResult> { let BlockInfo { diff --git a/client/rpc/src/eth/client.rs b/client/rpc/src/eth/client.rs index 5683fa2178..d85a0ff21e 100644 --- a/client/rpc/src/eth/client.rs +++ b/client/rpc/src/eth/client.rs @@ -34,13 +34,15 @@ use crate::{ internal_err, }; -impl> Eth +impl Eth where B: BlockT, C: ProvideRuntimeApi, C::Api: EthereumRuntimeRPCApi, C: HeaderBackend + StorageProvider + 'static, BE: Backend, + A: ChainApi, + EC: EthConfig, { pub fn protocol_version(&self) -> RpcResult { Ok(1) diff --git a/client/rpc/src/eth/execute.rs b/client/rpc/src/eth/execute.rs index 19f039cd52..5654370be9 100644 --- a/client/rpc/src/eth/execute.rs +++ b/client/rpc/src/eth/execute.rs @@ -31,6 +31,7 @@ use sp_api::{ }; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::HeaderBackend; +use sp_inherents::CreateInherentDataProviders; use sp_io::hashing::{blake2_128, twox_128}; use sp_runtime::{traits::Block as BlockT, DispatchError, SaturatedConversion}; use sp_state_machine::OverlayedChanges; @@ -41,7 +42,7 @@ use fp_rpc::{EthereumRuntimeRPCApi, RuntimeStorageOverride}; use fp_storage::{EVM_ACCOUNT_CODES, PALLET_EVM}; use crate::{ - eth::{pending_runtime_api, Eth, EthConfig}, + eth::{Eth, EthConfig}, frontier_backend_client, internal_err, }; @@ -66,14 +67,16 @@ impl EstimateGasAdapter for () { } } -impl> Eth +impl Eth where B: BlockT, C: CallApiAt + ProvideRuntimeApi, C::Api: BlockBuilderApi + EthereumRuntimeRPCApi, C: HeaderBackend + StorageProvider + 'static, BE: Backend + 'static, - A: ChainApi + 'static, + A: ChainApi, + CIDP: CreateInherentDataProviders + Send + 'static, + EC: EthConfig, { pub async fn call( &self, @@ -120,8 +123,9 @@ where } None => { // Not mapped in the db, assume pending. - let hash = self.client.info().best_hash; - let api = pending_runtime_api(self.client.as_ref(), self.graph.as_ref())?; + let (hash, api) = self.pending_runtime_api().await.map_err(|err| { + internal_err(format!("Create pending runtime api error: {err}")) + })?; (hash, api) } }; @@ -136,12 +140,12 @@ where let block = if api_version > 1 { api.current_block(substrate_hash) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? + .map_err(|err| internal_err(format!("runtime error: {err}")))? } else { #[allow(deprecated)] let legacy_block = api .current_block_before_version_2(substrate_hash) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))?; legacy_block.map(|block| block.into()) }; @@ -187,8 +191,8 @@ where nonce, false, ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; error_on_execution_failure(&info.exit_reason, &info.value)?; Ok(Bytes(info.value)) @@ -207,8 +211,8 @@ where nonce, false, ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; error_on_execution_failure(&info.exit_reason, &info.value)?; Ok(Bytes(info.value)) @@ -263,8 +267,8 @@ where }, ) }) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; error_on_execution_failure(&info.exit_reason, &info.value)?; info.value @@ -281,8 +285,8 @@ where }, ) }) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; error_on_execution_failure(&info.exit_reason, &info.value)?; info.value @@ -309,14 +313,14 @@ where nonce, false, ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; error_on_execution_failure(&info.exit_reason, &[])?; let code = api .account_code_at(substrate_hash, info.value) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))?; Ok(Bytes(code)) } else if api_version >= 2 && api_version < 4 { // Post-london @@ -332,14 +336,14 @@ where nonce, false, ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; error_on_execution_failure(&info.exit_reason, &[])?; let code = api .account_code_at(substrate_hash, info.value) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))?; Ok(Bytes(code)) } else if api_version == 4 { // Post-london + access list support @@ -362,14 +366,14 @@ where .collect(), ), ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; error_on_execution_failure(&info.exit_reason, &[])?; let code = api .account_code_at(substrate_hash, info.value) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))?; Ok(Bytes(code)) } else if api_version == 5 { // Post-london + access list support @@ -392,14 +396,14 @@ where .collect(), ), ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; error_on_execution_failure(&info.exit_reason, &[])?; let code = api .account_code_at(substrate_hash, info.value) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))?; Ok(Bytes(code)) } else { Err(internal_err("failed to retrieve Runtime Api version")) @@ -435,8 +439,9 @@ where } None => { // Not mapped in the db, assume pending. - let hash = client.info().best_hash; - let api = pending_runtime_api(client.as_ref(), self.graph.as_ref())?; + let (hash, api) = self.pending_runtime_api().await.map_err(|err| { + internal_err(format!("Create pending runtime api error: {err}")) + })?; (hash, api) } }; @@ -453,7 +458,7 @@ where if let Some(to) = request.to { let to_code = api .account_code_at(substrate_hash, to) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))?; if to_code.is_empty() { return Ok(MIN_GAS_PER_TX); } @@ -509,7 +514,7 @@ where if gas_price > U256::zero() { let balance = api .account_basic(substrate_hash, from) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? + .map_err(|err| internal_err(format!("runtime error: {err}")))? .balance; let mut available = balance; if let Some(value) = request.value { @@ -586,8 +591,8 @@ where nonce, estimate_mode, ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; (info.exit_reason, info.value, info.used_gas) } else if api_version < 4 { @@ -605,8 +610,8 @@ where nonce, estimate_mode, ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; (info.exit_reason, info.value, info.used_gas) } else if api_version == 4 { @@ -631,8 +636,8 @@ where .collect(), ), ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; (info.exit_reason, info.value, info.used_gas) } else { @@ -656,8 +661,8 @@ where .collect(), ), ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; (info.exit_reason, info.value, info.used_gas.effective) } @@ -676,8 +681,8 @@ where nonce, estimate_mode, ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; (info.exit_reason, Vec::new(), info.used_gas) } else if api_version < 4 { @@ -694,8 +699,8 @@ where nonce, estimate_mode, ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; (info.exit_reason, Vec::new(), info.used_gas) } else if api_version == 4 { @@ -719,8 +724,8 @@ where .collect(), ), ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; (info.exit_reason, Vec::new(), info.used_gas) } else { @@ -743,8 +748,8 @@ where .collect(), ), ) - .map_err(|err| internal_err(format!("runtime error: {:?}", err)))? - .map_err(|err| internal_err(format!("execution fatal: {:?}", err)))?; + .map_err(|err| internal_err(format!("runtime error: {err}")))? + .map_err(|err| internal_err(format!("execution fatal: {err:?}")))?; (info.exit_reason, Vec::new(), info.used_gas.effective) } @@ -810,8 +815,7 @@ where match exit_reason { ExitReason::Succeed(_) => { return Err(internal_err(format!( - "gas required exceeds allowance {}", - cap + "gas required exceeds allowance {cap}", ))) } // The execution has been done with block gas limit, so it is not a lack of gas from the user. @@ -964,13 +968,13 @@ where pub fn error_on_execution_failure(reason: &ExitReason, data: &[u8]) -> RpcResult<()> { match reason { ExitReason::Succeed(_) => Ok(()), - ExitReason::Error(e) => { - if *e == ExitError::OutOfGas { + ExitReason::Error(err) => { + if *err == ExitError::OutOfGas { // `ServerError(0)` will be useful in estimate gas return Err(internal_err("out of gas")); } Err(crate::internal_err_with_data( - format!("evm error: {:?}", e), + format!("evm error: {err:?}"), &[], )) } @@ -989,14 +993,14 @@ pub fn error_on_execution_failure(reason: &ExitReason, data: &[u8]) -> RpcResult if data.len() >= message_end { let body: &[u8] = &data[MESSAGE_START..message_end]; if let Ok(reason) = std::str::from_utf8(body) { - message = format!("{} {}", message, reason); + message = format!("{message} {reason}"); } } } Err(crate::internal_err_with_data(message, data)) } - ExitReason::Fatal(e) => Err(crate::internal_err_with_data( - format!("evm fatal: {:?}", e), + ExitReason::Fatal(err) => Err(crate::internal_err_with_data( + format!("evm fatal: {err:?}"), &[], )), } diff --git a/client/rpc/src/eth/fee.rs b/client/rpc/src/eth/fee.rs index 2abf78e544..81db0e0091 100644 --- a/client/rpc/src/eth/fee.rs +++ b/client/rpc/src/eth/fee.rs @@ -33,13 +33,15 @@ use crate::{ frontier_backend_client, internal_err, }; -impl> Eth +impl Eth where B: BlockT, C: ProvideRuntimeApi, C::Api: EthereumRuntimeRPCApi, C: HeaderBackend + StorageProvider + 'static, BE: Backend + 'static, + A: ChainApi, + EC: EthConfig, { pub fn gas_price(&self) -> RpcResult { let block_hash = self.client.info().best_hash; diff --git a/client/rpc/src/eth/filter.rs b/client/rpc/src/eth/filter.rs index 4c609db540..b0a34465ec 100644 --- a/client/rpc/src/eth/filter.rs +++ b/client/rpc/src/eth/filter.rs @@ -683,7 +683,7 @@ where Ok(()) } -async fn filter_range_logs( +async fn filter_range_logs( client: &C, block_data_cache: &EthBlockDataCacheTask, ret: &mut Vec, diff --git a/client/rpc/src/eth/mining.rs b/client/rpc/src/eth/mining.rs index 1d97ff4080..1b750312dc 100644 --- a/client/rpc/src/eth/mining.rs +++ b/client/rpc/src/eth/mining.rs @@ -26,7 +26,12 @@ use fc_rpc_core::types::*; use crate::eth::{Eth, EthConfig}; -impl> Eth { +impl Eth +where + B: BlockT, + A: ChainApi, + EC: EthConfig, +{ pub fn is_mining(&self) -> RpcResult { Ok(self.is_authority) } diff --git a/client/rpc/src/eth/mod.rs b/client/rpc/src/eth/mod.rs index 9effa35d21..1c7bb62ebc 100644 --- a/client/rpc/src/eth/mod.rs +++ b/client/rpc/src/eth/mod.rs @@ -24,6 +24,7 @@ mod fee; mod filter; pub mod format; mod mining; +pub mod pending; mod state; mod submit; mod transaction; @@ -37,11 +38,12 @@ use jsonrpsee::core::{async_trait, RpcResult}; use sc_client_api::backend::{Backend, StorageProvider}; use sc_network_sync::SyncingService; use sc_transaction_pool::{ChainApi, Pool}; -use sc_transaction_pool_api::{InPoolTransaction, TransactionPool}; -use sp_api::{ApiRef, CallApiAt, Core, HeaderT, ProvideRuntimeApi}; +use sc_transaction_pool_api::TransactionPool; +use sp_api::{CallApiAt, ProvideRuntimeApi}; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::HeaderBackend; use sp_core::hashing::keccak_256; +use sp_inherents::CreateInherentDataProviders; use sp_runtime::traits::{Block as BlockT, UniqueSaturatedInto}; // Frontier use fc_rpc_core::{types::*, EthApiServer}; @@ -71,7 +73,7 @@ impl EthConfig for () { } /// Eth API implementation. -pub struct Eth> { +pub struct Eth> { pool: Arc

, graph: Arc>, client: Arc, @@ -88,17 +90,20 @@ pub struct Eth> { /// block.gas_limit * execute_gas_limit_multiplier execute_gas_limit_multiplier: u64, forced_parent_hashes: Option>, - _marker: PhantomData<(B, BE, EC)>, + /// Something that can create the inherent data providers for pending state. + pending_create_inherent_data_providers: CIDP, + pending_consensus_data_provider: Option>>, + _marker: PhantomData<(BE, EC)>, } -impl Eth +impl Eth where - A: ChainApi, B: BlockT, C: ProvideRuntimeApi, C::Api: EthereumRuntimeRPCApi, C: HeaderBackend + StorageProvider + 'static, BE: Backend + 'static, + A: ChainApi, EC: EthConfig, { pub fn new( @@ -116,6 +121,8 @@ where fee_history_cache_limit: FeeHistoryCacheLimit, execute_gas_limit_multiplier: u64, forced_parent_hashes: Option>, + pending_create_inherent_data_providers: CIDP, + pending_consensus_data_provider: Option>>, ) -> Self { Self { client, @@ -132,6 +139,8 @@ where fee_history_cache_limit, execute_gas_limit_multiplier, forced_parent_hashes, + pending_create_inherent_data_providers, + pending_consensus_data_provider, _marker: PhantomData, } } @@ -251,8 +260,13 @@ where } } -impl> Eth { - pub fn replace_config>(self) -> Eth { +impl Eth +where + B: BlockT, + A: ChainApi, + EC: EthConfig, +{ + pub fn replace_config>(self) -> Eth { let Self { client, pool, @@ -268,6 +282,8 @@ impl> Eth> Eth EthApiServer for Eth +impl EthApiServer for Eth where B: BlockT, C: CallApiAt + ProvideRuntimeApi, @@ -302,6 +320,7 @@ where P: TransactionPool + 'static, CT: ConvertTransaction<::Extrinsic> + Send + Sync + 'static, A: ChainApi + 'static, + CIDP: CreateInherentDataProviders + Send + 'static, EC: EthConfig, { // ######################################################################## @@ -688,45 +707,6 @@ fn transaction_build( transaction } -fn pending_runtime_api<'a, B: BlockT, C, BE, A: ChainApi>( - client: &'a C, - graph: &'a Pool, -) -> RpcResult> -where - B: BlockT, - C: ProvideRuntimeApi, - C::Api: BlockBuilderApi + EthereumRuntimeRPCApi, - C: HeaderBackend + StorageProvider + 'static, - BE: Backend, - A: ChainApi + 'static, -{ - // In case of Pending, we need an overlayed state to query over. - let api = client.runtime_api(); - let best_hash = client.info().best_hash; - // Get all transactions in the ready queue. - let xts: Vec<::Extrinsic> = graph - .validated_pool() - .ready() - .map(|in_pool_tx| in_pool_tx.data().clone()) - .collect::::Extrinsic>>(); - // Manually initialize the overlay. - if let Ok(Some(header)) = client.header(best_hash) { - let parent_hash = *header.parent_hash(); - api.initialize_block(parent_hash, &header) - .map_err(|e| internal_err(format!("Runtime api access error: {:?}", e)))?; - // Apply the ready queue to the best block's state. - for xt in xts { - let _ = api.apply_extrinsic(best_hash, xt); - } - Ok(api) - } else { - Err(internal_err(format!( - "Cannot get header for block {:?}", - best_hash - ))) - } -} - /// The most commonly used block information in the rpc interfaces. #[derive(Clone, Default)] pub struct BlockInfo { diff --git a/client/rpc/src/eth/pending.rs b/client/rpc/src/eth/pending.rs new file mode 100644 index 0000000000..822562ded0 --- /dev/null +++ b/client/rpc/src/eth/pending.rs @@ -0,0 +1,221 @@ +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// This file is part of Frontier. +// +// Copyright (c) 2020-2022 Parity Technologies (UK) Ltd. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::{marker::PhantomData, sync::Arc}; + +// Substrate +use sc_client_api::{ + backend::{AuxStore, Backend, StorageProvider}, + UsageProvider, +}; +use sc_transaction_pool::ChainApi; +use sc_transaction_pool_api::InPoolTransaction; +use sp_api::{ApiExt, ApiRef, Core, ProvideRuntimeApi}; +use sp_block_builder::BlockBuilder as BlockBuilderApi; +use sp_blockchain::{ApplyExtrinsicFailed, HeaderBackend}; +use sp_inherents::{CreateInherentDataProviders, InherentData, InherentDataProvider}; +use sp_runtime::{ + generic::{Digest, DigestItem}, + traits::{Block as BlockT, Header as HeaderT, One}, + TransactionOutcome, +}; +use sp_timestamp::TimestampInherentData; + +use crate::eth::{Eth, EthConfig}; + +const LOG_TARGET: &str = "eth-pending"; + +/// The generated error type for creating pending runtime api. +#[derive(Debug, thiserror::Error)] +pub(crate) enum Error { + #[error("Failed to call runtime API, {0}")] + CallApi(#[from] sp_api::ApiError), + #[error("Failed to create pending inherent data, {0}")] + PendingInherentData(#[from] sp_inherents::Error), + #[error("Failed to create pending inherent data provider, {0}")] + PendingCreateInherentDataProvider(#[from] Box), + #[error(transparent)] + Backend(#[from] sp_blockchain::Error), + #[error(transparent)] + ApplyExtrinsicFailed(#[from] ApplyExtrinsicFailed), +} + +impl Eth +where + B: BlockT, + C: ProvideRuntimeApi, + C::Api: BlockBuilderApi, + C: HeaderBackend + StorageProvider + 'static, + BE: Backend, + A: ChainApi, + CIDP: CreateInherentDataProviders + Send + 'static, + EC: EthConfig, +{ + /// Creates a pending runtime API. + pub(crate) async fn pending_runtime_api(&self) -> Result<(B::Hash, ApiRef), Error> { + let api = self.client.runtime_api(); + + let info = self.client.info(); + let (best_number, best_hash) = (info.best_number, info.best_hash); + + let inherent_data_provider = self + .pending_create_inherent_data_providers + .create_inherent_data_providers(best_hash, ()) + .await?; + let inherent_data = inherent_data_provider.create_inherent_data().await?; + + let digest = if let Some(digest_provider) = &self.pending_consensus_data_provider { + if let Some(header) = self.client.header(best_hash)? { + digest_provider.create_digest(&header, &inherent_data)? + } else { + Default::default() + } + } else { + Default::default() + }; + + log::info!(target: LOG_TARGET, "Pending runtime API: header digest = {digest:?}"); + + let pending_header = <::Header as HeaderT>::new( + best_number + One::one(), + Default::default(), + Default::default(), + best_hash, + digest, + ); + + // Initialize the pending block header + api.initialize_block(best_hash, &pending_header)?; + + // Apply inherents to the pending block. + let inherents = api.execute_in_transaction(move |api| { + // `create_inherents` should not change any state, to ensure this we always rollback + // the transaction. + TransactionOutcome::Rollback(api.inherent_extrinsics(best_hash, inherent_data)) + })?; + log::debug!(target: LOG_TARGET, "Pending runtime API: inherent len = {}", inherents.len()); + // Apply the inherents to the best block's state. + for ext in inherents { + let _ = api.execute_in_transaction(|api| match api.apply_extrinsic(best_hash, ext) { + Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())), + Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err( + ApplyExtrinsicFailed::Validity(tx_validity).into(), + )), + Err(err) => TransactionOutcome::Rollback(Err(Error::from(err))), + }); + } + + // Get all extrinsics from the ready queue. + let extrinsics: Vec<::Extrinsic> = self + .graph + .validated_pool() + .ready() + .map(|in_pool_tx| in_pool_tx.data().clone()) + .collect::::Extrinsic>>(); + log::debug!(target: LOG_TARGET, "Pending runtime API: extrinsic len = {}", extrinsics.len()); + // Apply the extrinsics from the ready queue to the pending block's state. + for ext in extrinsics { + let _ = api.execute_in_transaction(|api| match api.apply_extrinsic(best_hash, ext) { + Ok(Ok(_)) => TransactionOutcome::Commit(Ok(())), + Ok(Err(tx_validity)) => TransactionOutcome::Rollback(Err( + ApplyExtrinsicFailed::Validity(tx_validity).into(), + )), + Err(err) => TransactionOutcome::Rollback(Err(Error::from(err))), + }); + } + + Ok((best_hash, api)) + } +} + +/// Consensus data provider, pending api uses this trait object for authoring blocks valid for any runtime. +pub trait ConsensusDataProvider: Send + Sync { + /// Attempt to create a consensus digest. + fn create_digest( + &self, + parent: &B::Header, + data: &InherentData, + ) -> Result; +} + +impl ConsensusDataProvider for () { + fn create_digest( + &self, + _: &B::Header, + _: &InherentData, + ) -> Result { + Ok(Default::default()) + } +} + +pub use self::aura::AuraConsensusDataProvider; +mod aura { + use super::*; + use sp_consensus_aura::{ + digests::CompatibleDigestItem, + sr25519::{AuthorityId, AuthoritySignature}, + AuraApi, Slot, SlotDuration, + }; + + /// Consensus data provider for Aura. + pub struct AuraConsensusDataProvider { + // slot duration + slot_duration: SlotDuration, + // phantom data for required generics + _phantom: PhantomData<(B, C)>, + } + + impl AuraConsensusDataProvider + where + B: BlockT, + C: AuxStore + ProvideRuntimeApi + UsageProvider, + C::Api: AuraApi, + { + /// Creates a new instance of the [`AuraConsensusDataProvider`], requires that `client` + /// implements [`sp_consensus_aura::AuraApi`] + pub fn new(client: Arc) -> Self { + let slot_duration = sc_consensus_aura::slot_duration(&*client) + .expect("slot_duration is always present; qed."); + Self { + slot_duration, + _phantom: PhantomData, + } + } + } + + impl ConsensusDataProvider for AuraConsensusDataProvider { + fn create_digest( + &self, + _parent: &B::Header, + data: &InherentData, + ) -> Result { + let timestamp = data + .timestamp_inherent_data()? + .expect("Timestamp is always present; qed"); + + let digest_item = + >::aura_pre_digest( + Slot::from_timestamp(timestamp, self.slot_duration), + ); + + Ok(Digest { + logs: vec![digest_item], + }) + } + } +} diff --git a/client/rpc/src/eth/state.rs b/client/rpc/src/eth/state.rs index 90e34f8d6d..5301128a51 100644 --- a/client/rpc/src/eth/state.rs +++ b/client/rpc/src/eth/state.rs @@ -26,17 +26,18 @@ use sc_transaction_pool_api::{InPoolTransaction, TransactionPool}; use sp_api::ProvideRuntimeApi; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::HeaderBackend; +use sp_inherents::CreateInherentDataProviders; use sp_runtime::traits::Block as BlockT; // Frontier use fc_rpc_core::types::*; use fp_rpc::EthereumRuntimeRPCApi; use crate::{ - eth::{pending_runtime_api, Eth, EthConfig}, + eth::{Eth, EthConfig}, frontier_backend_client, internal_err, }; -impl> Eth +impl Eth where B: BlockT, C: ProvideRuntimeApi, @@ -44,7 +45,9 @@ where C: HeaderBackend + StorageProvider + 'static, BE: Backend + 'static, P: TransactionPool + 'static, - A: ChainApi + 'static, + A: ChainApi, + CIDP: CreateInherentDataProviders + Send + 'static, + EC: EthConfig, { pub async fn balance( &self, @@ -53,10 +56,13 @@ where ) -> RpcResult { let number_or_hash = number_or_hash.unwrap_or(BlockNumberOrHash::Latest); if number_or_hash == BlockNumberOrHash::Pending { - let api = pending_runtime_api(self.client.as_ref(), self.graph.as_ref())?; + let (hash, api) = self + .pending_runtime_api() + .await + .map_err(|err| internal_err(format!("Create pending runtime api error: {err}")))?; Ok(api - .account_basic(self.client.info().best_hash, address) - .map_err(|err| internal_err(format!("fetch runtime chain id failed: {:?}", err)))? + .account_basic(hash, address) + .map_err(|err| internal_err(format!("Fetch account balances failed: {err}")))? .balance) } else if let Ok(Some(id)) = frontier_backend_client::native_block_id::( self.client.as_ref(), @@ -68,13 +74,13 @@ where let substrate_hash = self .client .expect_block_hash_from_id(&id) - .map_err(|_| internal_err(format!("Expect block number from id: {}", id)))?; + .map_err(|_| internal_err(format!("Expect block number from id: {id}")))?; Ok(self .client .runtime_api() .account_basic(substrate_hash, address) - .map_err(|err| internal_err(format!("fetch runtime chain id failed: {:?}", err)))? + .map_err(|err| internal_err(format!("Fetch account balances failed: {:?}", err)))? .balance) } else { Ok(U256::zero()) @@ -89,10 +95,11 @@ where ) -> RpcResult { let number_or_hash = number_or_hash.unwrap_or(BlockNumberOrHash::Latest); if number_or_hash == BlockNumberOrHash::Pending { - let api = pending_runtime_api(self.client.as_ref(), self.graph.as_ref())?; - Ok(api - .storage_at(self.client.info().best_hash, address, index) - .unwrap_or_default()) + let (hash, api) = self + .pending_runtime_api() + .await + .map_err(|err| internal_err(format!("Create pending runtime api error: {err}")))?; + Ok(api.storage_at(hash, address, index).unwrap_or_default()) } else if let Ok(Some(id)) = frontier_backend_client::native_block_id::( self.client.as_ref(), self.backend.as_ref(), @@ -103,7 +110,7 @@ where let substrate_hash = self .client .expect_block_hash_from_id(&id) - .map_err(|_| internal_err(format!("Expect block number from id: {}", id)))?; + .map_err(|_| internal_err(format!("Expect block number from id: {id}")))?; let schema = fc_storage::onchain_storage_schema(self.client.as_ref(), substrate_hash); Ok(self .overrides @@ -129,9 +136,7 @@ where .client .runtime_api() .account_basic(substrate_hash, address) - .map_err(|err| { - internal_err(format!("fetch runtime account basic failed: {:?}", err)) - })? + .map_err(|err| internal_err(format!("Fetch account nonce failed: {err}")))? .nonce; let mut current_nonce = nonce; @@ -162,13 +167,13 @@ where let substrate_hash = self .client .expect_block_hash_from_id(&id) - .map_err(|_| internal_err(format!("Expect block number from id: {}", id)))?; + .map_err(|_| internal_err(format!("Expect block number from id: {id}")))?; Ok(self .client .runtime_api() .account_basic(substrate_hash, address) - .map_err(|err| internal_err(format!("fetch runtime account basic failed: {:?}", err)))? + .map_err(|err| internal_err(format!("Fetch account nonce failed: {err}")))? .nonce) } @@ -179,9 +184,12 @@ where ) -> RpcResult { let number_or_hash = number_or_hash.unwrap_or(BlockNumberOrHash::Latest); if number_or_hash == BlockNumberOrHash::Pending { - let api = pending_runtime_api(self.client.as_ref(), self.graph.as_ref())?; + let (hash, api) = self + .pending_runtime_api() + .await + .map_err(|err| internal_err(format!("Create pending runtime api error: {err}")))?; Ok(api - .account_code_at(self.client.info().best_hash, address) + .account_code_at(hash, address) .unwrap_or_default() .into()) } else if let Ok(Some(id)) = frontier_backend_client::native_block_id::( @@ -194,7 +202,7 @@ where let substrate_hash = self .client .expect_block_hash_from_id(&id) - .map_err(|_| internal_err(format!("Expect block number from id: {}", id)))?; + .map_err(|_| internal_err(format!("Expect block number from id: {id}")))?; let schema = fc_storage::onchain_storage_schema(self.client.as_ref(), substrate_hash); Ok(self diff --git a/client/rpc/src/eth/submit.rs b/client/rpc/src/eth/submit.rs index 3e7eb3e81b..c87affd666 100644 --- a/client/rpc/src/eth/submit.rs +++ b/client/rpc/src/eth/submit.rs @@ -26,6 +26,7 @@ use sc_transaction_pool_api::TransactionPool; use sp_api::{ApiExt, ProvideRuntimeApi}; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::HeaderBackend; +use sp_inherents::CreateInherentDataProviders; use sp_runtime::{ generic::BlockId, traits::Block as BlockT, transaction_validity::TransactionSource, }; @@ -38,7 +39,7 @@ use crate::{ internal_err, }; -impl> Eth +impl Eth where B: BlockT, C: ProvideRuntimeApi, @@ -47,7 +48,9 @@ where BE: Backend + 'static, P: TransactionPool + 'static, CT: ConvertTransaction<::Extrinsic> + 'static, - A: ChainApi + 'static, + A: ChainApi, + CIDP: CreateInherentDataProviders + Send + 'static, + EC: EthConfig, { pub async fn send_transaction(&self, request: TransactionRequest) -> RpcResult { let from = match request.from { diff --git a/client/rpc/src/eth/transaction.rs b/client/rpc/src/eth/transaction.rs index f7e7ad9789..cf15d394bd 100644 --- a/client/rpc/src/eth/transaction.rs +++ b/client/rpc/src/eth/transaction.rs @@ -38,14 +38,15 @@ use crate::{ frontier_backend_client, internal_err, }; -impl> Eth +impl Eth where B: BlockT, C: ProvideRuntimeApi, C::Api: EthereumRuntimeRPCApi, C: HeaderBackend + StorageProvider + 'static, BE: Backend + 'static, - A: ChainApi + 'static, + A: ChainApi, + EC: EthConfig, { pub async fn transaction_by_hash(&self, hash: H256) -> RpcResult> { let client = Arc::clone(&self.client); diff --git a/client/rpc/src/lib.rs b/client/rpc/src/lib.rs index c03bfc13e4..e622c7c8d8 100644 --- a/client/rpc/src/lib.rs +++ b/client/rpc/src/lib.rs @@ -37,7 +37,10 @@ mod web3; #[cfg(feature = "txpool")] pub use self::txpool::TxPool; pub use self::{ - eth::{format, EstimateGasAdapter, Eth, EthBlockDataCacheTask, EthConfig, EthFilter, EthTask}, + eth::{ + format, pending, EstimateGasAdapter, Eth, EthBlockDataCacheTask, EthConfig, EthFilter, + EthTask, + }, eth_pubsub::{EthPubSub, EthereumSubIdProvider}, net::Net, signer::{EthDevSigner, EthSigner}, diff --git a/template/node/src/rpc/eth.rs b/template/node/src/rpc/eth.rs index ad94176693..caf7e478ef 100644 --- a/template/node/src/rpc/eth.rs +++ b/template/node/src/rpc/eth.rs @@ -5,6 +5,7 @@ use jsonrpsee::RpcModule; use sc_client_api::{ backend::{Backend, StorageProvider}, client::BlockchainEvents, + AuxStore, UsageProvider, }; use sc_network::NetworkService; use sc_network_sync::SyncingService; @@ -14,7 +15,9 @@ use sc_transaction_pool_api::TransactionPool; use sp_api::{CallApiAt, ProvideRuntimeApi}; use sp_block_builder::BlockBuilder as BlockBuilderApi; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sp_consensus_aura::{sr25519::AuthorityId as AuraId, AuraApi}; use sp_core::H256; +use sp_inherents::CreateInherentDataProviders; use sp_runtime::traits::Block as BlockT; // Frontier pub use fc_rpc::{EthBlockDataCacheTask, EthConfig, OverrideHandle, StorageOverride}; @@ -23,7 +26,7 @@ pub use fc_storage::overrides_handle; use fp_rpc::{ConvertTransaction, ConvertTransactionRuntimeApi, EthereumRuntimeRPCApi}; /// Extra dependencies for Ethereum compatibility. -pub struct EthDeps { +pub struct EthDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. @@ -59,36 +62,14 @@ pub struct EthDeps { pub execute_gas_limit_multiplier: u64, /// Mandated parent hashes for a given block hash. pub forced_parent_hashes: Option>, -} - -impl Clone for EthDeps { - fn clone(&self) -> Self { - Self { - client: self.client.clone(), - pool: self.pool.clone(), - graph: self.graph.clone(), - converter: self.converter.clone(), - is_authority: self.is_authority, - enable_dev_signer: self.enable_dev_signer, - network: self.network.clone(), - sync: self.sync.clone(), - frontier_backend: self.frontier_backend.clone(), - overrides: self.overrides.clone(), - block_data_cache: self.block_data_cache.clone(), - filter_pool: self.filter_pool.clone(), - max_past_logs: self.max_past_logs, - fee_history_cache: self.fee_history_cache.clone(), - fee_history_cache_limit: self.fee_history_cache_limit, - execute_gas_limit_multiplier: self.execute_gas_limit_multiplier, - forced_parent_hashes: self.forced_parent_hashes.clone(), - } - } + /// Something that can create the inherent data providers for pending state + pub pending_create_inherent_data_providers: CIDP, } /// Instantiate Ethereum-compatible RPC extensions. -pub fn create_eth>( +pub fn create_eth( mut io: RpcModule<()>, - deps: EthDeps, + deps: EthDeps, subscription_task_executor: SubscriptionTaskExecutor, pubsub_notification_sinks: Arc< fc_mapping_sync::EthereumBlockNotificationSinks< @@ -97,19 +78,25 @@ pub fn create_eth>( >, ) -> Result, Box> where - B: BlockT, + B: BlockT, C: CallApiAt + ProvideRuntimeApi, - C::Api: BlockBuilderApi + ConvertTransactionRuntimeApi + EthereumRuntimeRPCApi, - C: BlockchainEvents + 'static, - C: HeaderBackend + HeaderMetadata + StorageProvider, + C::Api: AuraApi + + BlockBuilderApi + + ConvertTransactionRuntimeApi + + EthereumRuntimeRPCApi, + C: HeaderBackend + HeaderMetadata, + C: BlockchainEvents + AuxStore + UsageProvider + StorageProvider + 'static, BE: Backend + 'static, P: TransactionPool + 'static, A: ChainApi + 'static, CT: ConvertTransaction<::Extrinsic> + Send + Sync + 'static, + CIDP: CreateInherentDataProviders + Send + 'static, + EC: EthConfig, { use fc_rpc::{ - Eth, EthApiServer, EthDevSigner, EthFilter, EthFilterApiServer, EthPubSub, - EthPubSubApiServer, EthSigner, Net, NetApiServer, Web3, Web3ApiServer, + pending::AuraConsensusDataProvider, Eth, EthApiServer, EthDevSigner, EthFilter, + EthFilterApiServer, EthPubSub, EthPubSubApiServer, EthSigner, Net, NetApiServer, Web3, + Web3ApiServer, }; #[cfg(feature = "txpool")] use fc_rpc::{TxPool, TxPoolApiServer}; @@ -132,6 +119,7 @@ where fee_history_cache_limit, execute_gas_limit_multiplier, forced_parent_hashes, + pending_create_inherent_data_providers, } = deps; let mut signers = Vec::new(); @@ -140,7 +128,7 @@ where } io.merge( - Eth::::new( + Eth::::new( client.clone(), pool.clone(), graph.clone(), @@ -155,6 +143,8 @@ where fee_history_cache_limit, execute_gas_limit_multiplier, forced_parent_hashes, + pending_create_inherent_data_providers, + Some(Box::new(AuraConsensusDataProvider::new(client.clone()))), ) .replace_config::() .into_rpc(), diff --git a/template/node/src/rpc/mod.rs b/template/node/src/rpc/mod.rs index d2b6530c10..b655b1e890 100644 --- a/template/node/src/rpc/mod.rs +++ b/template/node/src/rpc/mod.rs @@ -8,6 +8,7 @@ use jsonrpsee::RpcModule; use sc_client_api::{ backend::{Backend, StorageProvider}, client::BlockchainEvents, + AuxStore, UsageProvider, }; use sc_consensus_manual_seal::rpc::EngineCommand; use sc_rpc::SubscriptionTaskExecutor; @@ -16,6 +17,8 @@ use sc_service::TransactionPool; use sc_transaction_pool::ChainApi; use sp_api::{CallApiAt, ProvideRuntimeApi}; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_inherents::CreateInherentDataProviders; use sp_runtime::traits::Block as BlockT; // Runtime use frontier_template_runtime::{opaque::Block, AccountId, Balance, Hash, Nonce}; @@ -24,7 +27,7 @@ mod eth; pub use self::eth::{create_eth, overrides_handle, EthDeps}; /// Full client dependencies. -pub struct FullDeps { +pub struct FullDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. @@ -34,7 +37,7 @@ pub struct FullDeps { /// Manual seal command sink pub command_sink: Option>>, /// Ethereum-compatibility specific dependencies. - pub eth: EthDeps, + pub eth: EthDeps, } pub struct DefaultEthConfig(std::marker::PhantomData<(C, BE)>); @@ -50,8 +53,8 @@ where } /// Instantiate all Full RPC extensions. -pub fn create_full( - deps: FullDeps, +pub fn create_full( + deps: FullDeps, subscription_task_executor: SubscriptionTaskExecutor, pubsub_notification_sinks: Arc< fc_mapping_sync::EthereumBlockNotificationSinks< @@ -61,18 +64,18 @@ pub fn create_full( ) -> Result, Box> where C: CallApiAt + ProvideRuntimeApi, - C::Api: substrate_frame_rpc_system::AccountNonceApi, C::Api: sp_block_builder::BlockBuilder, + C::Api: sp_consensus_aura::AuraApi, + C::Api: substrate_frame_rpc_system::AccountNonceApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, C::Api: fp_rpc::ConvertTransactionRuntimeApi, C::Api: fp_rpc::EthereumRuntimeRPCApi, - C: BlockchainEvents + 'static, - C: HeaderBackend - + HeaderMetadata - + StorageProvider, + C: HeaderBackend + HeaderMetadata + 'static, + C: BlockchainEvents + AuxStore + UsageProvider + StorageProvider, BE: Backend + 'static, P: TransactionPool + 'static, A: ChainApi + 'static, + CIDP: CreateInherentDataProviders + Send + 'static, CT: fp_rpc::ConvertTransaction<::Extrinsic> + Send + Sync + 'static, { use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; @@ -100,7 +103,7 @@ where } // Ethereum compatibility RPCs - let io = create_eth::<_, _, _, _, _, _, DefaultEthConfig>( + let io = create_eth::<_, _, _, _, _, _, _, DefaultEthConfig>( io, eth, subscription_task_executor, diff --git a/template/node/src/service.rs b/template/node/src/service.rs index da571c8ead..24e5f5ecb0 100644 --- a/template/node/src/service.rs +++ b/template/node/src/service.rs @@ -384,41 +384,68 @@ where // for ethereum-compatibility rpc. config.rpc_id_provider = Some(Box::new(fc_rpc::EthereumSubIdProvider)); - let eth_rpc_params = crate::rpc::EthDeps { - client: client.clone(), - pool: transaction_pool.clone(), - graph: transaction_pool.pool().clone(), - converter: Some(TransactionConverter), - is_authority: config.role.is_authority(), - enable_dev_signer: eth_config.enable_dev_signer, - network: network.clone(), - sync: sync_service.clone(), - frontier_backend: match frontier_backend.clone() { - fc_db::Backend::KeyValue(b) => Arc::new(b), - fc_db::Backend::Sql(b) => Arc::new(b), - }, - overrides: overrides.clone(), - block_data_cache: Arc::new(fc_rpc::EthBlockDataCacheTask::new( + + let rpc_builder = { + let client = client.clone(); + let pool = transaction_pool.clone(); + let network = network.clone(); + let sync_service = sync_service.clone(); + + let is_authority = role.is_authority(); + let enable_dev_signer = eth_config.enable_dev_signer; + let max_past_logs = eth_config.max_past_logs; + let execute_gas_limit_multiplier = eth_config.execute_gas_limit_multiplier; + let filter_pool = filter_pool.clone(); + let frontier_backend = frontier_backend.clone(); + let pubsub_notification_sinks = pubsub_notification_sinks.clone(); + let overrides = overrides.clone(); + let fee_history_cache = fee_history_cache.clone(); + let block_data_cache = Arc::new(fc_rpc::EthBlockDataCacheTask::new( task_manager.spawn_handle(), overrides.clone(), eth_config.eth_log_block_cache, eth_config.eth_statuses_cache, prometheus_registry.clone(), - )), - filter_pool: filter_pool.clone(), - max_past_logs: eth_config.max_past_logs, - fee_history_cache: fee_history_cache.clone(), - fee_history_cache_limit, - execute_gas_limit_multiplier: eth_config.execute_gas_limit_multiplier, - forced_parent_hashes: None, - }; + )); - let rpc_builder = { - let client = client.clone(); - let pool = transaction_pool.clone(); - let pubsub_notification_sinks = pubsub_notification_sinks.clone(); + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + let target_gas_price = eth_config.target_gas_price; + let pending_create_inherent_data_providers = move |_, ()| async move { + let current = sp_timestamp::InherentDataProvider::from_system_time(); + let next_slot = current.timestamp().as_millis() + slot_duration.as_millis(); + let timestamp = sp_timestamp::InherentDataProvider::new(next_slot.into()); + let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + let dynamic_fee = fp_dynamic_fee::InherentDataProvider(U256::from(target_gas_price)); + Ok((slot, timestamp, dynamic_fee)) + }; Box::new(move |deny_unsafe, subscription_task_executor| { + let eth_deps = crate::rpc::EthDeps { + client: client.clone(), + pool: pool.clone(), + graph: pool.pool().clone(), + converter: Some(TransactionConverter), + is_authority, + enable_dev_signer, + network: network.clone(), + sync: sync_service.clone(), + frontier_backend: match frontier_backend.clone() { + fc_db::Backend::KeyValue(b) => Arc::new(b), + fc_db::Backend::Sql(b) => Arc::new(b), + }, + overrides: overrides.clone(), + block_data_cache: block_data_cache.clone(), + filter_pool: filter_pool.clone(), + max_past_logs, + fee_history_cache: fee_history_cache.clone(), + fee_history_cache_limit, + execute_gas_limit_multiplier, + forced_parent_hashes: None, + pending_create_inherent_data_providers, + }; let deps = crate::rpc::FullDeps { client: client.clone(), pool: pool.clone(), @@ -428,9 +455,8 @@ where } else { None }, - eth: eth_rpc_params.clone(), + eth: eth_deps, }; - crate::rpc::create_full( deps, subscription_task_executor, diff --git a/ts-tests/tests/test-balance.ts b/ts-tests/tests/test-balance.ts index bc8986571e..a2faff274d 100644 --- a/ts-tests/tests/test-balance.ts +++ b/ts-tests/tests/test-balance.ts @@ -39,8 +39,8 @@ describeWithFrontier("Frontier RPC (Balance)", (context) => { BigInt(TRANFER_VALUE) ).toString(); const expectedTestBalance = (Number(TRANFER_VALUE) - EXISTENTIAL_DEPOSIT).toString(); - // expect(await context.web3.eth.getBalance(GENESIS_ACCOUNT, "pending")).to.equal(expectedGenesisBalance); - // expect(await context.web3.eth.getBalance(TEST_ACCOUNT, "pending")).to.equal(expectedTestBalance); + expect(await context.web3.eth.getBalance(GENESIS_ACCOUNT, "pending")).to.equal(expectedGenesisBalance); + expect(await context.web3.eth.getBalance(TEST_ACCOUNT, "pending")).to.equal(expectedTestBalance); await createAndFinalizeBlock(context.web3); diff --git a/ts-tests/tests/test-block.ts b/ts-tests/tests/test-block.ts index 2921c38f77..46a3d89e6f 100644 --- a/ts-tests/tests/test-block.ts +++ b/ts-tests/tests/test-block.ts @@ -177,7 +177,6 @@ describeWithFrontier("Frontier RPC (Pending Block)", (context) => { nonce = nonce + 100; await sendTransaction(); - /* // do not seal, get pending block let pending_transactions = []; { @@ -194,7 +193,6 @@ describeWithFrontier("Frontier RPC (Pending Block)", (context) => { await createAndFinalizeBlock(context.web3); const latest_block = await context.web3.eth.getBlock("latest", false); expect(pending_transactions).to.be.deep.eq(latest_block.transactions); - */ }); }); diff --git a/ts-tests/tests/test-contract-storage.ts b/ts-tests/tests/test-contract-storage.ts index ac034a220e..66ff3f6ab7 100644 --- a/ts-tests/tests/test-contract-storage.ts +++ b/ts-tests/tests/test-contract-storage.ts @@ -41,7 +41,6 @@ describeWithFrontier("Frontier RPC (Contract)", (context) => { expect(getStorage0.result).to.be.eq("0x0000000000000000000000000000000000000000000000000000000000000000"); - /* const tx1 = await context.web3.eth.accounts.signTransaction( { from: GENESIS_ACCOUNT, @@ -80,7 +79,6 @@ describeWithFrontier("Frontier RPC (Contract)", (context) => { ]); expect(getStorage1.result).to.be.eq(expectedStorage); - */ }); it("SSTORE cost should properly take into account transaction initial value", async function () { diff --git a/ts-tests/tests/test-contract.ts b/ts-tests/tests/test-contract.ts index 5d7ca59555..dcdb8b80fa 100644 --- a/ts-tests/tests/test-contract.ts +++ b/ts-tests/tests/test-contract.ts @@ -40,14 +40,12 @@ describeWithFrontier("Frontier RPC (Contract)", (context) => { result: "0x", }); - /* // Verify the contract is in the pending state expect(await customRequest(context.web3, "eth_getCode", [FIRST_CONTRACT_ADDRESS, "pending"])).to.deep.equal({ id: 1, jsonrpc: "2.0", result: TEST_CONTRACT_DEPLOYED_BYTECODE, }); - */ // Verify the contract is stored after the block is produced await createAndFinalizeBlock(context.web3); diff --git a/ts-tests/tests/test-nonce.ts b/ts-tests/tests/test-nonce.ts index ba2f3a1ec4..00e2a33714 100644 --- a/ts-tests/tests/test-nonce.ts +++ b/ts-tests/tests/test-nonce.ts @@ -25,12 +25,12 @@ describeWithFrontier("Frontier RPC (Nonce)", (context) => { await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]); expect(await context.web3.eth.getTransactionCount(GENESIS_ACCOUNT, "latest")).to.eq(0); - // expect(await context.web3.eth.getTransactionCount(GENESIS_ACCOUNT, "pending")).to.eq(1); + expect(await context.web3.eth.getTransactionCount(GENESIS_ACCOUNT, "pending")).to.eq(1); await createAndFinalizeBlock(context.web3); expect(await context.web3.eth.getTransactionCount(GENESIS_ACCOUNT, "latest")).to.eq(1); - // expect(await context.web3.eth.getTransactionCount(GENESIS_ACCOUNT, "pending")).to.eq(1); + expect(await context.web3.eth.getTransactionCount(GENESIS_ACCOUNT, "pending")).to.eq(1); expect(await context.web3.eth.getTransactionCount(GENESIS_ACCOUNT, "earliest")).to.eq(0); }); diff --git a/ts-tests/tests/test-pending-pool.ts b/ts-tests/tests/test-pending-pool.ts index de5cb87216..809ad0aa9b 100644 --- a/ts-tests/tests/test-pending-pool.ts +++ b/ts-tests/tests/test-pending-pool.ts @@ -75,16 +75,13 @@ describeWithFrontier("Frontier RPC (Pending Transaction Count)", (context) => { return (await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction])).result; }; - /* { const pendingTransactionCount = ( await customRequest(context.web3, "eth_getBlockTransactionCountByNumber", ["pending"]) ).result; expect(pendingTransactionCount).to.eq("0x0"); } - */ - /* // block 1 should have 1 transaction await sendTransaction(); { @@ -93,11 +90,9 @@ describeWithFrontier("Frontier RPC (Pending Transaction Count)", (context) => { ).result; expect(pendingTransactionCount).to.eq("0x1"); } - */ await createAndFinalizeBlock(context.web3); - /* { const pendingTransactionCount = ( await customRequest(context.web3, "eth_getBlockTransactionCountByNumber", ["pending"]) @@ -108,25 +103,21 @@ describeWithFrontier("Frontier RPC (Pending Transaction Count)", (context) => { ).result; expect(processedTransactionCount).to.eq("0x1"); } - */ // block 2 should have 5 transactions for (var _ of Array(5)) { await sendTransaction(); } - /* { const pendingTransactionCount = ( await customRequest(context.web3, "eth_getBlockTransactionCountByNumber", ["pending"]) ).result; expect(pendingTransactionCount).to.eq("0x5"); } - */ await createAndFinalizeBlock(context.web3); - /* { const pendingTransactionCount = ( await customRequest(context.web3, "eth_getBlockTransactionCountByNumber", ["pending"]) @@ -137,6 +128,5 @@ describeWithFrontier("Frontier RPC (Pending Transaction Count)", (context) => { ).result; expect(processedTransactionCount).to.eq("0x5"); } - */ }); });