diff --git a/Cargo.lock b/Cargo.lock index c70ee5058..484540a77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7896,6 +7896,30 @@ dependencies = [ "sp-std 8.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", ] +[[package]] +name = "pallet-babe" +version = "4.0.0-dev" +source = "git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0#c8d2251cafadc108ba2f1f8a3208dc547ff38901" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "log", + "pallet-authorship", + "pallet-session", + "pallet-timestamp", + "parity-scale-codec", + "scale-info", + "sp-application-crypto 23.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", + "sp-consensus-babe", + "sp-core 21.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", + "sp-io 23.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", + "sp-runtime 24.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", + "sp-session", + "sp-staking", + "sp-std 8.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", +] + [[package]] name = "pallet-bags-list" version = "4.0.0-dev" @@ -15047,8 +15071,8 @@ dependencies = [ "log", "num_enum 0.5.11", "pallet-airdrop-claims", - "pallet-aura", "pallet-authorship", + "pallet-babe", "pallet-bags-list", "pallet-balances", "pallet-base-fee", @@ -15108,7 +15132,7 @@ dependencies = [ "serde", "sp-api", "sp-block-builder", - "sp-consensus-aura", + "sp-consensus-babe", "sp-core 21.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", "sp-inherents", "sp-io 23.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", @@ -15145,6 +15169,7 @@ dependencies = [ "smallvec", "sp-arithmetic 16.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", "sp-consensus-aura", + "sp-consensus-babe", "sp-core 21.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", "sp-runtime 24.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", "sp-std 8.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", @@ -15238,14 +15263,17 @@ dependencies = [ "sp-blockchain", "sp-consensus", "sp-consensus-aura", + "sp-consensus-babe", "sp-consensus-grandpa", "sp-core 21.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", "sp-inherents", + "sp-io 23.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", "sp-keyring 24.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", "sp-keystore 0.27.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", "sp-offchain", "sp-runtime 24.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", "sp-session", + "sp-statement-store", "sp-storage 13.0.0 (git+https://github.com/paritytech/polkadot-sdk?branch=release-polkadot-v1.1.0)", "sp-timestamp", "sp-transaction-pool", @@ -16244,7 +16272,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 0.1.10", "digest 0.10.7", "rand 0.8.5", "static_assertions", diff --git a/Cargo.toml b/Cargo.toml index a22167f16..593275366 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,6 +106,7 @@ pallet-dkg = { path = "pallets/dkg", default-features = false } sp-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-block-builder = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-consensus-babe = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-core = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-inherents = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-io = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } @@ -114,6 +115,7 @@ sc-offchain = { git = "https://github.com/paritytech/polkadot-sdk", branch = "re sp-runtime = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-session = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-staking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +sp-statement-store = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-version = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } @@ -148,6 +150,7 @@ sc-basic-authorship = { git = "https://github.com/paritytech/polkadot-sdk", bran sc-client-api = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } sc-consensus = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } sc-consensus-aura = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } +sc-consensus-babe = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } sc-network-common = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } sc-telemetry = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } sc-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0" } @@ -168,6 +171,7 @@ sc-client-db = { git = "https://github.com/paritytech/polkadot-sdk", branch = "r sp-application-crypto = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } pallet-aura = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-babe = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } pallet-bags-list = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } pallet-session = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } diff --git a/node/Cargo.toml b/node/Cargo.toml index c0043a285..283705f25 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -107,7 +107,7 @@ tangle-testnet-runtime = { workspace = true } tangle-service = { workspace = true } [features] -default = ["with-rocksdb-weights", "rocksdb", "sql", "tangle"] +default = ["with-rocksdb-weights", "rocksdb", "sql", "testnet"] runtime-benchmarks = ["tangle-mainnet-runtime/runtime-benchmarks"] integration-tests = ["tangle-mainnet-runtime/integration-tests"] with-rocksdb-weights = ["tangle-mainnet-runtime/with-rocksdb-weights"] @@ -128,5 +128,9 @@ relayer = [ "tangle-service/relayer", ] -tangle = [] -testnet = [] \ No newline at end of file +tangle = [ + "tangle-service/tangle", +] +testnet = [ + "tangle-service/testnet", +] \ No newline at end of file diff --git a/node/service/Cargo.toml b/node/service/Cargo.toml index 5f22ca63e..eb342acc1 100644 --- a/node/service/Cargo.toml +++ b/node/service/Cargo.toml @@ -42,6 +42,8 @@ sc-service = { workspace = true } sc-telemetry = { workspace = true } sc-transaction-pool = { workspace = true } sp-consensus-aura = { workspace = true } +sp-consensus-babe = { workspace = true } +sp-statement-store = { workspace = true } sp-core = { workspace = true } sp-inherents = { workspace = true } sp-keyring = { workspace = true } @@ -57,6 +59,7 @@ sp-api = { workspace = true } sp-block-builder = { workspace = true } sp-blockchain = { workspace = true } sp-timestamp = { workspace = true } +sp-io = { workspace = true } sp-keystore = { workspace = true } sp-session = { workspace = true } sp-offchain = { workspace = true } @@ -102,7 +105,7 @@ tangle-primitives = { workspace = true } tangle-testnet-runtime = { workspace = true } [features] -default = ["with-rocksdb-weights", "rocksdb", "sql", "tangle"] +default = ["with-rocksdb-weights", "rocksdb", "sql", "testnet"] runtime-benchmarks = ["tangle-testnet-runtime/runtime-benchmarks"] integration-tests = ["tangle-testnet-runtime/integration-tests"] with-rocksdb-weights = ["tangle-testnet-runtime/with-rocksdb-weights"] diff --git a/node/service/src/aura.rs b/node/service/src/aura.rs new file mode 100644 index 000000000..e2e1a7043 --- /dev/null +++ b/node/service/src/aura.rs @@ -0,0 +1,612 @@ +use crate::{ + client::Client, + eth::{ + new_frontier_partial, spawn_frontier_tasks, BackendType, EthApi, EthConfiguration, + FrontierPartialComponents, RpcConfig, + }, + open_frontier_backend, FrontierBlockImport, FullBackend, FullClient, IdentifyVariant, + PartialComponentsResult, RuntimeApiCollection, TestnetExecutor, +}; +use fc_consensus::FrontierBlockImport as TFrontierBlockImport; +use fc_db::DatabaseSource; +use futures::{channel::mpsc, FutureExt}; +use sc_chain_spec::ChainSpec; +use sc_client_api::{AuxStore, Backend, BlockBackend, StateBackend, StorageProvider}; +use sc_consensus_aura::ImportQueueParams; +use sc_consensus_grandpa::SharedVoterState; +pub use sc_executor::NativeElseWasmExecutor; +use sc_service::{ + error::Error as ServiceError, ChainType, Configuration, PartialComponents, TFullBackend, + TFullClient, TaskManager, +}; +use sc_telemetry::{Telemetry, TelemetryWorker}; +use sc_transaction_pool_api::OffchainTransactionPoolFactory; +use sp_api::{ConstructRuntimeApi, ProvideRuntimeApi}; +use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; +use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; +use sp_core::U256; +use tangle_primitives::Block; + +use std::{path::Path, sync::Arc, time::Duration}; + +use sp_runtime::{traits::BlakeTwo256, Percent}; + +pub const SOFT_DEADLINE_PERCENT: Percent = Percent::from_percent(100); + +/// Builds a new object suitable for chain operations. +#[allow(clippy::type_complexity)] +pub fn new_chain_ops( + config: &mut Configuration, + eth_config: &EthConfiguration, +) -> Result< + (Arc, Arc, sc_consensus::BasicQueue, TaskManager), + ServiceError, +> { + match &config.chain_spec { + #[cfg(feature = "testnet")] + spec if spec.is_testnet() => new_chain_ops_inner::< + tangle_testnet_runtime::RuntimeApi, + TestnetExecutor, + >(config, eth_config), + #[cfg(feature = "tangle")] + spec if spec.is_tangle() => new_chain_ops_inner::< + tangle_mainnet_runtime::RuntimeApi, + TangleExecutor, + >(config, eth_config), + _ => panic!("invalid chain spec"), + } +} + +#[allow(clippy::type_complexity)] +fn new_chain_ops_inner( + config: &mut Configuration, + eth_config: &EthConfiguration, +) -> Result< + (Arc, Arc, sc_consensus::BasicQueue, TaskManager), + ServiceError, +> +where + Client: From>>, + RuntimeApi: + ConstructRuntimeApi> + Send + Sync + 'static, + RuntimeApi::RuntimeApi: RuntimeApiCollection, + Executor: sc_executor::NativeExecutionDispatch + 'static, +{ + config.keystore = sc_service::config::KeystoreConfig::InMemory; + let PartialComponents { client, backend, import_queue, task_manager, .. } = + new_partial::(config, eth_config)?; + Ok((Arc::new(Client::from(client)), backend, import_queue, task_manager)) +} + +pub fn new_partial( + config: &Configuration, + eth_config: &EthConfiguration, +) -> PartialComponentsResult +where + RuntimeApi: + ConstructRuntimeApi> + Send + Sync + 'static, + RuntimeApi::RuntimeApi: RuntimeApiCollection, + Executor: sc_executor::NativeExecutionDispatch + 'static, +{ + println!(" ++++++++++++++++++++++++ + +++++++++++++++++++++++++++ + +++++++++++++++++++++++++++ + +++ ++++++ +++ @%%%%%%%%%%% %%% + ++++++ ++++ +++++ %%%%%%%%%%%% %%%@ + ++++++++++++++++++++++++++ %%%% %%%%@ %%% %%@ @%%%%%%% %%%@ %%%%@ + ++++++++ %%%% @%%%%%%%@ %%%%%%%%% @%%%%%%%%% %%%@ %%%%%%%%% + ++++++++ %%%% %%%%%%%%% %%%% @%%%@ %%%% %%%% %%%@ %%%%%%%%%% + ++++++++++++++++++++++++++ %%%% %%%%%%%%% %%% %%%% %%% @%%% %%%@ @%%%%% %%%%% + ++++++ ++++ ++++++ %%%% %%%%%%%%% %%% %%%% %%%%%%%%%% %%%@ %%%%%%%%%@ + +++ ++++++ +++ %%%% %%%%%%%%% %%% %%%@ %%%%%%%%% %%% %%%%%%%@ + ++++ +++++++++ +++ %%%% %%%% + ++++++++++++++++++++++++++++ %%%%%%%%% + +++++++++++++++++++++++ %%%%% \n"); + let telemetry = config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + let executor = sc_service::new_native_or_wasm_executor(config); + + let (client, backend, keystore_container, task_manager) = + sc_service::new_full_parts::( + config, + telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, + )?; + let client = Arc::new(client); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", None, worker.run()); + telemetry + }); + + let select_chain = sc_consensus::LongestChain::new(backend.clone()); + + let transaction_pool = sc_transaction_pool::BasicPool::new_full( + config.transaction_pool.clone(), + config.role.is_authority().into(), + config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( + client.clone(), + crate::GRANDPA_JUSTIFICATION_PERIOD, + &client, + select_chain.clone(), + telemetry.as_ref().map(|x| x.handle()), + )?; + + let overrides = crate::rpc::overrides_handle(client.clone()); + let frontier_backend = open_frontier_backend(client.clone(), config, ð_config)?; + + let frontier_block_import = FrontierBlockImport::new(client.clone(), client.clone()); + + 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::(ImportQueueParams { + block_import: frontier_block_import.clone(), + justification_import: Some(Box::new(grandpa_block_import.clone())), + 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: Default::default(), + })?; + + Ok(sc_service::PartialComponents { + client, + backend, + keystore_container, + task_manager, + select_chain, + import_queue, + transaction_pool, + other: ( + telemetry, + Box::new(frontier_block_import), + grandpa_link, + frontier_backend, + overrides, + ), + }) +} + +pub struct RunFullParams { + pub config: Configuration, + pub eth_config: EthConfiguration, + pub rpc_config: RpcConfig, + pub debug_output: Option, + #[cfg(feature = "relayer")] + pub relayer_cmd: tangle_relayer_gadget_cli::RelayerCmd, + #[cfg(feature = "light-client")] + pub light_client_relayer_cmd: + pallet_eth2_light_client_relayer_gadget_cli::LightClientRelayerCmd, + pub auto_insert_keys: bool, +} + +/// Builds a new service for a full client. +pub async fn new_full( + RunFullParams { + mut config, + eth_config, + rpc_config, + debug_output: _, + #[cfg(feature = "relayer")] + relayer_cmd, + #[cfg(feature = "light-client")] + light_client_relayer_cmd, + auto_insert_keys, + }: RunFullParams, +) -> Result +where + RuntimeApi: + ConstructRuntimeApi> + Send + Sync + 'static, + RuntimeApi::RuntimeApi: RuntimeApiCollection, + Executor: sc_executor::NativeExecutionDispatch + 'static, +{ + let sc_service::PartialComponents { + client, + backend, + mut task_manager, + import_queue, + keystore_container, + select_chain, + transaction_pool, + other: (mut telemetry, block_import, grandpa_link, frontier_backend, overrides), + } = new_partial::(&config, ð_config)?; + + if config.role.is_authority() { + if auto_insert_keys { + crate::utils::insert_controller_account_keys_into_keystore( + &config, + Some(keystore_container.keystore()), + ); + } else { + crate::utils::insert_dev_controller_account_keys_into_keystore( + &config, + Some(keystore_container.keystore()), + ); + } + + // finally check if keys are inserted correctly + if config.chain_spec.chain_type() != ChainType::Development { + if crate::utils::ensure_all_keys_exist_in_keystore(keystore_container.keystore()) + .is_err() + { + println!(" + ++++++++++++++++++++++++++++++++++++++++++++++++ + Validator keys not found, validator keys are essential to run a validator on + Tangle Network, refer to https://docs.webb.tools/docs/ecosystem-roles/validator/required-keys/ on + how to generate and insert keys. OR start the node with --auto-insert-keys to automatically generate the keys. + ++++++++++++++++++++++++++++++++++++++++++++++++ + \n"); + panic!("Keys not detected!") + } + } + } + + let FrontierPartialComponents { filter_pool, fee_history_cache, fee_history_cache_limit } = + new_frontier_partial(ð_config)?; + + let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); + + let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name( + &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), + &config.chain_spec, + ); + + net_config.add_notification_protocol(sc_consensus_grandpa::grandpa_peers_set_config( + grandpa_protocol_name.clone(), + )); + + let warp_sync = Arc::new(sc_consensus_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, sync_service) = + sc_service::build_network(sc_service::BuildNetworkParams { + config: &config, + net_config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + spawn_handle: task_manager.spawn_handle(), + import_queue, + block_announce_validator_builder: None, + warp_sync_params: Some(sc_service::WarpSyncParams::WithProvider(warp_sync)), + })?; + + let role = config.role.clone(); + let force_authoring = config.force_authoring; + let _backoff_authoring_blocks: Option<()> = None; + let name = config.network.node_name.clone(); + let enable_grandpa = !config.disable_grandpa; + let prometheus_registry = config.prometheus_registry().cloned(); + + if config.offchain_worker.enabled { + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-work", + sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { + runtime_api_provider: client.clone(), + keystore: Some(keystore_container.keystore()), + offchain_db: backend.offchain_storage(), + transaction_pool: Some(OffchainTransactionPoolFactory::new( + transaction_pool.clone(), + )), + network_provider: network.clone(), + is_validator: role.is_authority(), + enable_http_requests: true, + custom_extensions: move |_| vec![], + }) + .run(client.clone(), task_manager.spawn_handle()) + .boxed(), + ); + } + + // Channel for the rpc handler to communicate with the authorship task. + let (command_sink, _commands_stream) = mpsc::channel(1000); + + // Sinks for pubsub notifications. + // Everytime a new subscription is created, a new mpsc channel is added to the sink pool. + // The MappingSyncWorker sends through the channel on block import and the subscription emits a + // notification to the subscriber on receiving a message through this channel. This way we avoid + // race conditions when using native substrate block import notification stream. + let pubsub_notification_sinks: fc_mapping_sync::EthereumBlockNotificationSinks< + fc_mapping_sync::EthereumBlockNotification, + > = Default::default(); + let pubsub_notification_sinks = Arc::new(pubsub_notification_sinks); + + // for ethereum-compatibility rpc. + config.rpc_id_provider = Some(Box::new(fc_rpc::EthereumSubIdProvider)); + + 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)) + }; + + let ethapi_cmd = rpc_config.ethapi.clone(); + let tracing_requesters = + if ethapi_cmd.contains(&EthApi::Debug) || ethapi_cmd.contains(&EthApi::Trace) { + crate::rpc::tracing::spawn_tracing_tasks( + &task_manager, + client.clone(), + backend.clone(), + frontier_backend.clone(), + overrides.clone(), + &rpc_config, + prometheus_registry.clone(), + ) + } else { + crate::rpc::tracing::RpcRequesters { debug: None, trace: None } + }; + let eth_rpc_params = crate::rpc::EthDeps { + client: client.clone(), + pool: transaction_pool.clone(), + graph: transaction_pool.pool().clone(), + #[cfg(feature = "tangle")] + converter: Some(tangle_mainnet_runtime::TransactionConverter), + #[cfg(feature = "testnet")] + converter: Some(tangle_testnet_runtime::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( + 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, + tracing_config: Some(crate::rpc::eth::TracingConfig { + tracing_requesters: tracing_requesters.clone(), + trace_filter_max_count: rpc_config.ethapi_trace_max_count, + }), + pending_create_inherent_data_providers, + }; + + let rpc_builder = { + let client = client.clone(); + let pool = transaction_pool.clone(); + let pubsub_notification_sinks = pubsub_notification_sinks.clone(); + Box::new(move |deny_unsafe, subscription_task_executor| { + let deps = crate::rpc::FullDeps { + client: client.clone(), + pool: pool.clone(), + deny_unsafe, + command_sink: Some(command_sink.clone()), + eth: eth_rpc_params.clone(), + }; + if ethapi_cmd.contains(&EthApi::Debug) || ethapi_cmd.contains(&EthApi::Trace) { + crate::rpc::create_full( + deps, + subscription_task_executor, + pubsub_notification_sinks.clone(), + ) + .map_err(Into::into) + } else { + crate::rpc::create_full( + deps, + subscription_task_executor, + pubsub_notification_sinks.clone(), + ) + .map_err(Into::into) + } + }) + }; + + spawn_frontier_tasks( + &task_manager, + client.clone(), + backend.clone(), + frontier_backend, + filter_pool, + overrides, + fee_history_cache, + fee_history_cache_limit, + sync_service.clone(), + pubsub_notification_sinks, + ) + .await; + + if role.is_authority() { + // setup relayer gadget params + #[cfg(feature = "relayer")] + let relayer_params = tangle_relayer_gadget::RelayerParams { + local_keystore: keystore_container.local_keystore(), + config_dir: relayer_cmd.relayer_config_dir, + database_path: config + .database + .path() + .and_then(|path| path.parent()) + .map(|p| p.to_path_buf()), + rpc_addr: config.rpc_addr, + }; + + // Start Webb Relayer Gadget as non-essential task. + #[cfg(feature = "relayer")] + task_manager.spawn_handle().spawn( + "relayer-gadget", + None, + tangle_relayer_gadget::start_relayer_gadget( + relayer_params, + sp_application_crypto::KeyTypeId(*b"role"), + ), + ); + + // Start Eth2 Light client Relayer Gadget - (MAINNET RELAYER) + #[cfg(feature = "light-client")] + task_manager.spawn_handle().spawn( + "mainnet-relayer-gadget", + None, + pallet_eth2_light_client_relayer_gadget::start_gadget( + pallet_eth2_light_client_relayer_gadget::Eth2LightClientParams { + lc_relay_config_path: light_client_relayer_cmd + .light_client_relay_config_path + .clone(), + lc_init_config_path: light_client_relayer_cmd + .light_client_init_pallet_config_path + .clone(), + eth2_chain_id: webb_proposals::TypedChainId::Evm(1), + }, + ), + ); + } + let params = sc_service::SpawnTasksParams { + network: network.clone(), + client: client.clone(), + keystore: keystore_container.keystore(), + task_manager: &mut task_manager, + transaction_pool: transaction_pool.clone(), + rpc_builder, + backend: backend.clone(), + system_rpc_tx, + tx_handler_controller, + sync_service: sync_service.clone(), + config, + telemetry: telemetry.as_mut(), + }; + let _rpc_handlers = sc_service::spawn_tasks(params)?; + + if role.is_authority() { + let proposer_factory = 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 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 aura = sc_consensus_aura::start_aura::( + sc_consensus_aura::StartAuraParams { + slot_duration, + client, + select_chain, + block_import, + proposer_factory, + sync_oracle: sync_service.clone(), + justification_sync_link: sync_service.clone(), + create_inherent_data_providers, + force_authoring, + backoff_authoring_blocks: Option::<()>::None, + keystore: keystore_container.keystore(), + block_proposal_slot_portion: sc_consensus_aura::SlotProportion::new(2f32 / 3f32), + max_block_proposal_slot_portion: None, + telemetry: telemetry.as_ref().map(|x| x.handle()), + compatibility_mode: sc_consensus_aura::CompatibilityMode::None, + }, + )?; + + // the AURA authoring task is considered essential, i.e. if it + // fails we take down the service with it. + task_manager + .spawn_essential_handle() + .spawn_blocking("aura", Some("block-authoring"), aura); + } + + // if the node isn't actively participating in consensus then it doesn't + // need a keystore, regardless of which protocol we use below. + let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; + + let grandpa_config = sc_consensus_grandpa::Config { + // FIXME #1578 make this available through chainspec + gossip_duration: Duration::from_millis(333), + justification_generation_period: crate::GRANDPA_JUSTIFICATION_PERIOD, + name: Some(name), + observer_enabled: false, + keystore, + local_role: role, + telemetry: telemetry.as_ref().map(|x| x.handle()), + protocol_name: grandpa_protocol_name, + }; + + if enable_grandpa { + // start the full GRANDPA voter + // NOTE: non-authorities could run the GRANDPA observer protocol, but at + // this point the full voter should provide better guarantees of block + // and vote data availability than the observer. The observer has not + // been tested extensively yet and having most nodes in a network run it + // could lead to finality stalls. + let grandpa_config = sc_consensus_grandpa::GrandpaParams { + config: grandpa_config, + link: grandpa_link, + network, + sync: Arc::new(sync_service), + voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), + prometheus_registry, + shared_voter_state: SharedVoterState::empty(), + telemetry: telemetry.as_ref().map(|x| x.handle()), + offchain_tx_pool_factory: OffchainTransactionPoolFactory::new(transaction_pool), + }; + + // the GRANDPA voter task is considered infallible, i.e. + // if it fails we take down the service with it. + task_manager.spawn_essential_handle().spawn_blocking( + "grandpa-voter", + None, + sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?, + ); + } + + network_starter.start_network(); + Ok(task_manager) +} diff --git a/node/service/src/babe.rs b/node/service/src/babe.rs new file mode 100644 index 000000000..146cc3f2e --- /dev/null +++ b/node/service/src/babe.rs @@ -0,0 +1,842 @@ +// Copyright 2022 Webb Technologies Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![warn(unused_extern_crates)] + +//! Service implementation. Specialized wrapper over substrate service. + +use crate::Cli; +use codec::Encode; +use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; +use frame_system_rpc_runtime_api::AccountNonceApi; +use futures::prelude::*; +use kitchensink_runtime::RuntimeApi; +use node_primitives::Block; +use sc_client_api::{Backend, BlockBackend}; +use sc_consensus_babe::{self, SlotProportion}; +use sc_network::{event::Event, NetworkEventStream, NetworkService}; +use sc_network_sync::{warp::WarpSyncParams, SyncingService}; +use sc_service::{config::Configuration, error::Error as ServiceError, RpcHandlers, TaskManager}; +use sc_statement_store::Store as StatementStore; +use sc_telemetry::{Telemetry, TelemetryWorker}; +use sc_transaction_pool_api::OffchainTransactionPoolFactory; +use sp_api::ProvideRuntimeApi; +use sp_core::crypto::Pair; +use sp_runtime::{generic, traits::Block as BlockT, SaturatedConversion}; +use std::sync::Arc; + +/// Creates a new partial node. +pub fn new_partial( + config: &Configuration, + mixnet_config: Option<&sc_mixnet::Config>, +) -> Result< + sc_service::PartialComponents< + FullClient, + FullBackend, + FullSelectChain, + sc_consensus::DefaultImportQueue, + sc_transaction_pool::FullPool>, + ( + impl Fn( + node_rpc::DenyUnsafe, + sc_rpc::SubscriptionTaskExecutor, + ) -> Result, sc_service::Error>, + ( + sc_consensus_babe::BabeBlockImport< + Block, + FullClient, + FullGrandpaBlockImport, + >, + grandpa::LinkHalf, FullSelectChain>, + sc_consensus_babe::BabeLink, + ), + grandpa::SharedVoterState, + Option, + Arc, + Option, + ), + >, + ServiceError, +> { + let telemetry = config + .telemetry_endpoints + .clone() + .filter(|x| !x.is_empty()) + .map(|endpoints| -> Result<_, sc_telemetry::Error> { + let worker = TelemetryWorker::new(16)?; + let telemetry = worker.handle().new_telemetry(endpoints); + Ok((worker, telemetry)) + }) + .transpose()?; + + let executor = sc_service::new_wasm_executor(&config); + + let (client, backend, keystore_container, task_manager) = + sc_service::new_full_parts::( + config, + telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), + executor, + )?; + let client = Arc::new(client); + + let telemetry = telemetry.map(|(worker, telemetry)| { + task_manager.spawn_handle().spawn("telemetry", None, worker.run()); + telemetry + }); + + let select_chain = sc_consensus::LongestChain::new(backend.clone()); + + let transaction_pool = sc_transaction_pool::BasicPool::new_full( + config.transaction_pool.clone(), + config.role.is_authority().into(), + config.prometheus_registry(), + task_manager.spawn_essential_handle(), + client.clone(), + ); + + let (grandpa_block_import, grandpa_link) = grandpa::block_import( + client.clone(), + GRANDPA_JUSTIFICATION_PERIOD, + &(client.clone() as Arc<_>), + select_chain.clone(), + telemetry.as_ref().map(|x| x.handle()), + )?; + let justification_import = grandpa_block_import.clone(); + + let (block_import, babe_link) = sc_consensus_babe::block_import( + sc_consensus_babe::configuration(&*client)?, + grandpa_block_import, + client.clone(), + )?; + + let slot_duration = babe_link.config().slot_duration(); + let (import_queue, babe_worker_handle) = + sc_consensus_babe::import_queue(sc_consensus_babe::ImportQueueParams { + link: babe_link.clone(), + block_import: block_import.clone(), + justification_import: Some(Box::new(justification_import)), + client: client.clone(), + select_chain: select_chain.clone(), + create_inherent_data_providers: move |_, ()| async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_babe::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + Ok((slot, timestamp)) + }, + spawner: &task_manager.spawn_essential_handle(), + registry: config.prometheus_registry(), + telemetry: telemetry.as_ref().map(|x| x.handle()), + offchain_tx_pool_factory: OffchainTransactionPoolFactory::new(transaction_pool.clone()), + })?; + + let import_setup = (block_import, grandpa_link, babe_link); + + let statement_store = sc_statement_store::Store::new_shared( + &config.data_path, + Default::default(), + client.clone(), + keystore_container.local_keystore(), + config.prometheus_registry(), + &task_manager.spawn_handle(), + ) + .map_err(|e| ServiceError::Other(format!("Statement store error: {:?}", e)))?; + + let (mixnet_api, mixnet_api_backend) = mixnet_config.map(sc_mixnet::Api::new).unzip(); + + let (rpc_extensions_builder, rpc_setup) = { + let (_, grandpa_link, _) = &import_setup; + + let justification_stream = grandpa_link.justification_stream(); + let shared_authority_set = grandpa_link.shared_authority_set().clone(); + let shared_voter_state = grandpa::SharedVoterState::empty(); + let shared_voter_state2 = shared_voter_state.clone(); + + let finality_proof_provider = grandpa::FinalityProofProvider::new_for_service( + backend.clone(), + Some(shared_authority_set.clone()), + ); + + let client = client.clone(); + let pool = transaction_pool.clone(); + let select_chain = select_chain.clone(); + let keystore = keystore_container.keystore(); + let chain_spec = config.chain_spec.cloned_box(); + + let rpc_backend = backend.clone(); + let rpc_statement_store = statement_store.clone(); + let rpc_extensions_builder = move |deny_unsafe, subscription_executor| { + let deps = node_rpc::FullDeps { + client: client.clone(), + pool: pool.clone(), + select_chain: select_chain.clone(), + chain_spec: chain_spec.cloned_box(), + deny_unsafe, + babe: node_rpc::BabeDeps { + keystore: keystore.clone(), + babe_worker_handle: babe_worker_handle.clone(), + }, + grandpa: node_rpc::GrandpaDeps { + shared_voter_state: shared_voter_state.clone(), + shared_authority_set: shared_authority_set.clone(), + justification_stream: justification_stream.clone(), + subscription_executor, + finality_provider: finality_proof_provider.clone(), + }, + statement_store: rpc_statement_store.clone(), + backend: rpc_backend.clone(), + mixnet_api: mixnet_api.as_ref().cloned(), + }; + + node_rpc::create_full(deps).map_err(Into::into) + }; + + (rpc_extensions_builder, shared_voter_state2) + }; + + Ok(sc_service::PartialComponents { + client, + backend, + task_manager, + keystore_container, + select_chain, + import_queue, + transaction_pool, + other: ( + rpc_extensions_builder, + import_setup, + rpc_setup, + telemetry, + statement_store, + mixnet_api_backend, + ), + }) +} + +/// Result of [`new_full_base`]. +pub struct NewFullBase { + /// The task manager of the node. + pub task_manager: TaskManager, + /// The client instance of the node. + pub client: Arc, + /// The networking service of the node. + pub network: Arc::Hash>>, + /// The syncing service of the node. + pub sync: Arc>, + /// The transaction pool of the node. + pub transaction_pool: Arc, + /// The rpc handlers of the node. + pub rpc_handlers: RpcHandlers, +} + +/// Creates a full service from the configuration. +pub fn new_full_base( + config: Configuration, + mixnet_config: Option, + disable_hardware_benchmarks: bool, + with_startup_data: impl FnOnce( + &sc_consensus_babe::BabeBlockImport, + &sc_consensus_babe::BabeLink, + ), +) -> Result { + let hwbench = (!disable_hardware_benchmarks) + .then_some(config.database.path().map(|database_path| { + let _ = std::fs::create_dir_all(&database_path); + sc_sysinfo::gather_hwbench(Some(database_path)) + })) + .flatten(); + + let sc_service::PartialComponents { + client, + backend, + mut task_manager, + import_queue, + keystore_container, + select_chain, + transaction_pool, + other: + (rpc_builder, import_setup, rpc_setup, mut telemetry, statement_store, mixnet_api_backend), + } = new_partial(&config, mixnet_config.as_ref())?; + + let shared_voter_state = rpc_setup; + let auth_disc_publish_non_global_ips = config.network.allow_non_globals_in_dht; + let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); + let genesis_hash = client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"); + + let grandpa_protocol_name = grandpa::protocol_standard_name(&genesis_hash, &config.chain_spec); + let (grandpa_protocol_config, grandpa_notification_service) = + grandpa::grandpa_peers_set_config(grandpa_protocol_name.clone()); + net_config.add_notification_protocol(grandpa_protocol_config); + + let (statement_handler_proto, statement_config) = + sc_network_statement::StatementHandlerPrototype::new( + genesis_hash, + config.chain_spec.fork_id(), + ); + net_config.add_notification_protocol(statement_config); + + let mixnet_protocol_name = + sc_mixnet::protocol_name(genesis_hash.as_ref(), config.chain_spec.fork_id()); + let mixnet_notification_service = mixnet_config.as_ref().map(|mixnet_config| { + let (config, notification_service) = + sc_mixnet::peers_set_config(mixnet_protocol_name.clone(), mixnet_config); + net_config.add_notification_protocol(config); + notification_service + }); + + let warp_sync = Arc::new(grandpa::warp_proof::NetworkProvider::new( + backend.clone(), + import_setup.1.shared_authority_set().clone(), + Vec::default(), + )); + + let (network, system_rpc_tx, tx_handler_controller, network_starter, sync_service) = + sc_service::build_network(sc_service::BuildNetworkParams { + config: &config, + net_config, + client: client.clone(), + transaction_pool: transaction_pool.clone(), + spawn_handle: task_manager.spawn_handle(), + import_queue, + block_announce_validator_builder: None, + warp_sync_params: Some(WarpSyncParams::WithProvider(warp_sync)), + block_relay: None, + })?; + + if let Some(mixnet_config) = mixnet_config { + let mixnet = sc_mixnet::run( + mixnet_config, + mixnet_api_backend.expect("Mixnet API backend created if mixnet enabled"), + client.clone(), + sync_service.clone(), + network.clone(), + mixnet_protocol_name, + transaction_pool.clone(), + Some(keystore_container.keystore()), + mixnet_notification_service + .expect("`NotificationService` exists since mixnet was enabled; qed"), + ); + task_manager.spawn_handle().spawn("mixnet", None, mixnet); + } + + let role = config.role.clone(); + let force_authoring = config.force_authoring; + let backoff_authoring_blocks = + Some(sc_consensus_slots::BackoffAuthoringOnFinalizedHeadLagging::default()); + let name = config.network.node_name.clone(); + let enable_grandpa = !config.disable_grandpa; + let prometheus_registry = config.prometheus_registry().cloned(); + let enable_offchain_worker = config.offchain_worker.enabled; + + let rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { + config, + backend: backend.clone(), + client: client.clone(), + keystore: keystore_container.keystore(), + network: network.clone(), + rpc_builder: Box::new(rpc_builder), + transaction_pool: transaction_pool.clone(), + task_manager: &mut task_manager, + system_rpc_tx, + tx_handler_controller, + sync_service: sync_service.clone(), + telemetry: telemetry.as_mut(), + })?; + + if let Some(hwbench) = hwbench { + sc_sysinfo::print_hwbench(&hwbench); + match SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) { + Err(err) if role.is_authority() => { + log::warn!( + "⚠️ The hardware does not meet the minimal requirements {} for role 'Authority'.", + err + ); + }, + _ => {}, + } + + if let Some(ref mut telemetry) = telemetry { + let telemetry_handle = telemetry.handle(); + task_manager.spawn_handle().spawn( + "telemetry_hwbench", + None, + sc_sysinfo::initialize_hwbench_telemetry(telemetry_handle, hwbench), + ); + } + } + + let (block_import, grandpa_link, babe_link) = import_setup; + + (with_startup_data)(&block_import, &babe_link); + + if let sc_service::config::Role::Authority { .. } = &role { + let proposer = 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 client_clone = client.clone(); + let slot_duration = babe_link.config().slot_duration(); + let babe_config = sc_consensus_babe::BabeParams { + keystore: keystore_container.keystore(), + client: client.clone(), + select_chain, + env: proposer, + block_import, + sync_oracle: sync_service.clone(), + justification_sync_link: sync_service.clone(), + create_inherent_data_providers: move |parent, ()| { + let client_clone = client_clone.clone(); + async move { + let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); + + let slot = + sp_consensus_babe::inherents::InherentDataProvider::from_timestamp_and_slot_duration( + *timestamp, + slot_duration, + ); + + let storage_proof = + sp_transaction_storage_proof::registration::new_data_provider( + &*client_clone, + &parent, + )?; + + Ok((slot, timestamp, storage_proof)) + } + }, + force_authoring, + backoff_authoring_blocks, + babe_link, + block_proposal_slot_portion: SlotProportion::new(0.5), + max_block_proposal_slot_portion: None, + telemetry: telemetry.as_ref().map(|x| x.handle()), + }; + + let babe = sc_consensus_babe::start_babe(babe_config)?; + task_manager.spawn_essential_handle().spawn_blocking( + "babe-proposer", + Some("block-authoring"), + babe, + ); + } + + // Spawn authority discovery module. + if role.is_authority() { + let authority_discovery_role = + sc_authority_discovery::Role::PublishAndDiscover(keystore_container.keystore()); + let dht_event_stream = + network.event_stream("authority-discovery").filter_map(|e| async move { + match e { + Event::Dht(e) => Some(e), + _ => None, + } + }); + let (authority_discovery_worker, _service) = + sc_authority_discovery::new_worker_and_service_with_config( + sc_authority_discovery::WorkerConfig { + publish_non_global_ips: auth_disc_publish_non_global_ips, + ..Default::default() + }, + client.clone(), + network.clone(), + Box::pin(dht_event_stream), + authority_discovery_role, + prometheus_registry.clone(), + ); + + task_manager.spawn_handle().spawn( + "authority-discovery-worker", + Some("networking"), + authority_discovery_worker.run(), + ); + } + + // if the node isn't actively participating in consensus then it doesn't + // need a keystore, regardless of which protocol we use below. + let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; + + let grandpa_config = grandpa::Config { + // FIXME #1578 make this available through chainspec + gossip_duration: std::time::Duration::from_millis(333), + justification_generation_period: GRANDPA_JUSTIFICATION_PERIOD, + name: Some(name), + observer_enabled: false, + keystore, + local_role: role.clone(), + telemetry: telemetry.as_ref().map(|x| x.handle()), + protocol_name: grandpa_protocol_name, + }; + + if enable_grandpa { + // start the full GRANDPA voter + // NOTE: non-authorities could run the GRANDPA observer protocol, but at + // this point the full voter should provide better guarantees of block + // and vote data availability than the observer. The observer has not + // been tested extensively yet and having most nodes in a network run it + // could lead to finality stalls. + let grandpa_params = grandpa::GrandpaParams { + config: grandpa_config, + link: grandpa_link, + network: network.clone(), + sync: Arc::new(sync_service.clone()), + notification_service: grandpa_notification_service, + telemetry: telemetry.as_ref().map(|x| x.handle()), + voting_rule: grandpa::VotingRulesBuilder::default().build(), + prometheus_registry: prometheus_registry.clone(), + shared_voter_state, + offchain_tx_pool_factory: OffchainTransactionPoolFactory::new(transaction_pool.clone()), + }; + + // the GRANDPA voter task is considered infallible, i.e. + // if it fails we take down the service with it. + task_manager.spawn_essential_handle().spawn_blocking( + "grandpa-voter", + None, + grandpa::run_grandpa_voter(grandpa_params)?, + ); + } + + // Spawn statement protocol worker + let statement_protocol_executor = { + let spawn_handle = task_manager.spawn_handle(); + Box::new(move |fut| { + spawn_handle.spawn("network-statement-validator", Some("networking"), fut); + }) + }; + let statement_handler = statement_handler_proto.build( + network.clone(), + sync_service.clone(), + statement_store.clone(), + prometheus_registry.as_ref(), + statement_protocol_executor, + )?; + task_manager.spawn_handle().spawn( + "network-statement-handler", + Some("networking"), + statement_handler.run(), + ); + + if enable_offchain_worker { + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-work", + sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { + runtime_api_provider: client.clone(), + keystore: Some(keystore_container.keystore()), + offchain_db: backend.offchain_storage(), + transaction_pool: Some(OffchainTransactionPoolFactory::new( + transaction_pool.clone(), + )), + network_provider: network.clone(), + is_validator: role.is_authority(), + enable_http_requests: true, + custom_extensions: move |_| { + vec![Box::new(statement_store.clone().as_statement_store_ext()) as Box<_>] + }, + }) + .run(client.clone(), task_manager.spawn_handle()) + .boxed(), + ); + } + + network_starter.start_network(); + Ok(NewFullBase { + task_manager, + client, + network, + sync: sync_service, + transaction_pool, + rpc_handlers, + }) +} + +/// Builds a new service for a full client. +pub fn new_full(config: Configuration, cli: Cli) -> Result { + let mixnet_config = cli.mixnet_params.config(config.role.is_authority()); + let database_source = config.database.clone(); + let task_manager = new_full_base(config, mixnet_config, cli.no_hardware_benchmarks, |_, _| ()) + .map(|NewFullBase { task_manager, .. }| task_manager)?; + + sc_storage_monitor::StorageMonitorService::try_spawn( + cli.storage_monitor, + database_source, + &task_manager.spawn_essential_handle(), + ) + .map_err(|e| ServiceError::Application(e.into()))?; + + Ok(task_manager) +} + +#[cfg(test)] +mod tests { + use crate::service::{new_full_base, NewFullBase}; + use codec::Encode; + use kitchensink_runtime::{ + constants::{currency::CENTS, time::SLOT_DURATION}, + Address, BalancesCall, RuntimeCall, UncheckedExtrinsic, + }; + use node_primitives::{Block, DigestItem, Signature}; + use sc_client_api::BlockBackend; + use sc_consensus::{BlockImport, BlockImportParams, ForkChoiceStrategy}; + use sc_consensus_babe::{BabeIntermediate, CompatibleDigestItem, INTERMEDIATE_KEY}; + use sc_consensus_epochs::descendent_query; + use sc_keystore::LocalKeystore; + use sc_service_test::TestNetNode; + use sc_transaction_pool_api::{ChainEvent, MaintainedTransactionPool}; + use sp_consensus::{BlockOrigin, Environment, Proposer}; + use sp_core::crypto::Pair; + use sp_inherents::InherentDataProvider; + use sp_keyring::AccountKeyring; + use sp_keystore::KeystorePtr; + use sp_runtime::{ + generic::{Digest, Era, SignedPayload}, + key_types::BABE, + traits::{Block as BlockT, Header as HeaderT, IdentifyAccount, Verify}, + RuntimeAppPublic, + }; + use sp_timestamp; + use std::sync::Arc; + + type AccountPublic = ::Signer; + + #[test] + // It is "ignored", but the node-cli ignored tests are running on the CI. + // This can be run locally with `cargo test --release -p node-cli test_sync -- --ignored`. + #[ignore] + fn test_sync() { + sp_tracing::try_init_simple(); + + let keystore_path = tempfile::tempdir().expect("Creates keystore path"); + let keystore: KeystorePtr = LocalKeystore::open(keystore_path.path(), None) + .expect("Creates keystore") + .into(); + let alice: sp_consensus_babe::AuthorityId = keystore + .sr25519_generate_new(BABE, Some("//Alice")) + .expect("Creates authority pair") + .into(); + + let chain_spec = crate::chain_spec::tests::integration_test_config_with_single_authority(); + + // For the block factory + let mut slot = 1u64; + + // For the extrinsics factory + let bob = Arc::new(AccountKeyring::Bob.pair()); + let charlie = Arc::new(AccountKeyring::Charlie.pair()); + let mut index = 0; + + sc_service_test::sync( + chain_spec, + |config| { + let mut setup_handles = None; + let NewFullBase { task_manager, client, network, sync, transaction_pool, .. } = + new_full_base( + config, + None, + false, + |block_import: &sc_consensus_babe::BabeBlockImport, + babe_link: &sc_consensus_babe::BabeLink| { + setup_handles = Some((block_import.clone(), babe_link.clone())); + }, + )?; + + let node = sc_service_test::TestNetComponents::new( + task_manager, + client, + network, + sync, + transaction_pool, + ); + Ok((node, setup_handles.unwrap())) + }, + |service, &mut (ref mut block_import, ref babe_link)| { + let parent_hash = service.client().chain_info().best_hash; + let parent_header = service.client().header(parent_hash).unwrap().unwrap(); + let parent_number = *parent_header.number(); + + futures::executor::block_on(service.transaction_pool().maintain( + ChainEvent::NewBestBlock { hash: parent_header.hash(), tree_route: None }, + )); + + let mut proposer_factory = sc_basic_authorship::ProposerFactory::new( + service.spawn_handle(), + service.client(), + service.transaction_pool(), + None, + None, + ); + + let mut digest = Digest::default(); + + // even though there's only one authority some slots might be empty, + // so we must keep trying the next slots until we can claim one. + let (babe_pre_digest, epoch_descriptor) = loop { + let epoch_descriptor = babe_link + .epoch_changes() + .shared_data() + .epoch_descriptor_for_child_of( + descendent_query(&*service.client()), + &parent_hash, + parent_number, + slot.into(), + ) + .unwrap() + .unwrap(); + + let epoch = babe_link + .epoch_changes() + .shared_data() + .epoch_data(&epoch_descriptor, |slot| { + sc_consensus_babe::Epoch::genesis(babe_link.config(), slot) + }) + .unwrap(); + + if let Some(babe_pre_digest) = + sc_consensus_babe::authorship::claim_slot(slot.into(), &epoch, &keystore) + .map(|(digest, _)| digest) + { + break (babe_pre_digest, epoch_descriptor) + } + + slot += 1; + }; + + let inherent_data = futures::executor::block_on( + ( + sp_timestamp::InherentDataProvider::new( + std::time::Duration::from_millis(SLOT_DURATION * slot).into(), + ), + sp_consensus_babe::inherents::InherentDataProvider::new(slot.into()), + ) + .create_inherent_data(), + ) + .expect("Creates inherent data"); + + digest.push(::babe_pre_digest(babe_pre_digest)); + + let new_block = futures::executor::block_on(async move { + let proposer = proposer_factory.init(&parent_header).await; + proposer + .unwrap() + .propose(inherent_data, digest, std::time::Duration::from_secs(1), None) + .await + }) + .expect("Error making test block") + .block; + + let (new_header, new_body) = new_block.deconstruct(); + let pre_hash = new_header.hash(); + // sign the pre-sealed hash of the block and then + // add it to a digest item. + let to_sign = pre_hash.encode(); + let signature = keystore + .sr25519_sign(sp_consensus_babe::AuthorityId::ID, alice.as_ref(), &to_sign) + .unwrap() + .unwrap(); + let item = ::babe_seal(signature.into()); + slot += 1; + + let mut params = BlockImportParams::new(BlockOrigin::File, new_header); + params.post_digests.push(item); + params.body = Some(new_body); + params.insert_intermediate( + INTERMEDIATE_KEY, + BabeIntermediate:: { epoch_descriptor }, + ); + params.fork_choice = Some(ForkChoiceStrategy::LongestChain); + + futures::executor::block_on(block_import.import_block(params)) + .expect("error importing test block"); + }, + |service, _| { + let amount = 5 * CENTS; + let to: Address = AccountPublic::from(bob.public()).into_account().into(); + let from: Address = AccountPublic::from(charlie.public()).into_account().into(); + let genesis_hash = service.client().block_hash(0).unwrap().unwrap(); + let best_hash = service.client().chain_info().best_hash; + let (spec_version, transaction_version) = { + let version = service.client().runtime_version_at(best_hash).unwrap(); + (version.spec_version, version.transaction_version) + }; + let signer = charlie.clone(); + + let function = RuntimeCall::Balances(BalancesCall::transfer_allow_death { + dest: to.into(), + value: amount, + }); + + let check_non_zero_sender = frame_system::CheckNonZeroSender::new(); + let check_spec_version = frame_system::CheckSpecVersion::new(); + let check_tx_version = frame_system::CheckTxVersion::new(); + let check_genesis = frame_system::CheckGenesis::new(); + let check_era = frame_system::CheckEra::from(Era::Immortal); + let check_nonce = frame_system::CheckNonce::from(index); + let check_weight = frame_system::CheckWeight::new(); + let tx_payment = pallet_skip_feeless_payment::SkipCheckIfFeeless::from( + pallet_asset_conversion_tx_payment::ChargeAssetTxPayment::from(0, None), + ); + let extra = ( + check_non_zero_sender, + check_spec_version, + check_tx_version, + check_genesis, + check_era, + check_nonce, + check_weight, + tx_payment, + ); + let raw_payload = SignedPayload::from_raw( + function, + extra, + ((), spec_version, transaction_version, genesis_hash, genesis_hash, (), (), ()), + ); + let signature = raw_payload.using_encoded(|payload| signer.sign(payload)); + let (function, extra, _) = raw_payload.deconstruct(); + index += 1; + UncheckedExtrinsic::new_signed(function, from.into(), signature.into(), extra) + .into() + }, + ); + } + + #[test] + #[ignore] + fn test_consensus() { + sp_tracing::try_init_simple(); + + sc_service_test::consensus( + crate::chain_spec::tests::integration_test_config_with_two_authorities(), + |config| { + let NewFullBase { task_manager, client, network, sync, transaction_pool, .. } = + new_full_base(config, None, false, |_, _| ())?; + Ok(sc_service_test::TestNetComponents::new( + task_manager, + client, + network, + sync, + transaction_pool, + )) + }, + vec!["//Alice".into(), "//Bob".into()], + ) + } +} diff --git a/node/service/src/chainspec/mainnet.rs b/node/service/src/chainspec/mainnet.rs index 2cff7c474..41ffbe140 100644 --- a/node/service/src/chainspec/mainnet.rs +++ b/node/service/src/chainspec/mainnet.rs @@ -22,7 +22,7 @@ use core::marker::PhantomData; use pallet_im_online::sr25519::AuthorityId as ImOnlineId; use sc_consensus_grandpa::AuthorityId as GrandpaId; use sc_service::ChainType; -use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_consensus_babe::AuthorityId as BabeId; use sp_core::{sr25519, Pair, Public, H160}; use sp_runtime::traits::{IdentifyAccount, Verify}; use tangle_mainnet_runtime::{ @@ -52,15 +52,15 @@ where AccountPublic::from(get_from_seed::(seed)).into_account() } -/// Generate an Aura authority key. +/// Generate the authority key set and account keys. pub fn authority_keys_from_seed( controller: &str, stash: &str, -) -> (AccountId, AccountId, AuraId, GrandpaId, ImOnlineId) { +) -> (AccountId, AccountId, BabeId, GrandpaId, ImOnlineId) { ( get_account_id_from_seed::(controller), get_account_id_from_seed::(stash), - get_from_seed::(controller), + get_from_seed::(controller), get_from_seed::(controller), get_from_seed::(stash), ) @@ -72,10 +72,10 @@ pub fn authority_keys_from_seed( /// have just one key). fn session_keys( grandpa: GrandpaId, - aura: AuraId, + babe: BabeId, im_online: ImOnlineId, ) -> tangle_mainnet_runtime::opaque::SessionKeys { - tangle_mainnet_runtime::opaque::SessionKeys { grandpa, aura, im_online } + tangle_mainnet_runtime::opaque::SessionKeys { grandpa, babe, im_online } } pub fn local_testnet_config(chain_id: u64) -> Result { @@ -211,7 +211,7 @@ pub fn tangle_mainnet_config(chain_id: u64) -> Result { #[allow(clippy::too_many_arguments)] fn testnet_genesis( wasm_binary: &[u8], - initial_authorities: Vec<(AccountId, AccountId, AuraId, GrandpaId, ImOnlineId)>, + initial_authorities: Vec<(AccountId, AccountId, BabeId, GrandpaId, ImOnlineId)>, initial_nominators: Vec, root_key: AccountId, endowed_accounts: Vec, @@ -287,7 +287,7 @@ fn testnet_genesis( .collect(), }, treasury: Default::default(), - aura: Default::default(), + babe: Default::default(), grandpa: Default::default(), im_online: ImOnlineConfig { keys: vec![] }, eth_2_client: Eth2ClientConfig { @@ -322,7 +322,7 @@ fn testnet_genesis( #[allow(clippy::too_many_arguments)] fn mainnet_genesis( wasm_binary: &[u8], - initial_authorities: Vec<(AccountId, AccountId, AuraId, GrandpaId, ImOnlineId)>, + initial_authorities: Vec<(AccountId, AccountId, BabeId, GrandpaId, ImOnlineId)>, root_key: AccountId, endowed_accounts: Vec, chain_id: u64, @@ -377,7 +377,7 @@ fn mainnet_genesis( council: Default::default(), elections: Default::default(), treasury: Default::default(), - aura: Default::default(), + babe: Default::default(), grandpa: Default::default(), im_online: ImOnlineConfig { keys: vec![] }, nomination_pools: Default::default(), diff --git a/node/service/src/distributions/mainnet.rs b/node/service/src/distributions/mainnet.rs index fd4ac5660..7619c7f17 100644 --- a/node/service/src/distributions/mainnet.rs +++ b/node/service/src/distributions/mainnet.rs @@ -30,7 +30,9 @@ fn read_contents_to_substrate_accounts(path_str: &str) -> BTreeMap Vec { - read_contents_to_evm_accounts("node/service/src/distributions/data/edgeware_genesis_participants.json") + read_contents_to_evm_accounts( + "node/service/src/distributions/data/edgeware_genesis_participants.json", + ) } fn get_edgeware_snapshot_list() -> BTreeMap { diff --git a/node/service/src/distributions/testnet.rs b/node/service/src/distributions/testnet.rs index 282fb3d1b..a9403fb19 100644 --- a/node/service/src/distributions/testnet.rs +++ b/node/service/src/distributions/testnet.rs @@ -61,7 +61,9 @@ fn read_contents_to_substrate_accounts(path_str: &str) -> Vec { } fn get_edgeware_genesis_list() -> Vec { - read_contents_to_evm_accounts("node/service/src/distributions/data/edgeware_genesis_participants.json") + read_contents_to_evm_accounts( + "node/service/src/distributions/data/edgeware_genesis_participants.json", + ) } fn get_edgeware_snapshot_list() -> Vec { diff --git a/node/service/src/lib.rs b/node/service/src/lib.rs index ef8440238..3e4553a55 100644 --- a/node/service/src/lib.rs +++ b/node/service/src/lib.rs @@ -1,23 +1,13 @@ -pub mod chainspec; -pub mod client; -pub mod distributions; -pub mod eth; -pub mod fixtures; -pub mod rpc; -pub mod utils; - -use crate::eth::{ +use client::{Client, RuntimeApiCollection}; +use eth::{ new_frontier_partial, spawn_frontier_tasks, BackendType, EthApi, EthConfiguration, - FrontierPartialComponents, + FrontierPartialComponents, RpcConfig, }; -use client::{Client, RuntimeApiCollection}; -use eth::RpcConfig; use fc_consensus::FrontierBlockImport as TFrontierBlockImport; use fc_db::DatabaseSource; use futures::{channel::mpsc, FutureExt}; use sc_chain_spec::ChainSpec; use sc_client_api::{AuxStore, Backend, BlockBackend, StateBackend, StorageProvider}; -use sc_consensus_aura::ImportQueueParams; use sc_consensus_grandpa::SharedVoterState; pub use sc_executor::NativeElseWasmExecutor; use sc_service::{ @@ -28,12 +18,30 @@ use sc_telemetry::{Telemetry, TelemetryWorker}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; use sp_api::{ConstructRuntimeApi, ProvideRuntimeApi}; use sp_blockchain::{Error as BlockChainError, HeaderBackend, HeaderMetadata}; -use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use sp_core::U256; use tangle_primitives::Block; +use sc_consensus_aura::ImportQueueParams; +use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; + use std::{path::Path, sync::Arc, time::Duration}; +use sp_runtime::{traits::BlakeTwo256, Percent}; + +pub mod chainspec; +pub mod client; +pub mod distributions; +pub mod eth; +pub mod fixtures; +pub mod rpc; +pub mod utils; + +#[cfg(feature = "testnet")] +pub mod aura; + +#[cfg(feature = "tangle")] +pub mod babe; + /// The minimum period of blocks on which justifications will be /// imported and generated. const GRANDPA_JUSTIFICATION_PERIOD: u32 = 512; @@ -68,7 +76,18 @@ type PartialComponentsResult = Result< ServiceError, >; -pub type HostFunctions = (frame_benchmarking::benchmarking::HostFunctions, ()); +/// Host functions required for kitchensink runtime and Substrate node. +#[cfg(not(feature = "runtime-benchmarks"))] +pub type HostFunctions = + (sp_io::SubstrateHostFunctions, sp_statement_store::runtime_api::HostFunctions); + +/// Host functions required for kitchensink runtime and Substrate node. +#[cfg(feature = "runtime-benchmarks")] +pub type HostFunctions = ( + sp_io::SubstrateHostFunctions, + sp_statement_store::runtime_api::HostFunctions, + frame_benchmarking::benchmarking::HostFunctions, +); #[cfg(feature = "tangle")] pub struct TangleExecutor; @@ -212,585 +231,3 @@ where Ok(frontier_backend) } - -use sp_runtime::{traits::BlakeTwo256, Percent}; - -pub const SOFT_DEADLINE_PERCENT: Percent = Percent::from_percent(100); - -/// Builds a new object suitable for chain operations. -#[allow(clippy::type_complexity)] -pub fn new_chain_ops( - config: &mut Configuration, - eth_config: &EthConfiguration, -) -> Result< - (Arc, Arc, sc_consensus::BasicQueue, TaskManager), - ServiceError, -> { - match &config.chain_spec { - #[cfg(feature = "testnet")] - spec if spec.is_testnet() => new_chain_ops_inner::< - tangle_testnet_runtime::RuntimeApi, - TestnetExecutor, - >(config, eth_config), - #[cfg(feature = "tangle")] - spec if spec.is_tangle() => new_chain_ops_inner::< - tangle_mainnet_runtime::RuntimeApi, - TangleExecutor, - >(config, eth_config), - _ => panic!("invalid chain spec"), - } -} - -#[allow(clippy::type_complexity)] -fn new_chain_ops_inner( - config: &mut Configuration, - eth_config: &EthConfiguration, -) -> Result< - (Arc, Arc, sc_consensus::BasicQueue, TaskManager), - ServiceError, -> -where - Client: From>>, - RuntimeApi: - ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: RuntimeApiCollection, - Executor: sc_executor::NativeExecutionDispatch + 'static, -{ - config.keystore = sc_service::config::KeystoreConfig::InMemory; - let PartialComponents { client, backend, import_queue, task_manager, .. } = - new_partial::(config, eth_config)?; - Ok((Arc::new(Client::from(client)), backend, import_queue, task_manager)) -} - -pub fn new_partial( - config: &Configuration, - eth_config: &EthConfiguration, -) -> PartialComponentsResult -where - RuntimeApi: - ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: RuntimeApiCollection, - Executor: sc_executor::NativeExecutionDispatch + 'static, -{ - println!(" ++++++++++++++++++++++++ - +++++++++++++++++++++++++++ - +++++++++++++++++++++++++++ - +++ ++++++ +++ @%%%%%%%%%%% %%% - ++++++ ++++ +++++ %%%%%%%%%%%% %%%@ - ++++++++++++++++++++++++++ %%%% %%%%@ %%% %%@ @%%%%%%% %%%@ %%%%@ - ++++++++ %%%% @%%%%%%%@ %%%%%%%%% @%%%%%%%%% %%%@ %%%%%%%%% - ++++++++ %%%% %%%%%%%%% %%%% @%%%@ %%%% %%%% %%%@ %%%%%%%%%% - ++++++++++++++++++++++++++ %%%% %%%%%%%%% %%% %%%% %%% @%%% %%%@ @%%%%% %%%%% - ++++++ ++++ ++++++ %%%% %%%%%%%%% %%% %%%% %%%%%%%%%% %%%@ %%%%%%%%%@ - +++ ++++++ +++ %%%% %%%%%%%%% %%% %%%@ %%%%%%%%% %%% %%%%%%%@ - ++++ +++++++++ +++ %%%% %%%% - ++++++++++++++++++++++++++++ %%%%%%%%% - +++++++++++++++++++++++ %%%%% \n"); - let telemetry = config - .telemetry_endpoints - .clone() - .filter(|x| !x.is_empty()) - .map(|endpoints| -> Result<_, sc_telemetry::Error> { - let worker = TelemetryWorker::new(16)?; - let telemetry = worker.handle().new_telemetry(endpoints); - Ok((worker, telemetry)) - }) - .transpose()?; - - let executor = sc_service::new_native_or_wasm_executor(config); - - let (client, backend, keystore_container, task_manager) = - sc_service::new_full_parts::( - config, - telemetry.as_ref().map(|(_, telemetry)| telemetry.handle()), - executor, - )?; - let client = Arc::new(client); - - let telemetry = telemetry.map(|(worker, telemetry)| { - task_manager.spawn_handle().spawn("telemetry", None, worker.run()); - telemetry - }); - - let select_chain = sc_consensus::LongestChain::new(backend.clone()); - - let transaction_pool = sc_transaction_pool::BasicPool::new_full( - config.transaction_pool.clone(), - config.role.is_authority().into(), - config.prometheus_registry(), - task_manager.spawn_essential_handle(), - client.clone(), - ); - - let (grandpa_block_import, grandpa_link) = sc_consensus_grandpa::block_import( - client.clone(), - GRANDPA_JUSTIFICATION_PERIOD, - &client, - select_chain.clone(), - telemetry.as_ref().map(|x| x.handle()), - )?; - - let overrides = crate::rpc::overrides_handle(client.clone()); - let frontier_backend = open_frontier_backend(client.clone(), config, ð_config)?; - - let frontier_block_import = FrontierBlockImport::new(client.clone(), client.clone()); - - 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::(ImportQueueParams { - block_import: frontier_block_import.clone(), - justification_import: Some(Box::new(grandpa_block_import.clone())), - 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: Default::default(), - })?; - - Ok(sc_service::PartialComponents { - client, - backend, - keystore_container, - task_manager, - select_chain, - import_queue, - transaction_pool, - other: ( - telemetry, - Box::new(frontier_block_import), - grandpa_link, - frontier_backend, - overrides, - ), - }) -} - -pub struct RunFullParams { - pub config: Configuration, - pub eth_config: EthConfiguration, - pub rpc_config: RpcConfig, - pub debug_output: Option, - #[cfg(feature = "relayer")] - pub relayer_cmd: tangle_relayer_gadget_cli::RelayerCmd, - #[cfg(feature = "light-client")] - pub light_client_relayer_cmd: - pallet_eth2_light_client_relayer_gadget_cli::LightClientRelayerCmd, - pub auto_insert_keys: bool, -} - -/// Builds a new service for a full client. -pub async fn new_full( - RunFullParams { - mut config, - eth_config, - rpc_config, - debug_output: _, - #[cfg(feature = "relayer")] - relayer_cmd, - #[cfg(feature = "light-client")] - light_client_relayer_cmd, - auto_insert_keys, - }: RunFullParams, -) -> Result -where - RuntimeApi: - ConstructRuntimeApi> + Send + Sync + 'static, - RuntimeApi::RuntimeApi: RuntimeApiCollection, - Executor: sc_executor::NativeExecutionDispatch + 'static, -{ - let sc_service::PartialComponents { - client, - backend, - mut task_manager, - import_queue, - keystore_container, - select_chain, - transaction_pool, - other: (mut telemetry, block_import, grandpa_link, frontier_backend, overrides), - } = new_partial::(&config, ð_config)?; - - if config.role.is_authority() { - if auto_insert_keys { - crate::utils::insert_controller_account_keys_into_keystore( - &config, - Some(keystore_container.keystore()), - ); - } else { - crate::utils::insert_dev_controller_account_keys_into_keystore( - &config, - Some(keystore_container.keystore()), - ); - } - - // finally check if keys are inserted correctly - if config.chain_spec.chain_type() != ChainType::Development { - if crate::utils::ensure_all_keys_exist_in_keystore(keystore_container.keystore()) - .is_err() - { - println!(" - ++++++++++++++++++++++++++++++++++++++++++++++++ - Validator keys not found, validator keys are essential to run a validator on - Tangle Network, refer to https://docs.webb.tools/docs/ecosystem-roles/validator/required-keys/ on - how to generate and insert keys. OR start the node with --auto-insert-keys to automatically generate the keys. - ++++++++++++++++++++++++++++++++++++++++++++++++ - \n"); - panic!("Keys not detected!") - } - } - } - - let FrontierPartialComponents { filter_pool, fee_history_cache, fee_history_cache_limit } = - new_frontier_partial(ð_config)?; - - let mut net_config = sc_network::config::FullNetworkConfiguration::new(&config.network); - - let grandpa_protocol_name = sc_consensus_grandpa::protocol_standard_name( - &client.block_hash(0).ok().flatten().expect("Genesis block exists; qed"), - &config.chain_spec, - ); - - net_config.add_notification_protocol(sc_consensus_grandpa::grandpa_peers_set_config( - grandpa_protocol_name.clone(), - )); - - let warp_sync = Arc::new(sc_consensus_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, sync_service) = - sc_service::build_network(sc_service::BuildNetworkParams { - config: &config, - net_config, - client: client.clone(), - transaction_pool: transaction_pool.clone(), - spawn_handle: task_manager.spawn_handle(), - import_queue, - block_announce_validator_builder: None, - warp_sync_params: Some(sc_service::WarpSyncParams::WithProvider(warp_sync)), - })?; - - let role = config.role.clone(); - let force_authoring = config.force_authoring; - let _backoff_authoring_blocks: Option<()> = None; - let name = config.network.node_name.clone(); - let enable_grandpa = !config.disable_grandpa; - let prometheus_registry = config.prometheus_registry().cloned(); - - if config.offchain_worker.enabled { - task_manager.spawn_handle().spawn( - "offchain-workers-runner", - "offchain-work", - sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { - runtime_api_provider: client.clone(), - keystore: Some(keystore_container.keystore()), - offchain_db: backend.offchain_storage(), - transaction_pool: Some(OffchainTransactionPoolFactory::new( - transaction_pool.clone(), - )), - network_provider: network.clone(), - is_validator: role.is_authority(), - enable_http_requests: true, - custom_extensions: move |_| vec![], - }) - .run(client.clone(), task_manager.spawn_handle()) - .boxed(), - ); - } - - // Channel for the rpc handler to communicate with the authorship task. - let (command_sink, _commands_stream) = mpsc::channel(1000); - - // Sinks for pubsub notifications. - // Everytime a new subscription is created, a new mpsc channel is added to the sink pool. - // The MappingSyncWorker sends through the channel on block import and the subscription emits a - // notification to the subscriber on receiving a message through this channel. This way we avoid - // race conditions when using native substrate block import notification stream. - let pubsub_notification_sinks: fc_mapping_sync::EthereumBlockNotificationSinks< - fc_mapping_sync::EthereumBlockNotification, - > = Default::default(); - let pubsub_notification_sinks = Arc::new(pubsub_notification_sinks); - - // for ethereum-compatibility rpc. - config.rpc_id_provider = Some(Box::new(fc_rpc::EthereumSubIdProvider)); - - 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)) - }; - - let ethapi_cmd = rpc_config.ethapi.clone(); - let tracing_requesters = - if ethapi_cmd.contains(&EthApi::Debug) || ethapi_cmd.contains(&EthApi::Trace) { - crate::rpc::tracing::spawn_tracing_tasks( - &task_manager, - client.clone(), - backend.clone(), - frontier_backend.clone(), - overrides.clone(), - &rpc_config, - prometheus_registry.clone(), - ) - } else { - crate::rpc::tracing::RpcRequesters { debug: None, trace: None } - }; - let eth_rpc_params = crate::rpc::EthDeps { - client: client.clone(), - pool: transaction_pool.clone(), - graph: transaction_pool.pool().clone(), - #[cfg(feature = "tangle")] - converter: Some(tangle_mainnet_runtime::TransactionConverter), - #[cfg(feature = "testnet")] - converter: Some(tangle_testnet_runtime::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( - 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, - tracing_config: Some(crate::rpc::eth::TracingConfig { - tracing_requesters: tracing_requesters.clone(), - trace_filter_max_count: rpc_config.ethapi_trace_max_count, - }), - pending_create_inherent_data_providers, - }; - - let rpc_builder = { - let client = client.clone(); - let pool = transaction_pool.clone(); - let pubsub_notification_sinks = pubsub_notification_sinks.clone(); - Box::new(move |deny_unsafe, subscription_task_executor| { - let deps = crate::rpc::FullDeps { - client: client.clone(), - pool: pool.clone(), - deny_unsafe, - command_sink: Some(command_sink.clone()), - eth: eth_rpc_params.clone(), - }; - if ethapi_cmd.contains(&EthApi::Debug) || ethapi_cmd.contains(&EthApi::Trace) { - crate::rpc::create_full( - deps, - subscription_task_executor, - pubsub_notification_sinks.clone(), - ) - .map_err(Into::into) - } else { - crate::rpc::create_full( - deps, - subscription_task_executor, - pubsub_notification_sinks.clone(), - ) - .map_err(Into::into) - } - }) - }; - - spawn_frontier_tasks( - &task_manager, - client.clone(), - backend.clone(), - frontier_backend, - filter_pool, - overrides, - fee_history_cache, - fee_history_cache_limit, - sync_service.clone(), - pubsub_notification_sinks, - ) - .await; - - if role.is_authority() { - // setup relayer gadget params - #[cfg(feature = "relayer")] - let relayer_params = tangle_relayer_gadget::RelayerParams { - local_keystore: keystore_container.local_keystore(), - config_dir: relayer_cmd.relayer_config_dir, - database_path: config - .database - .path() - .and_then(|path| path.parent()) - .map(|p| p.to_path_buf()), - rpc_addr: config.rpc_addr, - }; - - // Start Webb Relayer Gadget as non-essential task. - #[cfg(feature = "relayer")] - task_manager.spawn_handle().spawn( - "relayer-gadget", - None, - tangle_relayer_gadget::start_relayer_gadget( - relayer_params, - sp_application_crypto::KeyTypeId(*b"role"), - ), - ); - - // Start Eth2 Light client Relayer Gadget - (MAINNET RELAYER) - #[cfg(feature = "light-client")] - task_manager.spawn_handle().spawn( - "mainnet-relayer-gadget", - None, - pallet_eth2_light_client_relayer_gadget::start_gadget( - pallet_eth2_light_client_relayer_gadget::Eth2LightClientParams { - lc_relay_config_path: light_client_relayer_cmd - .light_client_relay_config_path - .clone(), - lc_init_config_path: light_client_relayer_cmd - .light_client_init_pallet_config_path - .clone(), - eth2_chain_id: webb_proposals::TypedChainId::Evm(1), - }, - ), - ); - } - let params = sc_service::SpawnTasksParams { - network: network.clone(), - client: client.clone(), - keystore: keystore_container.keystore(), - task_manager: &mut task_manager, - transaction_pool: transaction_pool.clone(), - rpc_builder, - backend: backend.clone(), - system_rpc_tx, - tx_handler_controller, - sync_service: sync_service.clone(), - config, - telemetry: telemetry.as_mut(), - }; - let _rpc_handlers = sc_service::spawn_tasks(params)?; - - if role.is_authority() { - let proposer_factory = 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 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 aura = sc_consensus_aura::start_aura::( - sc_consensus_aura::StartAuraParams { - slot_duration, - client, - select_chain, - block_import, - proposer_factory, - sync_oracle: sync_service.clone(), - justification_sync_link: sync_service.clone(), - create_inherent_data_providers, - force_authoring, - backoff_authoring_blocks: Option::<()>::None, - keystore: keystore_container.keystore(), - block_proposal_slot_portion: sc_consensus_aura::SlotProportion::new(2f32 / 3f32), - max_block_proposal_slot_portion: None, - telemetry: telemetry.as_ref().map(|x| x.handle()), - compatibility_mode: sc_consensus_aura::CompatibilityMode::None, - }, - )?; - - // the AURA authoring task is considered essential, i.e. if it - // fails we take down the service with it. - task_manager - .spawn_essential_handle() - .spawn_blocking("aura", Some("block-authoring"), aura); - } - - // if the node isn't actively participating in consensus then it doesn't - // need a keystore, regardless of which protocol we use below. - let keystore = if role.is_authority() { Some(keystore_container.keystore()) } else { None }; - - let grandpa_config = sc_consensus_grandpa::Config { - // FIXME #1578 make this available through chainspec - gossip_duration: Duration::from_millis(333), - justification_generation_period: GRANDPA_JUSTIFICATION_PERIOD, - name: Some(name), - observer_enabled: false, - keystore, - local_role: role, - telemetry: telemetry.as_ref().map(|x| x.handle()), - protocol_name: grandpa_protocol_name, - }; - - if enable_grandpa { - // start the full GRANDPA voter - // NOTE: non-authorities could run the GRANDPA observer protocol, but at - // this point the full voter should provide better guarantees of block - // and vote data availability than the observer. The observer has not - // been tested extensively yet and having most nodes in a network run it - // could lead to finality stalls. - let grandpa_config = sc_consensus_grandpa::GrandpaParams { - config: grandpa_config, - link: grandpa_link, - network, - sync: Arc::new(sync_service), - voting_rule: sc_consensus_grandpa::VotingRulesBuilder::default().build(), - prometheus_registry, - shared_voter_state: SharedVoterState::empty(), - telemetry: telemetry.as_ref().map(|x| x.handle()), - offchain_tx_pool_factory: OffchainTransactionPoolFactory::new(transaction_pool), - }; - - // the GRANDPA voter task is considered infallible, i.e. - // if it fails we take down the service with it. - task_manager.spawn_essential_handle().spawn_blocking( - "grandpa-voter", - None, - sc_consensus_grandpa::run_grandpa_voter(grandpa_config)?, - ); - } - - network_starter.start_network(); - Ok(task_manager) -} diff --git a/node/src/cli.rs b/node/src/cli.rs index 533fc0a52..8b42ea032 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -11,6 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. + #![allow(clippy::all, deprecated)] use sc_cli::RunCmd; diff --git a/node/src/command.rs b/node/src/command.rs index 3bdad9752..b8da338b0 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -456,7 +456,7 @@ pub fn run() -> sc_cli::Result<()> { new_full::< tangle_testnet_runtime::RuntimeApi, tangle_service::TestnetExecutor, - >(tangle_service::RunFullParams { + >(tangle_service::aura::RunFullParams { config, rpc_config, eth_config: cli.eth, diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index b424b4845..a069e4434 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -16,6 +16,7 @@ serde = { workspace = true } smallvec = { workspace = true } sp-arithmetic = { workspace = true } sp-consensus-aura = { workspace = true } +sp-consensus-babe = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index f2731a599..d4dae4ce8 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -56,6 +56,18 @@ pub mod time { pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); pub const HOURS: BlockNumber = MINUTES * 60; pub const DAYS: BlockNumber = HOURS * 24; + + // 1 in 4 blocks (on average, not counting collisions) will be primary BABE blocks. + pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4); + + // NOTE: Currently it is not possible to change the epoch duration after the chain has started. + // Attempting to do so will brick block production. + pub const EPOCH_DURATION_IN_BLOCKS: BlockNumber = 10 * MINUTES; + pub const EPOCH_DURATION_IN_SLOTS: u64 = { + const SLOT_FILL_RATE: f64 = MILLISECS_PER_BLOCK as f64 / SLOT_DURATION as f64; + + (EPOCH_DURATION_IN_BLOCKS as f64 * SLOT_FILL_RATE) as u64 + }; } /// Money matters. @@ -165,3 +177,4 @@ pub const MAXIMUM_BLOCK_WEIGHT: Weight = Weight::from_parts(WEIGHT_REF_TIME_PER_SECOND, MAX_POV_SIZE as u64); pub use sp_consensus_aura::sr25519::AuthorityId as AuraId; +pub use sp_consensus_babe::AuthorityId as BabeId; diff --git a/runtime/mainnet/Cargo.toml b/runtime/mainnet/Cargo.toml index 70888c746..a6456b85e 100644 --- a/runtime/mainnet/Cargo.toml +++ b/runtime/mainnet/Cargo.toml @@ -28,7 +28,7 @@ pallet-airdrop-claims = { workspace = true } # Substrate dependencies sp-api = { workspace = true } sp-block-builder = { workspace = true } -sp-consensus-aura = { workspace = true } +sp-consensus-babe = { workspace = true } sp-core = { workspace = true } sp-inherents = { workspace = true } sp-io = { workspace = true } @@ -48,7 +48,7 @@ frame-system = { workspace = true } frame-system-benchmarking = { workspace = true, optional = true } frame-system-rpc-runtime-api = { workspace = true } -pallet-aura = { workspace = true } +pallet-babe = { workspace = true } pallet-bags-list = { workspace = true } pallet-session = { workspace = true } @@ -162,14 +162,14 @@ std = [ "sp-block-builder/std", "sp-transaction-pool/std", "sp-inherents/std", - "sp-consensus-aura/std", + "sp-consensus-babe/std", "frame-support/std", "frame-executive/std", "frame-system/std", "frame-system-rpc-runtime-api/std", "frame-election-provider-support/std", "pallet-authorship/std", - "pallet-aura/std", + "pallet-babe/std", "pallet-bags-list/std", "pallet-bounties/std", "pallet-child-bounties/std", diff --git a/runtime/mainnet/src/frontier_evm.rs b/runtime/mainnet/src/frontier_evm.rs index 3c01c5889..e354b8527 100644 --- a/runtime/mainnet/src/frontier_evm.rs +++ b/runtime/mainnet/src/frontier_evm.rs @@ -41,8 +41,8 @@ impl> FindAuthor for FindAuthorTruncated { I: 'a + IntoIterator, { if let Some(author_index) = F::find_author(digests) { - let authority_id = Aura::authorities()[author_index as usize].clone(); - return Some(H160::from_slice(&authority_id.to_raw_vec()[4..24])) + let authority_id = Babe::authorities()[author_index as usize].clone(); + return Some(H160::from_slice(&authority_id.0.to_raw_vec()[4..24])) } None } @@ -151,7 +151,7 @@ impl pallet_evm::Config for Runtime { type OnChargeTransaction = pallet_evm::EVMCurrencyAdapter>; type OnCreate = (); - type FindAuthor = FindAuthorTruncated; + type FindAuthor = FindAuthorTruncated; type GasLimitPovSizeRatio = GasLimitPovSizeRatio; type Timestamp = Timestamp; type WeightInfo = pallet_evm::weights::SubstrateWeight; diff --git a/runtime/mainnet/src/lib.rs b/runtime/mainnet/src/lib.rs index c6a745b35..947d0bb77 100644 --- a/runtime/mainnet/src/lib.rs +++ b/runtime/mainnet/src/lib.rs @@ -94,7 +94,7 @@ use sp_runtime::generic::Era; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; pub use sp_runtime::{MultiAddress, Perbill, Percent, Permill}; -use tangle_primitives::AuraId; +use tangle_primitives::BabeId; pub use tangle_primitives::{ currency::*, fee::*, time::*, AccountId, AccountIndex, Address, Balance, BlockNumber, Hash, Header, Index, Moment, Signature, AVERAGE_ON_INITIALIZE_RATIO, EPOCH_DURATION_IN_BLOCKS, @@ -109,6 +109,13 @@ use pallet_ethereum::{Call::transact, Transaction as EthereumTransaction}; use pallet_evm::{Account as EVMAccount, FeeCalculator, HashedAddressMapping, Runner}; pub type Nonce = u32; +/// The BABE epoch configuration at genesis. +pub const BABE_GENESIS_EPOCH_CONFIG: sp_consensus_babe::BabeEpochConfiguration = + sp_consensus_babe::BabeEpochConfiguration { + c: PRIMARY_PROBABILITY, + allowed_slots: sp_consensus_babe::AllowedSlots::PrimaryAndSecondaryPlainSlots, + }; + /// This runtime version. #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { @@ -163,7 +170,7 @@ pub mod opaque { impl_opaque_keys! { pub struct SessionKeys { - pub aura: Aura, + pub babe: Babe, pub grandpa: Grandpa, pub im_online: ImOnline, } @@ -215,7 +222,7 @@ parameter_types! { impl pallet_timestamp::Config for Runtime { /// A timestamp: milliseconds since the unix epoch. type Moment = u64; - type OnTimestampSet = (); + type OnTimestampSet = Babe; type MinimumPeriod = MinimumPeriod; type WeightInfo = (); } @@ -317,22 +324,28 @@ impl pallet_sudo::Config for Runtime { } parameter_types! { - #[derive(Serialize, Deserialize)] + // NOTE: Currently it is not possible to change the epoch duration after the chain has started. + // Attempting to do so will brick block production. + pub const EpochDuration: u64 = EPOCH_DURATION_IN_SLOTS; + pub const ExpectedBlockTime: Moment = MILLISECS_PER_BLOCK; + pub const ReportLongevity: u64 = + BondingDuration::get() as u64 * SessionsPerEra::get() as u64 * EpochDuration::get(); pub const MaxAuthorities: u32 = 1000; - #[derive(Serialize, Deserialize)] - pub const MaxProposalLength: u32 = 1000; + pub const MaxNominators: u32 = 1000; } -impl pallet_aura::Config for Runtime { - type AuthorityId = AuraId; - type DisabledValidators = (); +impl pallet_babe::Config for Runtime { + type EpochDuration = EpochDuration; + type ExpectedBlockTime = ExpectedBlockTime; + type EpochChangeTrigger = pallet_babe::ExternalTrigger; + type DisabledValidators = Session; + type WeightInfo = (); type MaxAuthorities = MaxAuthorities; - type AllowMultipleBlocksPerSlot = frame_support::traits::ConstBool; -} - -parameter_types! { - pub const ReportLongevity: u64 = - BondingDuration::get() as u64 * SessionsPerEra::get() as u64 * Period::get(); + type MaxNominators = MaxNominators; + type KeyOwnerProof = + >::Proof; + type EquivocationReportSystem = + pallet_babe::EquivocationReportSystem; } impl pallet_grandpa::Config for Runtime { @@ -351,7 +364,7 @@ parameter_types! { impl pallet_authorship::Config for Runtime { type EventHandler = Staking; - type FindAuthor = pallet_session::FindAccountFromAuthorIndex; + type FindAuthor = pallet_session::FindAccountFromAuthorIndex; } parameter_types! { @@ -1076,7 +1089,7 @@ construct_runtime!( TransactionPayment: pallet_transaction_payment, Authorship: pallet_authorship, - Aura: pallet_aura, + Babe: pallet_babe, Grandpa: pallet_grandpa, Indices: pallet_indices, @@ -1554,13 +1567,52 @@ impl_runtime_apis! { } } - impl sp_consensus_aura::AuraApi for Runtime { - fn slot_duration() -> sp_consensus_aura::SlotDuration { - sp_consensus_aura::SlotDuration::from_millis(Aura::slot_duration()) + impl sp_consensus_babe::BabeApi for Runtime { + fn configuration() -> sp_consensus_babe::BabeConfiguration { + let epoch_config = Babe::epoch_config().unwrap_or(BABE_GENESIS_EPOCH_CONFIG); + sp_consensus_babe::BabeConfiguration { + slot_duration: Babe::slot_duration(), + epoch_length: EpochDuration::get(), + c: epoch_config.c, + authorities: Babe::authorities().to_vec(), + randomness: Babe::randomness(), + allowed_slots: epoch_config.allowed_slots, + } + } + + fn current_epoch_start() -> sp_consensus_babe::Slot { + Babe::current_epoch_start() } - fn authorities() -> Vec { - Aura::authorities().into_inner() + fn current_epoch() -> sp_consensus_babe::Epoch { + Babe::current_epoch() + } + + fn next_epoch() -> sp_consensus_babe::Epoch { + Babe::next_epoch() + } + + fn generate_key_ownership_proof( + _slot: sp_consensus_babe::Slot, + authority_id: sp_consensus_babe::AuthorityId, + ) -> Option { + use parity_scale_codec::Encode; + + Historical::prove((sp_consensus_babe::KEY_TYPE, authority_id)) + .map(|p| p.encode()) + .map(sp_consensus_babe::OpaqueKeyOwnershipProof::new) + } + + fn submit_report_equivocation_unsigned_extrinsic( + equivocation_proof: sp_consensus_babe::EquivocationProof<::Header>, + key_owner_proof: sp_consensus_babe::OpaqueKeyOwnershipProof, + ) -> Option<()> { + let key_owner_proof = key_owner_proof.decode()?; + + Babe::submit_unsigned_equivocation_report( + equivocation_proof, + key_owner_proof, + ) } }