diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a92dea0e14..a496baeb67 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -29,8 +29,6 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Run tests run: cargo test --locked --verbose --all - - name: Ensure runtime-benchmarks and try-runtime features compiles - run: cargo check --release --features=runtime-benchmarks,try-runtime integration: name: 'Run integration tests' @@ -48,10 +46,10 @@ jobs: uses: arduino/setup-protoc@v1 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: Build manual seal client + - name: Build client run: | cd template/node - cargo build --release --locked --verbose --no-default-features --features manual-seal,rpc_binary_search_estimate + cargo build --release --locked --verbose --features rpc-binary-search-estimate - name: Use Node.js 18 uses: actions/setup-node@v3 with: diff --git a/Cargo.lock b/Cargo.lock index 1a1c9017cd..3c1533cb14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -706,9 +706,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.0.29" +version = "4.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" +checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2" dependencies = [ "bitflags", "clap_derive", @@ -721,9 +721,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.0.21" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" +checksum = "684a277d672e91966334af371f1a7b5833f9aa00b07c84e92fbce95e00208ce8" dependencies = [ "heck", "proc-macro-error", @@ -2152,12 +2152,14 @@ dependencies = [ "frame-benchmarking", "frame-benchmarking-cli", "frame-system", + "frame-system-rpc-runtime-api", "frontier-template-runtime", "futures", "jsonrpsee", "log", "pallet-transaction-payment", "pallet-transaction-payment-rpc", + "pallet-transaction-payment-rpc-runtime-api", "parity-scale-codec", "sc-basic-authorship", "sc-cli", @@ -2167,13 +2169,14 @@ dependencies = [ "sc-consensus-manual-seal", "sc-executor", "sc-finality-grandpa", - "sc-keystore", "sc-network", "sc-rpc", "sc-rpc-api", "sc-service", "sc-telemetry", "sc-transaction-pool", + "sc-transaction-pool-api", + "serde", "sp-api", "sp-block-builder", "sp-blockchain", @@ -2182,10 +2185,16 @@ dependencies = [ "sp-finality-grandpa", "sp-inherents", "sp-keyring", + "sp-offchain", "sp-runtime", + "sp-session", + "sp-state-machine", "sp-timestamp", + "sp-transaction-pool", + "sp-trie", "substrate-build-script-utils", "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", ] [[package]] @@ -6694,18 +6703,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.150" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.150" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -6714,9 +6723,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.89" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "itoa", "ryu", @@ -7994,18 +8003,18 @@ checksum = "95059e91184749cb66be6dc994f67f182b6d897cb3df74a5bf66b5e709295fd8" [[package]] name = "thiserror" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.37" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -8103,9 +8112,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.23.0" +version = "1.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae" dependencies = [ "autocfg", "bytes", diff --git a/Cargo.toml b/Cargo.toml index b82467ba56..e22ed2bf38 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,10 +84,12 @@ sp-keyring = { version = "7.0.0", git = "https://github.com/paritytech/substrate sp-offchain = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-runtime = { version = "7.0.0", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-session = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-state-machine = { version = "0.13.0", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-std = { version = "5.0.0", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-storage = { version = "7.0.0", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-timestamp = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-transaction-pool = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-trie = { version = "7.0.0", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } sp-version = { version = "5.0.0", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } # Substrate FRAME frame-benchmarking = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index 99f1b869a2..0d8fe0f4d8 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -11,7 +11,7 @@ repository = { workspace = true } targets = ["x86_64-unknown-linux-gnu"] [dependencies] -clap = { version = "4.0", features = ["derive"] } +clap = { version = "4.1", features = ["derive", "deprecated"] } ethereum-types = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } @@ -36,4 +36,4 @@ sp-consensus = { workspace = true } sp-io = { workspace = true } substrate-test-runtime-client = { workspace = true } # Frontier -frontier-template-runtime = { workspace = true, features = ["std", "aura", "with-rocksdb-weights"] } +frontier-template-runtime = { workspace = true, features = ["std", "with-rocksdb-weights"] } diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index 2c4304da3c..f45fedeb8c 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -24,7 +24,7 @@ prometheus = { version = "0.13.1", default-features = false } rand = "0.8" rlp = { workspace = true } scale-codec = { package = "parity-scale-codec", workspace = true } -tokio = { version = "1.19", features = ["sync"] } +tokio = { version = "1.24", features = ["sync"] } # Substrate prometheus-endpoint = { workspace = true } @@ -59,4 +59,4 @@ sp-consensus = { workspace = true } substrate-test-runtime-client = { workspace = true } [features] -rpc_binary_search_estimate = [] +rpc-binary-search-estimate = [] diff --git a/client/rpc/src/eth/execute.rs b/client/rpc/src/eth/execute.rs index d35b647a6a..fa8e3ac2bf 100644 --- a/client/rpc/src/eth/execute.rs +++ b/client/rpc/src/eth/execute.rs @@ -599,7 +599,7 @@ where // Verify that the transaction succeed with highest capacity let cap = highest; - let estimate_mode = !cfg!(feature = "rpc_binary_search_estimate"); + let estimate_mode = !cfg!(feature = "rpc-binary-search-estimate"); let ExecutableResult { data, exit_reason, @@ -656,11 +656,11 @@ where other => error_on_execution_failure(&other, &data)?, }; - #[cfg(not(feature = "rpc_binary_search_estimate"))] + #[cfg(not(feature = "rpc-binary-search-estimate"))] { Ok(used_gas) } - #[cfg(feature = "rpc_binary_search_estimate")] + #[cfg(feature = "rpc-binary-search-estimate")] { // On binary search, evm estimate mode is disabled let estimate_mode = false; diff --git a/template/README.md b/template/README.md index 3a9592a433..3f8cdab8b8 100644 --- a/template/README.md +++ b/template/README.md @@ -38,7 +38,9 @@ The node also supports to use manual seal (to produce block manually through RPC This is also used by the ts-tests: ``` -$ ./target/release/frontier-template-node --dev --manual-seal +$ ./target/release/frontier-template-node --dev --sealing=manual +# Or +$ ./target/release/frontier-template-node --dev --sealing=instant ``` ### Docker Based Development diff --git a/template/node/Cargo.toml b/template/node/Cargo.toml index 73016f49ad..84aa127aa9 100644 --- a/template/node/Cargo.toml +++ b/template/node/Cargo.toml @@ -14,28 +14,30 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1" -clap = { version = "4.0", features = ["derive"] } +clap = { version = "4.1", features = ["derive", "deprecated"] } futures = "0.3.25" jsonrpsee = { workspace = true, features = ["server", "macros"] } log = "0.4.17" scale-codec = { package = "parity-scale-codec", workspace = true } +serde = { workspace = true } # Substrate +prometheus-endpoint = { package = "substrate-prometheus-endpoint", workspace = true } sc-basic-authorship = { workspace = true } sc-cli = { workspace = true } sc-client-api = { workspace = true } sc-consensus = { workspace = true } -sc-consensus-aura = { workspace = true, optional = true } -sc-consensus-manual-seal = { workspace = true, optional = true } +sc-consensus-aura = { workspace = true } +sc-consensus-manual-seal = { workspace = true } sc-executor = { workspace = true } sc-finality-grandpa = { workspace = true } -sc-keystore = { workspace = true } sc-network = { workspace = true } sc-rpc = { workspace = true } sc-rpc-api = { workspace = true } sc-service = { workspace = true } sc-telemetry = { workspace = true } sc-transaction-pool = { workspace = true } +sc-transaction-pool-api = { workspace = true } sp-api = { workspace = true, features = ["std"] } sp-block-builder = { workspace = true } sp-blockchain = { workspace = true } @@ -44,10 +46,17 @@ sp-core = { workspace = true, features = ["std"] } sp-finality-grandpa = { workspace = true, features = ["std"] } sp-inherents = { workspace = true, features = ["std"] } sp-keyring = { workspace = true } +sp-offchain = { workspace = true, features = ["std"] } sp-runtime = { workspace = true, features = ["std"] } +sp-session = { workspace = true, features = ["std"] } +sp-state-machine = { workspace = true, features = ["std"] } sp-timestamp = { workspace = true, features = ["std"] } +sp-transaction-pool = { workspace = true, features = ["std"] } +sp-trie = { workspace = true, features = ["std"] } # These dependencies are used for RPC +frame-system-rpc-runtime-api = { workspace = true } pallet-transaction-payment-rpc = { workspace = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true } substrate-frame-rpc-system = { workspace = true } # These dependencies are used for runtime benchmarking frame-benchmarking = { workspace = true, optional = true } @@ -72,18 +81,10 @@ frontier-template-runtime = { workspace = true, features = ["std"] } substrate-build-script-utils = { workspace = true } [features] -default = ["aura", "with-rocksdb-weights"] -aura = [ - "sc-consensus-aura", - "frontier-template-runtime/aura", -] -manual-seal = [ - "sc-consensus-manual-seal", - "frontier-template-runtime/manual-seal", -] +default = ["with-rocksdb-weights"] with-rocksdb-weights = ["frontier-template-runtime/with-rocksdb-weights"] with-paritydb-weights = ["frontier-template-runtime/with-paritydb-weights"] -rpc_binary_search_estimate = ["fc-rpc/rpc_binary_search_estimate"] +rpc-binary-search-estimate = ["fc-rpc/rpc-binary-search-estimate"] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-benchmarking-cli/runtime-benchmarks", diff --git a/template/node/src/benchmarking.rs b/template/node/src/benchmarking.rs index e93e189689..d0d5b5d584 100644 --- a/template/node/src/benchmarking.rs +++ b/template/node/src/benchmarking.rs @@ -32,18 +32,18 @@ use sp_runtime::{generic::Era, AccountId32, OpaqueExtrinsic, SaturatedConversion // Frontier use frontier_template_runtime::{self as runtime, AccountId, Balance, BalancesCall, SystemCall}; -use crate::service::FullClient; +use crate::client::Client; /// Generates extrinsics for the `benchmark overhead` command. /// /// Note: Should only be used for benchmarking. pub struct RemarkBuilder { - client: Arc, + client: Arc, } impl RemarkBuilder { /// Creates a new [`Self`] from the given client. - pub fn new(client: Arc) -> Self { + pub fn new(client: Arc) -> Self { Self { client } } } @@ -75,14 +75,14 @@ impl frame_benchmarking_cli::ExtrinsicBuilder for RemarkBuilder { /// /// Note: Should only be used for benchmarking. pub struct TransferKeepAliveBuilder { - client: Arc, + client: Arc, dest: AccountId, value: Balance, } impl TransferKeepAliveBuilder { /// Creates a new [`Self`] from the given client. - pub fn new(client: Arc, dest: AccountId, value: Balance) -> Self { + pub fn new(client: Arc, dest: AccountId, value: Balance) -> Self { Self { client, dest, @@ -122,7 +122,7 @@ impl frame_benchmarking_cli::ExtrinsicBuilder for TransferKeepAliveBuilder { /// /// Note: Should only be used for benchmarking. pub fn create_benchmark_extrinsic( - client: &FullClient, + client: &Client, sender: sr25519::Pair, call: runtime::RuntimeCall, nonce: u32, diff --git a/template/node/src/chain_spec.rs b/template/node/src/chain_spec.rs index 154419f326..b4b1ffe392 100644 --- a/template/node/src/chain_spec.rs +++ b/template/node/src/chain_spec.rs @@ -1,12 +1,17 @@ use std::{collections::BTreeMap, str::FromStr}; +use serde::{Deserialize, Serialize}; +// Substrate use sc_service::ChainType; use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_core::{sr25519, Pair, Public, H160, U256}; +use sp_core::{sr25519, storage::Storage, Pair, Public, H160, U256}; use sp_finality_grandpa::AuthorityId as GrandpaId; use sp_runtime::traits::{IdentifyAccount, Verify}; - -use frontier_template_runtime::{AccountId, GenesisConfig, Signature, WASM_BINARY}; +use sp_state_machine::BasicExternalities; +// Frontier +use frontier_template_runtime::{ + AccountId, EnableManualSeal, GenesisConfig, Signature, WASM_BINARY, +}; // The URL for the telemetry server. // const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/"; @@ -14,6 +19,29 @@ use frontier_template_runtime::{AccountId, GenesisConfig, Signature, WASM_BINARY /// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type. pub type ChainSpec = sc_service::GenericChainSpec; +/// Specialized `ChainSpec` for development. +pub type DevChainSpec = sc_service::GenericChainSpec; + +/// Extension for the dev genesis config to support a custom changes to the genesis state. +#[derive(Serialize, Deserialize)] +pub struct DevGenesisExt { + /// Genesis config. + genesis_config: GenesisConfig, + /// The flag that if enable manual-seal mode. + enable_manual_seal: Option, +} + +impl sp_runtime::BuildStorage for DevGenesisExt { + fn assimilate_storage(&self, storage: &mut Storage) -> Result<(), String> { + BasicExternalities::execute_with_storage(storage, || { + if let Some(enable_manual_seal) = &self.enable_manual_seal { + EnableManualSeal::set(enable_manual_seal); + } + }); + self.genesis_config.assimilate_storage(storage) + } +} + /// Generate a crypto pair from seed. pub fn get_from_seed(seed: &str) -> ::Public { TPublic::Pair::from_string(&format!("//{}", seed), None) @@ -36,31 +64,34 @@ pub fn authority_keys_from_seed(s: &str) -> (AuraId, GrandpaId) { (get_from_seed::(s), get_from_seed::(s)) } -pub fn development_config() -> Result { - let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; +pub fn development_config(enable_manual_seal: Option) -> DevChainSpec { + let wasm_binary = WASM_BINARY.expect("WASM not available"); - Ok(ChainSpec::from_genesis( + DevChainSpec::from_genesis( // Name "Development", // ID "dev", ChainType::Development, move || { - testnet_genesis( - wasm_binary, - // Sudo account - get_account_id_from_seed::("Alice"), - // Pre-funded accounts - vec![ + DevGenesisExt { + genesis_config: testnet_genesis( + wasm_binary, + // Sudo account get_account_id_from_seed::("Alice"), - get_account_id_from_seed::("Bob"), - get_account_id_from_seed::("Alice//stash"), - get_account_id_from_seed::("Bob//stash"), - ], - // Initial PoA authorities - vec![authority_keys_from_seed("Alice")], - 42, - ) + // Pre-funded accounts + vec![ + get_account_id_from_seed::("Alice"), + get_account_id_from_seed::("Bob"), + get_account_id_from_seed::("Alice//stash"), + get_account_id_from_seed::("Bob//stash"), + ], + // Initial PoA authorities + vec![authority_keys_from_seed("Alice")], + 42, + ), + enable_manual_seal, + } }, // Bootnodes vec![], @@ -73,13 +104,13 @@ pub fn development_config() -> Result { None, // Extensions None, - )) + ) } -pub fn local_testnet_config() -> Result { - let wasm_binary = WASM_BINARY.ok_or_else(|| "Development wasm not available".to_string())?; +pub fn local_testnet_config() -> ChainSpec { + let wasm_binary = WASM_BINARY.expect("WASM not available"); - Ok(ChainSpec::from_genesis( + ChainSpec::from_genesis( // Name "Local Testnet", // ID @@ -124,7 +155,7 @@ pub fn local_testnet_config() -> Result { None, // Extensions None, - )) + ) } /// Configure initial storage state for FRAME modules. diff --git a/template/node/src/cli.rs b/template/node/src/cli.rs index 7843135674..e800e2e42e 100644 --- a/template/node/src/cli.rs +++ b/template/node/src/cli.rs @@ -1,5 +1,6 @@ +use crate::service::EthConfiguration; + /// Available Sealing methods. -#[cfg(feature = "manual-seal")] #[derive(Debug, Copy, Clone, clap::ValueEnum)] pub enum Sealing { // Seal using rpc method. @@ -8,48 +9,27 @@ pub enum Sealing { Instant, } -#[cfg(feature = "manual-seal")] impl Default for Sealing { fn default() -> Sealing { Sealing::Manual } } -#[allow(missing_docs)] #[derive(Debug, clap::Parser)] -pub struct RunCmd { +pub struct Cli { + #[command(subcommand)] + pub subcommand: Option, + #[allow(missing_docs)] #[command(flatten)] - pub base: sc_cli::RunCmd, + pub run: sc_cli::RunCmd, /// Choose sealing method. - #[cfg(feature = "manual-seal")] #[arg(long, value_enum, ignore_case = true)] - pub sealing: Sealing, - - #[arg(long)] - pub enable_dev_signer: bool, - - /// Maximum number of logs in a query. - #[arg(long, default_value = "10000")] - pub max_past_logs: u32, - - /// Maximum fee history cache size. - #[arg(long, default_value = "2048")] - pub fee_history_limit: u64, - - /// The dynamic-fee pallet target gas price set by block author - #[arg(long, default_value = "1")] - pub target_gas_price: u64, -} - -#[derive(Debug, clap::Parser)] -pub struct Cli { - #[command(subcommand)] - pub subcommand: Option, + pub sealing: Option, #[command(flatten)] - pub run: RunCmd, + pub eth: EthConfiguration, } #[derive(Debug, clap::Subcommand)] diff --git a/template/node/src/client.rs b/template/node/src/client.rs new file mode 100644 index 0000000000..5b58b71eef --- /dev/null +++ b/template/node/src/client.rs @@ -0,0 +1,86 @@ +// Substrate +use sc_executor::{NativeElseWasmExecutor, NativeExecutionDispatch, NativeVersion}; +use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_runtime::traits::BlakeTwo256; +// Local +use frontier_template_runtime::{opaque::Block, AccountId, Balance, Index}; + +use crate::eth::EthCompatRuntimeApiCollection; + +/// Full backend. +pub type FullBackend = sc_service::TFullBackend; +/// Full client. +pub type FullClient = + sc_service::TFullClient>; + +pub type Client = FullClient; + +/// Only enable the benchmarking host functions when we actually want to benchmark. +#[cfg(feature = "runtime-benchmarks")] +pub type HostFunctions = frame_benchmarking::benchmarking::HostFunctions; +/// Otherwise we use empty host functions for ext host functions. +#[cfg(not(feature = "runtime-benchmarks"))] +pub type HostFunctions = (); + +pub struct TemplateRuntimeExecutor; +impl NativeExecutionDispatch for TemplateRuntimeExecutor { + type ExtendHostFunctions = HostFunctions; + + fn dispatch(method: &str, data: &[u8]) -> Option> { + frontier_template_runtime::api::dispatch(method, data) + } + + fn native_version() -> NativeVersion { + frontier_template_runtime::native_version() + } +} + +/// A set of APIs that every runtimes must implement. +pub trait BaseRuntimeApiCollection: + sp_api::ApiExt + + sp_api::Metadata + + sp_block_builder::BlockBuilder + + sp_offchain::OffchainWorkerApi + + sp_session::SessionKeys + + sp_transaction_pool::runtime_api::TaggedTransactionQueue +where + >::StateBackend: sp_api::StateBackend, +{ +} + +impl BaseRuntimeApiCollection for Api +where + Api: sp_api::ApiExt + + sp_api::Metadata + + sp_block_builder::BlockBuilder + + sp_offchain::OffchainWorkerApi + + sp_session::SessionKeys + + sp_transaction_pool::runtime_api::TaggedTransactionQueue, + >::StateBackend: sp_api::StateBackend, +{ +} + +/// A set of APIs that template runtime must implement. +pub trait RuntimeApiCollection: + BaseRuntimeApiCollection + + EthCompatRuntimeApiCollection + + sp_consensus_aura::AuraApi + + sp_finality_grandpa::GrandpaApi + + frame_system_rpc_runtime_api::AccountNonceApi + + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi +where + >::StateBackend: sp_api::StateBackend, +{ +} + +impl RuntimeApiCollection for Api +where + Api: BaseRuntimeApiCollection + + EthCompatRuntimeApiCollection + + sp_consensus_aura::AuraApi + + sp_finality_grandpa::GrandpaApi + + frame_system_rpc_runtime_api::AccountNonceApi + + pallet_transaction_payment_rpc_runtime_api::TransactionPaymentApi, + >::StateBackend: sp_api::StateBackend, +{ +} diff --git a/template/node/src/command.rs b/template/node/src/command.rs index 412f453a8b..9b4a271e12 100644 --- a/template/node/src/command.rs +++ b/template/node/src/command.rs @@ -18,7 +18,7 @@ use clap::Parser; // Substrate use sc_cli::{ChainSpec, RuntimeVersion, SubstrateCli}; -use sc_service::{DatabaseSource, PartialComponents}; +use sc_service::DatabaseSource; // Frontier use fc_db::frontier_database_dir; @@ -53,10 +53,13 @@ impl SubstrateCli for Cli { 2021 } - fn load_spec(&self, id: &str) -> Result, String> { + fn load_spec(&self, id: &str) -> Result, String> { Ok(match id { - "dev" => Box::new(chain_spec::development_config()?), - "" | "local" => Box::new(chain_spec::local_testnet_config()?), + "dev" => { + let enable_manual_seal = self.sealing.map(|_| true); + Box::new(chain_spec::development_config(enable_manual_seal)) + } + "" | "local" => Box::new(chain_spec::local_testnet_config()), path => Box::new(chain_spec::ChainSpec::from_json_file( std::path::PathBuf::from(path), )?), @@ -80,47 +83,33 @@ pub fn run() -> sc_cli::Result<()> { } Some(Subcommand::CheckBlock(cmd)) => { let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { - client, - task_manager, - import_queue, - .. - } = service::new_partial(&config, &cli)?; + runner.async_run(|mut config| { + let (client, _, import_queue, task_manager, _) = + service::new_chain_ops(&mut config, &cli.eth)?; Ok((cmd.run(client, import_queue), task_manager)) }) } Some(Subcommand::ExportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { - client, - task_manager, - .. - } = service::new_partial(&config, &cli)?; + runner.async_run(|mut config| { + let (client, _, _, task_manager, _) = + service::new_chain_ops(&mut config, &cli.eth)?; Ok((cmd.run(client, config.database), task_manager)) }) } Some(Subcommand::ExportState(cmd)) => { let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { - client, - task_manager, - .. - } = service::new_partial(&config, &cli)?; + runner.async_run(|mut config| { + let (client, _, _, task_manager, _) = + service::new_chain_ops(&mut config, &cli.eth)?; Ok((cmd.run(client, config.chain_spec), task_manager)) }) } Some(Subcommand::ImportBlocks(cmd)) => { let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { - client, - task_manager, - import_queue, - .. - } = service::new_partial(&config, &cli)?; + runner.async_run(|mut config| { + let (client, _, import_queue, task_manager, _) = + service::new_chain_ops(&mut config, &cli.eth)?; Ok((cmd.run(client, import_queue), task_manager)) }) } @@ -147,13 +136,9 @@ pub fn run() -> sc_cli::Result<()> { } Some(Subcommand::Revert(cmd)) => { let runner = cli.create_runner(cmd)?; - runner.async_run(|config| { - let PartialComponents { - client, - task_manager, - backend, - .. - } = service::new_partial(&config, &cli)?; + runner.async_run(|mut config| { + let (client, backend, _, task_manager, _) = + service::new_chain_ops(&mut config, &cli.eth)?; let aux_revert = Box::new(move |client, _, blocks| { sc_finality_grandpa::revert(client, blocks)?; Ok(()) @@ -173,26 +158,21 @@ pub fn run() -> sc_cli::Result<()> { let runner = cli.create_runner(cmd)?; match cmd { - BenchmarkCmd::Pallet(cmd) => { - runner.sync_run(|config| cmd.run::(config)) - } - BenchmarkCmd::Block(cmd) => runner.sync_run(|config| { - let PartialComponents { client, .. } = service::new_partial(&config, &cli)?; + BenchmarkCmd::Pallet(cmd) => runner + .sync_run(|config| cmd.run::(config)), + BenchmarkCmd::Block(cmd) => runner.sync_run(|mut config| { + let (client, _, _, _, _) = service::new_chain_ops(&mut config, &cli.eth)?; cmd.run(client) }), - BenchmarkCmd::Storage(cmd) => runner.sync_run(|config| { - let PartialComponents { - client, backend, .. - } = service::new_partial(&config, &cli)?; + BenchmarkCmd::Storage(cmd) => runner.sync_run(|mut config| { + let (client, backend, _, _, _) = service::new_chain_ops(&mut config, &cli.eth)?; let db = backend.expose_db(); let storage = backend.expose_storage(); - cmd.run(config, client, db, storage) }), - BenchmarkCmd::Overhead(cmd) => runner.sync_run(|config| { - let PartialComponents { client, .. } = service::new_partial(&config, &cli)?; + BenchmarkCmd::Overhead(cmd) => runner.sync_run(|mut config| { + let (client, _, _, _, _) = service::new_chain_ops(&mut config, &cli.eth)?; let ext_builder = RemarkBuilder::new(client.clone()); - cmd.run( config, client, @@ -201,8 +181,8 @@ pub fn run() -> sc_cli::Result<()> { &ext_builder, ) }), - BenchmarkCmd::Extrinsic(cmd) => runner.sync_run(|config| { - let PartialComponents { client, .. } = service::new_partial(&config, &cli)?; + BenchmarkCmd::Extrinsic(cmd) => runner.sync_run(|mut config| { + let (client, _, _, _, _) = service::new_chain_ops(&mut config, &cli.eth)?; // Register the *Remark* and *TKA* builders. let ext_factory = ExtrinsicFactory(vec![ Box::new(RemarkBuilder::new(client.clone())), @@ -226,16 +206,16 @@ pub fn run() -> sc_cli::Result<()> { .into()), Some(Subcommand::FrontierDb(cmd)) => { let runner = cli.create_runner(cmd)?; - runner.sync_run(|config| { - let PartialComponents { client, other, .. } = service::new_partial(&config, &cli)?; - let frontier_backend = other.2; - cmd.run::<_, frontier_template_runtime::opaque::Block>(client, frontier_backend) + runner.sync_run(|mut config| { + let (client, _, _, _, frontier_backend) = + service::new_chain_ops(&mut config, &cli.eth)?; + cmd.run(client, frontier_backend) }) } None => { - let runner = cli.create_runner(&cli.run.base)?; + let runner = cli.create_runner(&cli.run)?; runner.run_node_until_exit(|config| async move { - service::new_full(config, &cli).map_err(sc_cli::Error::Service) + service::build_full(config, cli.eth, cli.sealing).map_err(Into::into) }) } } diff --git a/template/node/src/eth.rs b/template/node/src/eth.rs new file mode 100644 index 0000000000..4bae5d9099 --- /dev/null +++ b/template/node/src/eth.rs @@ -0,0 +1,161 @@ +use std::{ + collections::BTreeMap, + path::PathBuf, + sync::{Arc, Mutex}, + time::Duration, +}; + +use futures::{future, prelude::*}; +// Substrate +use sc_client_api::{BlockchainEvents, StateBackendFor}; +use sc_executor::NativeExecutionDispatch; +use sc_service::{error::Error as ServiceError, BasePath, Configuration, TaskManager}; +use sp_api::ConstructRuntimeApi; +use sp_runtime::traits::BlakeTwo256; +// Frontier +pub use fc_consensus::FrontierBlockImport; +pub use fc_db::frontier_database_dir; +use fc_mapping_sync::{MappingSyncWorker, SyncStrategy}; +use fc_rpc::{EthTask, OverrideHandle}; +pub use fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool}; +// Local +use frontier_template_runtime::opaque::Block; + +use crate::client::{FullBackend, FullClient}; + +/// Frontier DB backend type. +pub type FrontierBackend = fc_db::Backend; + +pub fn db_config_dir(config: &Configuration) -> PathBuf { + let application = &config.impl_name; + config + .base_path + .as_ref() + .map(|base_path| base_path.config_dir(config.chain_spec.id())) + .unwrap_or_else(|| { + BasePath::from_project("", "", application).config_dir(config.chain_spec.id()) + }) +} + +/// The ethereum-compatibility configuration used to run a node. +#[derive(Clone, Debug, clap::Parser)] +pub struct EthConfiguration { + /// Maximum number of logs in a query. + #[arg(long, default_value = "10000")] + pub max_past_logs: u32, + + /// Maximum fee history cache size. + #[arg(long, default_value = "2048")] + pub fee_history_limit: u64, + + #[arg(long)] + pub enable_dev_signer: bool, + + /// The dynamic-fee pallet target gas price set by block author + #[arg(long, default_value = "1")] + pub target_gas_price: u64, + + /// Maximum allowed gas limit will be `block.gas_limit * execute_gas_limit_multiplier` + /// when using eth_call/eth_estimateGas. + #[arg(long, default_value = "10")] + pub execute_gas_limit_multiplier: u64, + + /// Size in bytes of the LRU cache for block data. + #[arg(long, default_value = "50")] + pub eth_log_block_cache: usize, + + /// Size in bytes of the LRU cache for transactions statuses data. + #[arg(long, default_value = "50")] + pub eth_statuses_cache: usize, +} + +pub struct FrontierPartialComponents { + pub filter_pool: Option, + pub fee_history_cache: FeeHistoryCache, + pub fee_history_cache_limit: FeeHistoryCacheLimit, +} + +pub fn new_frontier_partial( + config: &EthConfiguration, +) -> Result { + Ok(FrontierPartialComponents { + filter_pool: Some(Arc::new(Mutex::new(BTreeMap::new()))), + fee_history_cache: Arc::new(Mutex::new(BTreeMap::new())), + fee_history_cache_limit: config.fee_history_limit, + }) +} + +/// A set of APIs that ethereum-compatible runtimes must implement. +pub trait EthCompatRuntimeApiCollection: + sp_api::ApiExt + + fp_rpc::EthereumRuntimeRPCApi + + fp_rpc::ConvertTransactionRuntimeApi +where + >::StateBackend: sp_api::StateBackend, +{ +} + +impl EthCompatRuntimeApiCollection for Api +where + Api: sp_api::ApiExt + + fp_rpc::EthereumRuntimeRPCApi + + fp_rpc::ConvertTransactionRuntimeApi, + >::StateBackend: sp_api::StateBackend, +{ +} + +pub fn spawn_frontier_tasks( + task_manager: &TaskManager, + client: Arc>, + backend: Arc, + frontier_backend: Arc, + filter_pool: Option, + overrides: Arc>, + fee_history_cache: FeeHistoryCache, + fee_history_cache_limit: FeeHistoryCacheLimit, +) where + RuntimeApi: ConstructRuntimeApi>, + RuntimeApi: Send + Sync + 'static, + RuntimeApi::RuntimeApi: + EthCompatRuntimeApiCollection>, + Executor: NativeExecutionDispatch + 'static, +{ + task_manager.spawn_essential_handle().spawn( + "frontier-mapping-sync-worker", + Some("frontier"), + MappingSyncWorker::new( + client.import_notification_stream(), + Duration::new(6, 0), + client.clone(), + backend, + frontier_backend, + 3, + 0, + SyncStrategy::Normal, + ) + .for_each(|()| future::ready(())), + ); + + // Spawn Frontier EthFilterApi maintenance task. + if let Some(filter_pool) = filter_pool { + // Each filter is allowed to stay in the pool for 100 blocks. + const FILTER_RETAIN_THRESHOLD: u64 = 100; + task_manager.spawn_essential_handle().spawn( + "frontier-filter-pool", + Some("frontier"), + EthTask::filter_pool_task(client.clone(), filter_pool, FILTER_RETAIN_THRESHOLD), + ); + } + + // Spawn Frontier FeeHistory cache maintenance task. + task_manager.spawn_essential_handle().spawn( + "frontier-fee-history", + Some("frontier"), + EthTask::fee_history_task( + client, + overrides, + fee_history_cache, + fee_history_cache_limit, + ), + ); +} diff --git a/template/node/src/main.rs b/template/node/src/main.rs index 3c57320f53..b00d6c4b35 100644 --- a/template/node/src/main.rs +++ b/template/node/src/main.rs @@ -7,7 +7,9 @@ mod benchmarking; mod chain_spec; mod cli; +mod client; mod command; +mod eth; mod rpc; mod service; diff --git a/template/node/src/rpc.rs b/template/node/src/rpc/eth.rs similarity index 52% rename from template/node/src/rpc.rs rename to template/node/src/rpc/eth.rs index b73cce25dc..b71431c4e1 100644 --- a/template/node/src/rpc.rs +++ b/template/node/src/rpc/eth.rs @@ -1,5 +1,3 @@ -//! A collection of node-specific RPC methods. - use std::{collections::BTreeMap, sync::Arc}; use jsonrpsee::RpcModule; @@ -8,73 +6,88 @@ use sc_client_api::{ backend::{AuxStore, Backend, StateBackend, StorageProvider}, client::BlockchainEvents, }; -#[cfg(feature = "manual-seal")] -use sc_consensus_manual_seal::rpc::{ManualSeal, ManualSealApiServer}; use sc_network::NetworkService; use sc_rpc::SubscriptionTaskExecutor; -use sc_rpc_api::DenyUnsafe; -use sc_service::TransactionPool; use sc_transaction_pool::{ChainApi, Pool}; +use sc_transaction_pool_api::TransactionPool; use sp_api::ProvideRuntimeApi; -use sp_block_builder::BlockBuilder; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; -use sp_runtime::traits::BlakeTwo256; +use sp_core::H256; +use sp_runtime::traits::{BlakeTwo256, Block as BlockT, Header as HeaderT}; // Frontier -use fc_rpc::{ +use fc_db::Backend as FrontierBackend; +pub use fc_rpc::{ EthBlockDataCacheTask, OverrideHandle, RuntimeApiStorageOverride, SchemaV1Override, SchemaV2Override, SchemaV3Override, StorageOverride, }; -use fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool}; +pub use fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool}; use fp_storage::EthereumStorageSchema; -// Runtime -use frontier_template_runtime::{opaque::Block, AccountId, Balance, Hash, Index}; -/// Full client dependencies. -pub struct FullDeps { +/// Extra dependencies for Ethereum compatibility. +pub struct EthDeps { /// The client instance to use. pub client: Arc, /// Transaction pool instance. pub pool: Arc

, /// Graph pool instance. pub graph: Arc>, - /// Whether to deny unsafe calls - pub deny_unsafe: DenyUnsafe, + /// Ethereum transaction converter. + pub converter: Option, /// The Node authority flag pub is_authority: bool, /// Whether to enable dev signer pub enable_dev_signer: bool, /// Network service - pub network: Arc>, + pub network: Arc>, + /// Frontier Backend. + pub frontier_backend: Arc>, + /// Ethereum data access overrides. + pub overrides: Arc>, + /// Cache for Ethereum block data. + pub block_data_cache: Arc>, /// EthFilterApi pool. pub filter_pool: Option, - /// Backend. - pub backend: Arc>, /// Maximum number of logs in a query. pub max_past_logs: u32, /// Fee history cache. pub fee_history_cache: FeeHistoryCache, /// Maximum fee history cache size. pub fee_history_cache_limit: FeeHistoryCacheLimit, - /// Ethereum data access overrides. - pub overrides: Arc>, - /// Cache for Ethereum block data. - pub block_data_cache: Arc>, - /// Manual seal command sink - #[cfg(feature = "manual-seal")] - pub command_sink: - Option>>, + /// Maximum allowed gas limit will be ` block.gas_limit * execute_gas_limit_multiplier` when + /// using eth_call/eth_estimateGas. + pub execute_gas_limit_multiplier: u64, } -pub fn overrides_handle(client: Arc) -> Arc> +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(), + 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, + } + } +} + +pub fn overrides_handle(client: Arc) -> Arc> where - C: ProvideRuntimeApi + StorageProvider + AuxStore, - C: HeaderBackend + HeaderMetadata, - C: Send + Sync + 'static, - C::Api: sp_api::ApiExt - + fp_rpc::EthereumRuntimeRPCApi - + fp_rpc::ConvertTransactionRuntimeApi, - BE: Backend + 'static, + C: ProvideRuntimeApi + StorageProvider + AuxStore, + C: HeaderBackend + HeaderMetadata + 'static, + C::Api: fp_rpc::EthereumRuntimeRPCApi, + BE: Backend + 'static, BE::State: StateBackend, + B: BlockT, { let mut overrides_map = BTreeMap::new(); overrides_map.insert( @@ -95,60 +108,54 @@ where Arc::new(OverrideHandle { schemas: overrides_map, - fallback: Box::new(RuntimeApiStorageOverride::new(client)), + fallback: Box::new(RuntimeApiStorageOverride::::new(client)), }) } -/// Instantiate all Full RPC extensions. -pub fn create_full( - deps: FullDeps, +/// Instantiate Ethereum-compatible RPC extensions. +pub fn create_eth( + mut io: RpcModule<()>, + deps: EthDeps, subscription_task_executor: SubscriptionTaskExecutor, ) -> Result, Box> where - BE: Backend + 'static, + C: ProvideRuntimeApi + StorageProvider + AuxStore, + C: BlockchainEvents, + C: HeaderBackend + HeaderMetadata + 'static, + C::Api: sp_block_builder::BlockBuilder, + C::Api: fp_rpc::EthereumRuntimeRPCApi, + C::Api: fp_rpc::ConvertTransactionRuntimeApi, + BE: Backend + 'static, BE::State: StateBackend, - C: ProvideRuntimeApi + StorageProvider + AuxStore, - C: BlockchainEvents, - C: HeaderBackend + HeaderMetadata, - C: Send + Sync + 'static, - C::Api: substrate_frame_rpc_system::AccountNonceApi, - C::Api: BlockBuilder, - C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, - C::Api: fp_rpc::ConvertTransactionRuntimeApi, - C::Api: fp_rpc::EthereumRuntimeRPCApi, - P: TransactionPool + 'static, - A: ChainApi + 'static, + P: TransactionPool + 'static, + A: ChainApi + 'static, + CT: fp_rpc::ConvertTransaction<::Extrinsic> + Send + Sync + 'static, + B: BlockT, + B::Header: HeaderT, { use fc_rpc::{ Eth, EthApiServer, EthDevSigner, EthFilter, EthFilterApiServer, EthPubSub, EthPubSubApiServer, EthSigner, Net, NetApiServer, Web3, Web3ApiServer, }; - use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; - use substrate_frame_rpc_system::{System, SystemApiServer}; - let mut io = RpcModule::new(()); - let FullDeps { + let EthDeps { client, pool, graph, - deny_unsafe, + converter, is_authority, enable_dev_signer, network, + frontier_backend, + overrides, + block_data_cache, filter_pool, - backend, max_past_logs, fee_history_cache, fee_history_cache_limit, - overrides, - block_data_cache, - #[cfg(feature = "manual-seal")] - command_sink, + execute_gas_limit_multiplier, } = deps; - io.merge(System::new(client.clone(), pool.clone(), deny_unsafe).into_rpc())?; - io.merge(TransactionPayment::new(client.clone()).into_rpc())?; - let mut signers = Vec::new(); if enable_dev_signer { signers.push(Box::new(EthDevSigner::new()) as Box); @@ -159,17 +166,16 @@ where client.clone(), pool.clone(), graph, - Some(frontier_template_runtime::TransactionConverter), + converter, network.clone(), - signers, + vec![], overrides.clone(), - backend.clone(), - // Is authority. + frontier_backend.clone(), is_authority, block_data_cache.clone(), fee_history_cache, fee_history_cache_limit, - 10, + execute_gas_limit_multiplier, ) .into_rpc(), )?; @@ -178,7 +184,7 @@ where io.merge( EthFilter::new( client.clone(), - backend, + frontier_backend, filter_pool, 500_usize, // max stored filters max_past_logs, @@ -211,14 +217,5 @@ where io.merge(Web3::new(client).into_rpc())?; - #[cfg(feature = "manual-seal")] - if let Some(command_sink) = command_sink { - io.merge( - // We provide the rpc handler with the sending end of the channel to allow the rpc - // send EngineCommands to the background block authorship task. - ManualSeal::new(command_sink).into_rpc(), - )?; - } - Ok(io) } diff --git a/template/node/src/rpc/mod.rs b/template/node/src/rpc/mod.rs new file mode 100644 index 0000000000..e5e0165056 --- /dev/null +++ b/template/node/src/rpc/mod.rs @@ -0,0 +1,90 @@ +//! A collection of node-specific RPC methods. + +use std::sync::Arc; + +use futures::channel::mpsc; +use jsonrpsee::RpcModule; +// Substrate +use sc_client_api::{ + backend::{AuxStore, Backend, StateBackend, StorageProvider}, + client::BlockchainEvents, +}; +use sc_consensus_manual_seal::rpc::EngineCommand; +use sc_rpc::SubscriptionTaskExecutor; +use sc_rpc_api::DenyUnsafe; +use sc_service::TransactionPool; +use sc_transaction_pool::ChainApi; +use sp_api::ProvideRuntimeApi; +use sp_block_builder::BlockBuilder; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sp_runtime::traits::{BlakeTwo256, Block as BlockT}; +// Runtime +use frontier_template_runtime::{opaque::Block, AccountId, Balance, Hash, Index}; + +mod eth; +pub use self::eth::{create_eth, overrides_handle, EthDeps}; + +/// Full client dependencies. +pub struct FullDeps { + /// The client instance to use. + pub client: Arc, + /// Transaction pool instance. + pub pool: Arc

, + /// Whether to deny unsafe calls + pub deny_unsafe: DenyUnsafe, + /// Manual seal command sink + pub command_sink: Option>>, + /// Ethereum-compatibility specific dependencies. + pub eth: EthDeps, +} + +/// Instantiate all Full RPC extensions. +pub fn create_full( + deps: FullDeps, + subscription_task_executor: SubscriptionTaskExecutor, +) -> Result, Box> +where + BE: Backend + 'static, + BE::State: StateBackend, + C: ProvideRuntimeApi + StorageProvider + AuxStore, + C: BlockchainEvents, + C: HeaderBackend + HeaderMetadata, + C: Send + Sync + 'static, + C::Api: substrate_frame_rpc_system::AccountNonceApi, + C::Api: BlockBuilder, + C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, + C::Api: fp_rpc::ConvertTransactionRuntimeApi, + C::Api: fp_rpc::EthereumRuntimeRPCApi, + P: TransactionPool + 'static, + A: ChainApi + 'static, + CT: fp_rpc::ConvertTransaction<::Extrinsic> + Send + Sync + 'static, +{ + use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; + use sc_consensus_manual_seal::rpc::{ManualSeal, ManualSealApiServer}; + use substrate_frame_rpc_system::{System, SystemApiServer}; + + let mut io = RpcModule::new(()); + let FullDeps { + client, + pool, + deny_unsafe, + command_sink, + eth, + } = deps; + + io.merge(System::new(client.clone(), pool, deny_unsafe).into_rpc())?; + io.merge(TransactionPayment::new(client).into_rpc())?; + + if let Some(command_sink) = command_sink { + io.merge( + // We provide the rpc handler with the sending end of the channel to allow the rpc + // send EngineCommands to the background block authorship task. + ManualSeal::new(command_sink).into_rpc(), + )?; + } + + // Ethereum compatibility RPCs + let io = create_eth::<_, _, _, _, _, _>(io, eth, subscription_task_executor)?; + + Ok(io) +} diff --git a/template/node/src/service.rs b/template/node/src/service.rs index d45de6f7f9..20fba528f9 100644 --- a/template/node/src/service.rs +++ b/template/node/src/service.rs @@ -1,112 +1,87 @@ //! Service and ServiceFactory implementation. Specialized wrapper over substrate service. -use std::{ - collections::BTreeMap, - path::PathBuf, - sync::{Arc, Mutex}, - time::Duration, -}; +use std::{cell::RefCell, sync::Arc, time::Duration}; -use futures::{future, StreamExt}; +use futures::{channel::mpsc, prelude::*}; // Substrate -use sc_cli::SubstrateCli; -use sc_client_api::BlockchainEvents; -use sc_executor::NativeElseWasmExecutor; -use sc_keystore::LocalKeystore; -use sc_service::{error::Error as ServiceError, BasePath, Configuration, TaskManager}; -use sc_telemetry::{Telemetry, TelemetryWorker}; +use prometheus_endpoint::Registry; +use sc_client_api::{BlockBackend, StateBackendFor}; +use sc_consensus::BasicQueue; +use sc_executor::{NativeElseWasmExecutor, NativeExecutionDispatch}; +use sc_service::{error::Error as ServiceError, Configuration, PartialComponents, TaskManager}; +use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker}; +use sp_api::{ConstructRuntimeApi, TransactionFor}; +use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use sp_core::U256; -// Frontier -use fc_consensus::FrontierBlockImport; -use fc_db::Backend as FrontierBackend; -use fc_mapping_sync::{MappingSyncWorker, SyncStrategy}; -use fc_rpc::{EthTask, OverrideHandle}; -use fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool}; +use sp_runtime::traits::BlakeTwo256; +use sp_trie::PrefixedMemoryDB; // Runtime -use frontier_template_runtime::{opaque::Block, RuntimeApi}; - -use crate::cli::Cli; -#[cfg(feature = "manual-seal")] -use crate::cli::Sealing; - -// Our native executor instance. -pub struct ExecutorDispatch; - -impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { - /// Only enable the benchmarking host functions when we actually want to benchmark. - #[cfg(feature = "runtime-benchmarks")] - type ExtendHostFunctions = frame_benchmarking::benchmarking::HostFunctions; - /// Otherwise we only use the default Substrate host functions. - #[cfg(not(feature = "runtime-benchmarks"))] - type ExtendHostFunctions = (); - - fn dispatch(method: &str, data: &[u8]) -> Option> { - frontier_template_runtime::api::dispatch(method, data) - } - - fn native_version() -> sc_executor::NativeVersion { - frontier_template_runtime::native_version() - } -} +use frontier_template_runtime::{opaque::Block, Hash, TransactionConverter}; + +use crate::{ + cli::Sealing, + client::{BaseRuntimeApiCollection, FullBackend, FullClient, RuntimeApiCollection}, + eth::{ + new_frontier_partial, spawn_frontier_tasks, FrontierBackend, FrontierBlockImport, + FrontierPartialComponents, + }, +}; +pub use crate::{ + client::{Client, TemplateRuntimeExecutor}, + eth::{db_config_dir, EthConfiguration}, +}; -pub type FullClient = - sc_service::TFullClient>; -type FullBackend = sc_service::TFullBackend; +type BasicImportQueue = sc_consensus::DefaultImportQueue; +type FullPool = sc_transaction_pool::FullPool; type FullSelectChain = sc_consensus::LongestChain; -#[cfg(feature = "aura")] -pub type ConsensusResult = ( - FrontierBlockImport< - Block, - sc_finality_grandpa::GrandpaBlockImport, - FullClient, - >, - sc_finality_grandpa::LinkHalf, -); - -#[cfg(feature = "manual-seal")] -pub type ConsensusResult = ( - FrontierBlockImport, FullClient>, - Sealing, -); - -pub(crate) fn db_config_dir(config: &Configuration) -> PathBuf { - config - .base_path - .as_ref() - .map(|base_path| base_path.config_dir(config.chain_spec.id())) - .unwrap_or_else(|| { - BasePath::from_project("", "", &Cli::executable_name()) - .config_dir(config.chain_spec.id()) - }) -} +type GrandpaBlockImport = + sc_finality_grandpa::GrandpaBlockImport; +type GrandpaLinkHalf = sc_finality_grandpa::LinkHalf; +type BoxBlockImport = sc_consensus::BoxBlockImport>; -pub fn new_partial( +pub fn new_partial( config: &Configuration, - cli: &Cli, + eth_config: &EthConfiguration, + build_import_queue: BIQ, ) -> Result< - sc_service::PartialComponents< - FullClient, + PartialComponents< + FullClient, FullBackend, FullSelectChain, - sc_consensus::DefaultImportQueue, - sc_transaction_pool::FullPool, + BasicImportQueue>, + FullPool>, ( Option, - ConsensusResult, - Arc>, - Option, - (FeeHistoryCache, FeeHistoryCacheLimit), + BoxBlockImport>, + GrandpaLinkHalf>, + Arc, ), >, ServiceError, -> { - if config.keystore_remote.is_some() { - return Err(ServiceError::Other( - "Remote Keystores are not supported.".to_string(), - )); - } - +> +where + RuntimeApi: ConstructRuntimeApi>, + RuntimeApi: Send + Sync + 'static, + RuntimeApi::RuntimeApi: + BaseRuntimeApiCollection>, + Executor: NativeExecutionDispatch + 'static, + BIQ: FnOnce( + Arc>, + &Configuration, + &EthConfiguration, + &TaskManager, + Option, + GrandpaBlockImport>, + Arc, + ) -> Result< + ( + BasicImportQueue>, + BoxBlockImport>, + ), + ServiceError, + >, +{ let telemetry = config .telemetry_endpoints .clone() @@ -118,7 +93,7 @@ pub fn new_partial( }) .transpose()?; - let executor = NativeElseWasmExecutor::::new( + let executor = NativeElseWasmExecutor::::new( config.wasm_method, config.default_heap_pages, config.max_runtime_instances, @@ -141,6 +116,27 @@ pub fn new_partial( }); let select_chain = sc_consensus::LongestChain::new(backend.clone()); + let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import( + client.clone(), + &(client.clone() as Arc<_>), + select_chain.clone(), + telemetry.as_ref().map(|x| x.handle()), + )?; + + let frontier_backend = Arc::new(FrontierBackend::open( + client.clone(), + &config.database, + &db_config_dir(config), + )?); + let (import_queue, block_import) = build_import_queue( + client.clone(), + config, + eth_config, + &task_manager, + telemetry.as_ref().map(|x| x.handle()), + grandpa_block_import, + frontier_backend.clone(), + )?; let transaction_pool = sc_transaction_pool::BasicPool::new_full( config.transaction_pool.clone(), @@ -150,174 +146,171 @@ pub fn new_partial( client.clone(), ); - let frontier_backend = Arc::new(FrontierBackend::open( - Arc::clone(&client), - &config.database, - &db_config_dir(config), - )?); - let filter_pool: Option = Some(Arc::new(Mutex::new(BTreeMap::new()))); - let fee_history_cache: FeeHistoryCache = Arc::new(Mutex::new(BTreeMap::new())); - let fee_history_cache_limit: FeeHistoryCacheLimit = cli.run.fee_history_limit; - - #[cfg(feature = "aura")] - { - use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; - - let (grandpa_block_import, grandpa_link) = sc_finality_grandpa::block_import( - client.clone(), - &(client.clone() as Arc<_>), - select_chain.clone(), - telemetry.as_ref().map(|x| x.handle()), - )?; + Ok(PartialComponents { + client, + backend, + keystore_container, + task_manager, + select_chain, + import_queue, + transaction_pool, + other: (telemetry, block_import, grandpa_link, frontier_backend), + }) +} - let frontier_block_import = FrontierBlockImport::new( - grandpa_block_import.clone(), - client.clone(), - frontier_backend.clone(), - ); +/// Build the import queue for the template runtime (aura + grandpa). +pub fn build_aura_grandpa_import_queue( + client: Arc>, + config: &Configuration, + eth_config: &EthConfiguration, + task_manager: &TaskManager, + telemetry: Option, + grandpa_block_import: GrandpaBlockImport>, + frontier_backend: Arc, +) -> Result< + ( + BasicImportQueue>, + BoxBlockImport>, + ), + ServiceError, +> +where + RuntimeApi: ConstructRuntimeApi>, + RuntimeApi: Send + Sync + 'static, + RuntimeApi::RuntimeApi: + RuntimeApiCollection>, + Executor: NativeExecutionDispatch + 'static, +{ + let frontier_block_import = FrontierBlockImport::new( + grandpa_block_import.clone(), + client.clone(), + frontier_backend, + ); - let slot_duration = sc_consensus_aura::slot_duration(&*client)?; - let target_gas_price = cli.run.target_gas_price; - let create_inherent_data_providers = move |_, ()| async move { - let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); - let slot = sp_consensus_aura::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + let slot_duration = sc_consensus_aura::slot_duration(&*client)?; + let target_gas_price = eth_config.target_gas_price; + let create_inherent_data_providers = move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + 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)) - }; - - let import_queue = sc_consensus_aura::import_queue::( - sc_consensus_aura::ImportQueueParams { - block_import: frontier_block_import.clone(), - justification_import: Some(Box::new(grandpa_block_import)), - client: client.clone(), - create_inherent_data_providers, - spawner: &task_manager.spawn_essential_handle(), - registry: config.prometheus_registry(), - check_for_equivocation: Default::default(), - telemetry: telemetry.as_ref().map(|x| x.handle()), - compatibility_mode: sc_consensus_aura::CompatibilityMode::None, - }, - )?; + let dynamic_fee = fp_dynamic_fee::InherentDataProvider(U256::from(target_gas_price)); + Ok((slot, timestamp, dynamic_fee)) + }; - Ok(sc_service::PartialComponents { + let import_queue = sc_consensus_aura::import_queue::( + sc_consensus_aura::ImportQueueParams { + block_import: frontier_block_import.clone(), + justification_import: Some(Box::new(grandpa_block_import)), client, - backend, - task_manager, - import_queue, - keystore_container, - select_chain, - transaction_pool, - other: ( - telemetry, - (frontier_block_import, grandpa_link), - frontier_backend, - filter_pool, - (fee_history_cache, fee_history_cache_limit), - ), - }) - } - - #[cfg(feature = "manual-seal")] - { - let sealing = cli.run.sealing; - - let frontier_block_import = - FrontierBlockImport::new(client.clone(), client.clone(), frontier_backend.clone()); + create_inherent_data_providers, + spawner: &task_manager.spawn_essential_handle(), + registry: config.prometheus_registry(), + check_for_equivocation: Default::default(), + telemetry, + compatibility_mode: sc_consensus_aura::CompatibilityMode::None, + }, + ) + .map_err::(Into::into)?; + + Ok((import_queue, Box::new(frontier_block_import))) +} - let import_queue = sc_consensus_manual_seal::import_queue( +/// Build the import queue for the template runtime (manual seal). +pub fn build_manual_seal_import_queue( + client: Arc>, + config: &Configuration, + _eth_config: &EthConfiguration, + task_manager: &TaskManager, + _telemetry: Option, + _grandpa_block_import: GrandpaBlockImport>, + frontier_backend: Arc, +) -> Result< + ( + BasicImportQueue>, + BoxBlockImport>, + ), + ServiceError, +> +where + RuntimeApi: ConstructRuntimeApi>, + RuntimeApi: Send + Sync + 'static, + RuntimeApi::RuntimeApi: + RuntimeApiCollection>, + Executor: NativeExecutionDispatch + 'static, +{ + let frontier_block_import = FrontierBlockImport::new(client.clone(), client, frontier_backend); + Ok(( + sc_consensus_manual_seal::import_queue( Box::new(frontier_block_import.clone()), &task_manager.spawn_essential_handle(), config.prometheus_registry(), - ); - - Ok(sc_service::PartialComponents { - client, - backend, - task_manager, - import_queue, - keystore_container, - select_chain, - transaction_pool, - other: ( - telemetry, - (frontier_block_import, sealing), - frontier_backend, - filter_pool, - (fee_history_cache, fee_history_cache_limit), - ), - }) - } -} - -fn remote_keystore(_url: &str) -> Result, &'static str> { - // FIXME: here would the concrete keystore be built, - // must return a concrete type (NOT `LocalKeystore`) that - // implements `CryptoStore` and `SyncCryptoStore` - Err("Remote Keystore not supported.") + ), + Box::new(frontier_block_import), + )) } /// Builds a new service for a full client. -#[cfg(feature = "aura")] -pub fn new_full(mut config: Configuration, cli: &Cli) -> Result { - use sc_client_api::BlockBackend; - use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; - - // Use ethereum style for subscription ids - config.rpc_id_provider = Some(Box::new(fc_rpc::EthereumSubIdProvider)); +pub fn new_full( + mut config: Configuration, + eth_config: EthConfiguration, + sealing: Option, +) -> Result +where + RuntimeApi: ConstructRuntimeApi>, + RuntimeApi: Send + Sync + 'static, + RuntimeApi::RuntimeApi: + RuntimeApiCollection>, + Executor: NativeExecutionDispatch + 'static, +{ + let build_import_queue = if sealing.is_some() { + build_manual_seal_import_queue:: + } else { + build_aura_grandpa_import_queue:: + }; - let sc_service::PartialComponents { + let PartialComponents { client, backend, mut task_manager, import_queue, - mut keystore_container, + keystore_container, select_chain, transaction_pool, - other: - ( - mut telemetry, - consensus_result, - frontier_backend, - filter_pool, - (fee_history_cache, fee_history_cache_limit), - ), - } = new_partial(&config, cli)?; - - if let Some(url) = &config.keystore_remote { - match remote_keystore(url) { - Ok(k) => keystore_container.set_remote_keystore(k), - Err(e) => { - return Err(ServiceError::Other(format!( - "Error hooking up remote keystore for {}: {}", - url, e - ))) - } - }; - } + other: (mut telemetry, block_import, grandpa_link, frontier_backend), + } = new_partial(&config, ð_config, build_import_queue)?; + + let FrontierPartialComponents { + filter_pool, + fee_history_cache, + fee_history_cache_limit, + } = new_frontier_partial(ð_config)?; let grandpa_protocol_name = sc_finality_grandpa::protocol_standard_name( - &client - .block_hash(0) - .ok() - .flatten() - .expect("Genesis block exists; qed"), + &client.block_hash(0)?.expect("Genesis block exists; qed"), &config.chain_spec, ); - config - .network - .extra_sets - .push(sc_finality_grandpa::grandpa_peers_set_config( - grandpa_protocol_name.clone(), - )); - - let warp_sync = Arc::new(sc_finality_grandpa::warp_proof::NetworkProvider::new( - backend.clone(), - consensus_result.1.shared_authority_set().clone(), - Vec::default(), - )); + + let warp_sync: Option>> = + if sealing.is_some() { + None + } else { + config + .network + .extra_sets + .push(sc_finality_grandpa::grandpa_peers_set_config( + grandpa_protocol_name.clone(), + )); + Some(Arc::new( + sc_finality_grandpa::warp_proof::NetworkProvider::new( + backend.clone(), + grandpa_link.shared_authority_set().clone(), + Vec::default(), + ), + )) + }; let (network, system_rpc_tx, tx_handler_controller, network_starter) = sc_service::build_network(sc_service::BuildNetworkParams { @@ -327,7 +320,7 @@ pub fn new_full(mut config: Configuration, cli: &Cli) -> Result Result Result Result Result Result { - // Use ethereum style for subscription ids - config.rpc_id_provider = Some(Box::new(fc_rpc::EthereumSubIdProvider)); - - let sc_service::PartialComponents { - client, - backend, - mut task_manager, - import_queue, - mut keystore_container, - select_chain, - transaction_pool, - other: - ( - mut telemetry, - consensus_result, - frontier_backend, - filter_pool, - (fee_history_cache, fee_history_cache_limit), - ), - } = new_partial(&config, cli)?; - - if let Some(url) = &config.keystore_remote { - match remote_keystore(url) { - Ok(k) => keystore_container.set_remote_keystore(k), - Err(e) => { - return Err(ServiceError::Other(format!( - "Error hooking up remote keystore for {}: {}", - url, e - ))) - } - }; - } - - let (network, system_rpc_tx, tx_handler_controller, network_starter) = - sc_service::build_network(sc_service::BuildNetworkParams { - config: &config, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - spawn_handle: task_manager.spawn_handle(), - import_queue, - block_announce_validator_builder: None, - warp_sync: None, - })?; - - if config.offchain_worker.enabled { - sc_service::build_offchain_workers( - &config, - task_manager.spawn_handle(), - client.clone(), - network.clone(), - ); - } - - let role = config.role.clone(); - let prometheus_registry = config.prometheus_registry().cloned(); - let overrides = crate::rpc::overrides_handle(client.clone()); - let block_data_cache = Arc::new(fc_rpc::EthBlockDataCacheTask::new( +fn run_manual_seal_authorship( + eth_config: &EthConfiguration, + sealing: Sealing, + client: Arc>, + transaction_pool: Arc>>, + select_chain: FullSelectChain, + block_import: BoxBlockImport>, + task_manager: &TaskManager, + prometheus_registry: Option<&Registry>, + telemetry: Option<&Telemetry>, + commands_stream: mpsc::Receiver>, +) -> Result<(), ServiceError> +where + RuntimeApi: ConstructRuntimeApi>, + RuntimeApi: Send + Sync + 'static, + RuntimeApi::RuntimeApi: + RuntimeApiCollection>, + Executor: NativeExecutionDispatch + 'static, +{ + let proposer_factory = sc_basic_authorship::ProposerFactory::new( task_manager.spawn_handle(), - overrides.clone(), - 50, - 50, - prometheus_registry.clone(), - )); - // Channel for the rpc handler to communicate with the authorship task. - let (command_sink, commands_stream) = futures::channel::mpsc::channel(1000); - - let rpc_builder = { - let client = client.clone(); - let pool = transaction_pool.clone(); - let is_authority = role.is_authority(); - let enable_dev_signer = cli.run.enable_dev_signer; - let network = network.clone(); - let filter_pool = filter_pool.clone(); - let frontier_backend = frontier_backend.clone(); - let overrides = overrides.clone(); - let fee_history_cache = fee_history_cache.clone(); - let max_past_logs = cli.run.max_past_logs; - - Box::new(move |deny_unsafe, subscription_task_executor| { - let deps = crate::rpc::FullDeps { - client: client.clone(), - pool: pool.clone(), - graph: pool.pool().clone(), - deny_unsafe, - is_authority, - enable_dev_signer, - network: network.clone(), - filter_pool: filter_pool.clone(), - backend: frontier_backend.clone(), - max_past_logs, - fee_history_cache: fee_history_cache.clone(), - fee_history_cache_limit, - overrides: overrides.clone(), - block_data_cache: block_data_cache.clone(), - command_sink: Some(command_sink.clone()), - }; - - crate::rpc::create_full(deps, subscription_task_executor).map_err(Into::into) - }) - }; - - let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { - config, - client: client.clone(), - backend: backend.clone(), - task_manager: &mut task_manager, - keystore: keystore_container.sync_keystore(), - transaction_pool: transaction_pool.clone(), - rpc_builder, - network, - system_rpc_tx, - tx_handler_controller, - telemetry: telemetry.as_mut(), - })?; - - spawn_frontier_tasks( - &task_manager, client.clone(), - backend, - frontier_backend, - filter_pool, - overrides, - fee_history_cache, - fee_history_cache_limit, + transaction_pool.clone(), + prometheus_registry, + telemetry.as_ref().map(|x| x.handle()), ); - if role.is_authority() { - let env = sc_basic_authorship::ProposerFactory::new( - task_manager.spawn_handle(), - client.clone(), - transaction_pool.clone(), - prometheus_registry.as_ref(), - telemetry.as_ref().map(|x| x.handle()), - ); - - let (block_import, sealing) = consensus_result; - - const INHERENT_IDENTIFIER: sp_inherents::InherentIdentifier = *b"timstap0"; - thread_local!(static TIMESTAMP: std::cell::RefCell = std::cell::RefCell::new(0)); - - /// Provide a mock duration starting at 0 in millisecond for timestamp inherent. - /// Each call will increment timestamp by slot_duration making Aura think time has passed. - struct MockTimestampInherentDataProvider; - - #[async_trait::async_trait] - impl sp_inherents::InherentDataProvider for MockTimestampInherentDataProvider { - async fn provide_inherent_data( - &self, - inherent_data: &mut sp_inherents::InherentData, - ) -> Result<(), sp_inherents::Error> { - TIMESTAMP.with(|x| { - *x.borrow_mut() += frontier_template_runtime::SLOT_DURATION; - inherent_data.put_data(INHERENT_IDENTIFIER, &*x.borrow()) - }) - } - - async fn try_handle_error( - &self, - _identifier: &sp_inherents::InherentIdentifier, - _error: &[u8], - ) -> Option> { - // The pallet never reports error. - None - } + thread_local!(static TIMESTAMP: RefCell = RefCell::new(0)); + + /// Provide a mock duration starting at 0 in millisecond for timestamp inherent. + /// Each call will increment timestamp by slot_duration making Aura think time has passed. + struct MockTimestampInherentDataProvider; + + #[async_trait::async_trait] + impl sp_inherents::InherentDataProvider for MockTimestampInherentDataProvider { + async fn provide_inherent_data( + &self, + inherent_data: &mut sp_inherents::InherentData, + ) -> Result<(), sp_inherents::Error> { + TIMESTAMP.with(|x| { + *x.borrow_mut() += frontier_template_runtime::SLOT_DURATION; + inherent_data.put_data(sp_timestamp::INHERENT_IDENTIFIER, &*x.borrow()) + }) } - let target_gas_price = cli.run.target_gas_price; - let create_inherent_data_providers = move |_, ()| async move { - let mock_timestamp = MockTimestampInherentDataProvider; - let dynamic_fee = fp_dynamic_fee::InherentDataProvider(U256::from(target_gas_price)); - Ok((mock_timestamp, dynamic_fee)) - }; - - let manual_seal = match sealing { - Sealing::Manual => future::Either::Left(sc_consensus_manual_seal::run_manual_seal( - sc_consensus_manual_seal::ManualSealParams { - block_import, - env, - client, - pool: transaction_pool, - commands_stream, - select_chain, - consensus_data_provider: None, - create_inherent_data_providers, - }, - )), - Sealing::Instant => future::Either::Right(sc_consensus_manual_seal::run_instant_seal( - sc_consensus_manual_seal::InstantSealParams { - block_import, - env, - client, - pool: transaction_pool, - select_chain, - consensus_data_provider: None, - create_inherent_data_providers, - }, - )), - }; - // we spawn the future on a background thread managed by service. - task_manager - .spawn_essential_handle() - .spawn_blocking("manual-seal", None, manual_seal); + async fn try_handle_error( + &self, + _identifier: &sp_inherents::InherentIdentifier, + _error: &[u8], + ) -> Option> { + // The pallet never reports error. + None + } } - log::info!("Manual Seal Ready"); + let target_gas_price = eth_config.target_gas_price; + let create_inherent_data_providers = move |_, ()| async move { + let timestamp = MockTimestampInherentDataProvider; + let dynamic_fee = fp_dynamic_fee::InherentDataProvider(U256::from(target_gas_price)); + Ok((timestamp, dynamic_fee)) + }; - network_starter.start_network(); - Ok(task_manager) -} + let manual_seal = match sealing { + Sealing::Manual => future::Either::Left(sc_consensus_manual_seal::run_manual_seal( + sc_consensus_manual_seal::ManualSealParams { + block_import, + env: proposer_factory, + client, + pool: transaction_pool, + commands_stream, + select_chain, + consensus_data_provider: None, + create_inherent_data_providers, + }, + )), + Sealing::Instant => future::Either::Right(sc_consensus_manual_seal::run_instant_seal( + sc_consensus_manual_seal::InstantSealParams { + block_import, + env: proposer_factory, + client, + pool: transaction_pool, + select_chain, + consensus_data_provider: None, + create_inherent_data_providers, + }, + )), + }; -fn spawn_frontier_tasks( - task_manager: &TaskManager, - client: Arc, - backend: Arc, - frontier_backend: Arc>, - filter_pool: Option, - overrides: Arc>, - fee_history_cache: FeeHistoryCache, - fee_history_cache_limit: FeeHistoryCacheLimit, -) { - task_manager.spawn_essential_handle().spawn( - "frontier-mapping-sync-worker", - None, - MappingSyncWorker::new( - client.import_notification_stream(), - Duration::new(6, 0), - client.clone(), - backend, - frontier_backend, - 3, - 0, - SyncStrategy::Normal, - ) - .for_each(|()| future::ready(())), - ); + // we spawn the future on a background thread managed by service. + task_manager + .spawn_essential_handle() + .spawn_blocking("manual-seal", None, manual_seal); + Ok(()) +} - // Spawn Frontier EthFilterApi maintenance task. - if let Some(filter_pool) = filter_pool { - // Each filter is allowed to stay in the pool for 100 blocks. - const FILTER_RETAIN_THRESHOLD: u64 = 100; - task_manager.spawn_essential_handle().spawn( - "frontier-filter-pool", - None, - EthTask::filter_pool_task(client.clone(), filter_pool, FILTER_RETAIN_THRESHOLD), - ); - } +pub fn build_full( + config: Configuration, + eth_config: EthConfiguration, + sealing: Option, +) -> Result { + new_full::( + config, eth_config, sealing, + ) +} - // Spawn Frontier FeeHistory cache maintenance task. - task_manager.spawn_essential_handle().spawn( - "frontier-fee-history", - None, - EthTask::fee_history_task( - client, - overrides, - fee_history_cache, - fee_history_cache_limit, - ), - ); +pub fn new_chain_ops( + mut config: &mut Configuration, + eth_config: &EthConfiguration, +) -> Result< + ( + Arc, + Arc, + BasicQueue>, + TaskManager, + Arc, + ), + ServiceError, +> { + config.keystore = sc_service::config::KeystoreConfig::InMemory; + let PartialComponents { + client, + backend, + import_queue, + task_manager, + other, + .. + } = new_partial::( + config, + eth_config, + build_aura_grandpa_import_queue, + )?; + Ok((client, backend, import_queue, task_manager, other.3)) } diff --git a/template/runtime/Cargo.toml b/template/runtime/Cargo.toml index 9eb49d7705..a538843622 100644 --- a/template/runtime/Cargo.toml +++ b/template/runtime/Cargo.toml @@ -60,9 +60,7 @@ pallet-hotfix-sufficients = { workspace = true } substrate-wasm-builder = { workspace = true } [features] -default = ["std", "aura", "with-rocksdb-weights"] -aura = [] -manual-seal = ["with-rocksdb-weights"] +default = ["std", "with-rocksdb-weights"] with-rocksdb-weights = [] with-paritydb-weights = [] std = [ diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index 31304787e6..f2ed3f3d6d 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -23,7 +23,7 @@ use sp_runtime::{ IdentifyAccount, NumberFor, PostDispatchInfoOf, UniqueSaturatedInto, Verify, }, transaction_validity::{TransactionSource, TransactionValidity, TransactionValidityError}, - ApplyExtrinsicResult, MultiSignature, Perbill, Permill, + ApplyExtrinsicResult, ConsensusEngineId, MultiSignature, Perbill, Permill, }; use sp_std::{marker::PhantomData, prelude::*}; use sp_version::RuntimeVersion; @@ -32,6 +32,11 @@ use sp_version::RuntimeVersion; use frame_support::weights::constants::ParityDbWeight as RuntimeDbWeight; #[cfg(feature = "with-rocksdb-weights")] use frame_support::weights::constants::RocksDbWeight as RuntimeDbWeight; +use frame_support::{ + construct_runtime, parameter_types, + traits::{ConstU32, ConstU8, FindAuthor, KeyOwnerProofSystem, OnTimestampSet}, + weights::{constants::WEIGHT_REF_TIME_PER_SECOND, ConstantMultiplier, IdentityFee, Weight}, +}; use pallet_grandpa::{ fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList, }; @@ -44,15 +49,6 @@ use pallet_evm::{ }; // A few exports that help ease life for downstream crates. -pub use frame_support::{ - construct_runtime, parameter_types, - traits::{ConstU32, ConstU8, FindAuthor, KeyOwnerProofSystem, Randomness}, - weights::{ - constants::{BlockExecutionWeight, ExtrinsicBaseWeight, WEIGHT_REF_TIME_PER_SECOND}, - ConstantMultiplier, IdentityFee, Weight, - }, - ConsensusEngineId, StorageValue, -}; pub use frame_system::Call as SystemCall; pub use pallet_balances::Call as BalancesCall; pub use pallet_timestamp::Call as TimestampCall; @@ -240,15 +236,23 @@ impl pallet_grandpa::Config for Runtime { parameter_types! { pub const MinimumPeriod: u64 = SLOT_DURATION / 2; + pub storage EnableManualSeal: bool = false; +} + +pub struct ConsensusOnTimestampSet(PhantomData); +impl OnTimestampSet for ConsensusOnTimestampSet { + fn on_timestamp_set(moment: T::Moment) { + if EnableManualSeal::get() { + return; + } + as OnTimestampSet>::on_timestamp_set(moment) + } } impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; - #[cfg(feature = "aura")] - type OnTimestampSet = Aura; - #[cfg(feature = "manual-seal")] - type OnTimestampSet = (); + type OnTimestampSet = ConsensusOnTimestampSet; type MinimumPeriod = MinimumPeriod; type WeightInfo = (); } @@ -400,6 +404,7 @@ construct_runtime!( } ); +#[derive(Clone)] pub struct TransactionConverter; impl fp_rpc::ConvertTransaction for TransactionConverter { diff --git a/ts-tests/README.md b/ts-tests/README.md index ce3794bcb9..de5ec0e9ce 100644 --- a/ts-tests/README.md +++ b/ts-tests/README.md @@ -9,10 +9,10 @@ It is written in typescript, using Mocha/Chai as Test framework. Tests are separated depending on their genesis requirements. Each group will start a `frontier test node` with a given `spec` before executing the tests. -## Build the manual seal node for tests +## Build the node for tests ```bash -cargo build --release --no-default-features --features manual-seal,rpc_binary_search_estimate +cargo build --release --features rpc-binary-search-estimate ``` ## Installation diff --git a/ts-tests/package-lock.json b/ts-tests/package-lock.json index f9f27fc423..f936170f95 100644 --- a/ts-tests/package-lock.json +++ b/ts-tests/package-lock.json @@ -7170,6 +7170,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.5.tgz", "integrity": "sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A==", + "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "^4.3.0" @@ -7506,6 +7507,7 @@ "version": "5.0.7", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.7.tgz", "integrity": "sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q==", + "hasInstallScript": true, "optional": true, "dependencies": { "node-gyp-build": "^4.3.0"