diff --git a/Cargo.lock b/Cargo.lock index 590c800cc9bf..2e822a3100ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13865,6 +13865,102 @@ dependencies = [ ] [[package]] +<<<<<<< HEAD +======= +name = "polkadot-omni-node" +version = "0.1.0" +dependencies = [ + "color-eyre", + "polkadot-omni-node-lib", + "substrate-build-script-utils", +] + +[[package]] +name = "polkadot-omni-node-lib" +version = "0.1.0" +dependencies = [ + "assert_cmd", + "async-trait", + "clap 4.5.13", + "color-print", + "cumulus-client-cli", + "cumulus-client-collator", + "cumulus-client-consensus-aura", + "cumulus-client-consensus-common", + "cumulus-client-consensus-proposer", + "cumulus-client-consensus-relay-chain", + "cumulus-client-parachain-inherent", + "cumulus-client-service", + "cumulus-primitives-aura 0.7.0", + "cumulus-primitives-core 0.7.0", + "cumulus-relay-chain-interface", + "cumulus-test-runtime", + "docify", + "frame-benchmarking 28.0.0", + "frame-benchmarking-cli", + "frame-support 28.0.0", + "frame-system-rpc-runtime-api 26.0.0", + "frame-try-runtime 0.34.0", + "futures", + "futures-timer", + "jsonrpsee", + "log", + "nix 0.29.0", + "pallet-transaction-payment 28.0.0", + "pallet-transaction-payment-rpc", + "pallet-transaction-payment-rpc-runtime-api 28.0.0", + "parachains-common 7.0.0", + "parity-scale-codec", + "polkadot-cli", + "polkadot-primitives 7.0.0", + "sc-basic-authorship", + "sc-chain-spec", + "sc-cli", + "sc-client-api", + "sc-client-db", + "sc-consensus", + "sc-consensus-manual-seal", + "sc-executor 0.32.0", + "sc-network", + "sc-offchain", + "sc-rpc", + "sc-runtime-utilities", + "sc-service", + "sc-sysinfo", + "sc-telemetry", + "sc-tracing", + "sc-transaction-pool", + "sc-transaction-pool-api", + "scale-info", + "serde", + "serde_json", + "sp-api 26.0.0", + "sp-block-builder 26.0.0", + "sp-consensus", + "sp-consensus-aura 0.32.0", + "sp-core 28.0.0", + "sp-crypto-hashing 0.1.0", + "sp-genesis-builder 0.8.0", + "sp-inherents 26.0.0", + "sp-keystore 0.34.0", + "sp-offchain 26.0.0", + "sp-runtime 31.0.1", + "sp-session 27.0.0", + "sp-storage 19.0.0", + "sp-timestamp 26.0.0", + "sp-transaction-pool 26.0.0", + "sp-version 29.0.0", + "sp-weights 27.0.0", + "substrate-frame-rpc-system", + "substrate-prometheus-endpoint", + "substrate-state-trie-migration-rpc", + "subxt-metadata", + "tokio", + "wait-timeout", +] + +[[package]] +>>>>>>> 87f4f3f0 (omni-node: add offchain worker (#7479)) name = "polkadot-overseer" version = "18.0.0" dependencies = [ diff --git a/cumulus/polkadot-omni-node/lib/Cargo.toml b/cumulus/polkadot-omni-node/lib/Cargo.toml new file mode 100644 index 000000000000..020d980d3d9d --- /dev/null +++ b/cumulus/polkadot-omni-node/lib/Cargo.toml @@ -0,0 +1,130 @@ +[package] +name = "polkadot-omni-node-lib" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +description = "Helper library that can be used to build a parachain node" +license = "Apache-2.0" +homepage.workspace = true +repository.workspace = true + +[lints] +workspace = true + +[lib] +path = "src/lib.rs" + +[dependencies] +async-trait = { workspace = true } +clap = { features = ["derive"], workspace = true } +codec = { workspace = true, default-features = true } +color-print = { workspace = true } +docify = { workspace = true } +futures = { workspace = true } +log = { workspace = true, default-features = true } +serde = { features = ["derive"], workspace = true, default-features = true } +serde_json = { workspace = true, default-features = true } + +# Local +jsonrpsee = { features = ["server"], workspace = true } +parachains-common = { workspace = true, default-features = true } +scale-info = { workspace = true } +subxt-metadata = { workspace = true, default-features = true } + +# Substrate +frame-benchmarking = { optional = true, workspace = true, default-features = true } +frame-benchmarking-cli = { workspace = true, default-features = true } +frame-support = { optional = true, workspace = true, default-features = true } +frame-system-rpc-runtime-api = { workspace = true, default-features = true } +frame-try-runtime = { optional = true, workspace = true, default-features = true } +pallet-transaction-payment = { workspace = true, default-features = true } +pallet-transaction-payment-rpc = { workspace = true, default-features = true } +pallet-transaction-payment-rpc-runtime-api = { workspace = true, default-features = true } +prometheus-endpoint = { workspace = true, default-features = true } +sc-basic-authorship = { workspace = true, default-features = true } +sc-chain-spec = { workspace = true, default-features = true } +sc-cli = { workspace = true, default-features = true } +sc-client-api = { workspace = true, default-features = true } +sc-client-db = { workspace = true, default-features = true } +sc-consensus = { workspace = true, default-features = true } +sc-consensus-manual-seal = { workspace = true, default-features = true } +sc-executor = { workspace = true, default-features = true } +sc-network = { workspace = true, default-features = true } +sc-offchain = { workspace = true, default-features = true } +sc-rpc = { workspace = true, default-features = true } +sc-runtime-utilities = { workspace = true, default-features = true } +sc-service = { workspace = true, default-features = true } +sc-sysinfo = { workspace = true, default-features = true } +sc-telemetry = { workspace = true, default-features = true } +sc-tracing = { workspace = true, default-features = true } +sc-transaction-pool = { workspace = true, default-features = true } +sc-transaction-pool-api = { workspace = true, default-features = true } +sp-api = { workspace = true, default-features = true } +sp-block-builder = { workspace = true, default-features = true } +sp-consensus = { workspace = true, default-features = true } +sp-consensus-aura = { workspace = true, default-features = true } +sp-core = { workspace = true, default-features = true } +sp-crypto-hashing = { workspace = true } +sp-genesis-builder = { workspace = true } +sp-inherents = { workspace = true, default-features = true } +sp-keystore = { workspace = true, default-features = true } +sp-offchain = { workspace = true, default-features = true } +sp-runtime = { workspace = true } +sp-session = { workspace = true, default-features = true } +sp-storage = { workspace = true, default-features = true } +sp-timestamp = { workspace = true, default-features = true } +sp-transaction-pool = { workspace = true, default-features = true } +sp-version = { workspace = true, default-features = true } +sp-weights = { workspace = true, default-features = true } +substrate-frame-rpc-system = { workspace = true, default-features = true } +substrate-state-trie-migration-rpc = { workspace = true, default-features = true } + +# Polkadot +polkadot-cli = { workspace = true, default-features = true } +polkadot-primitives = { workspace = true, default-features = true } + +# Cumulus +cumulus-client-cli = { workspace = true, default-features = true } +cumulus-client-collator = { workspace = true, default-features = true } +cumulus-client-consensus-aura = { workspace = true, default-features = true } +cumulus-client-consensus-common = { workspace = true, default-features = true } +cumulus-client-consensus-proposer = { workspace = true, default-features = true } +cumulus-client-consensus-relay-chain = { workspace = true, default-features = true } +cumulus-client-parachain-inherent = { workspace = true, default-features = true } +cumulus-client-service = { workspace = true, default-features = true } +cumulus-primitives-aura = { workspace = true, default-features = true } +cumulus-primitives-core = { workspace = true, default-features = true } +cumulus-relay-chain-interface = { workspace = true, default-features = true } +futures-timer = "3.0.3" + +[dev-dependencies] +assert_cmd = { workspace = true } +cumulus-test-runtime = { workspace = true } +nix = { features = ["signal"], workspace = true } +tokio = { version = "1.32.0", features = ["macros", "parking_lot", "time"] } +wait-timeout = { workspace = true } + +[features] +default = [] +rococo-native = ["polkadot-cli/rococo-native"] +westend-native = ["polkadot-cli/westend-native"] +runtime-benchmarks = [ + "cumulus-primitives-core/runtime-benchmarks", + "frame-benchmarking-cli/runtime-benchmarks", + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "pallet-transaction-payment/runtime-benchmarks", + "parachains-common/runtime-benchmarks", + "polkadot-cli/runtime-benchmarks", + "polkadot-primitives/runtime-benchmarks", + "sc-client-db/runtime-benchmarks", + "sc-service/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-try-runtime/try-runtime", + "pallet-transaction-payment/try-runtime", + "polkadot-cli/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs new file mode 100644 index 000000000000..96802177ec10 --- /dev/null +++ b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs @@ -0,0 +1,287 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +use crate::common::{ + rpc::BuildRpcExtensions as BuildRpcExtensionsT, + spec::{BaseNodeSpec, BuildImportQueue, ClientBlockImport, NodeSpec as NodeSpecT}, + types::{Hash, ParachainBlockImport, ParachainClient}, +}; +use codec::Encode; +use cumulus_client_parachain_inherent::{MockValidationDataInherentDataProvider, MockXcmConfig}; +use cumulus_primitives_aura::AuraUnincludedSegmentApi; +use cumulus_primitives_core::{CollectCollationInfo, ParaId}; +use futures::FutureExt; +use polkadot_primitives::UpgradeGoAhead; +use sc_client_api::Backend; +use sc_consensus::{DefaultImportQueue, LongestChain}; +use sc_consensus_manual_seal::rpc::{ManualSeal, ManualSealApiServer}; +use sc_network::NetworkBackend; +use sc_service::{Configuration, PartialComponents, TaskManager}; +use sc_telemetry::TelemetryHandle; +use sc_transaction_pool_api::OffchainTransactionPoolFactory; +use sp_api::{ApiExt, ProvideRuntimeApi}; +use sp_runtime::traits::Header; +use std::{marker::PhantomData, sync::Arc}; + +pub struct ManualSealNode(PhantomData); + +impl + BuildImportQueue< + NodeSpec::Block, + NodeSpec::RuntimeApi, + Arc>, + > for ManualSealNode +{ + fn build_import_queue( + client: Arc>, + _block_import: ParachainBlockImport< + NodeSpec::Block, + Arc>, + >, + config: &Configuration, + _telemetry_handle: Option, + task_manager: &TaskManager, + ) -> sc_service::error::Result> { + Ok(sc_consensus_manual_seal::import_queue( + Box::new(client.clone()), + &task_manager.spawn_essential_handle(), + config.prometheus_registry(), + )) + } +} + +impl BaseNodeSpec for ManualSealNode { + type Block = NodeSpec::Block; + type RuntimeApi = NodeSpec::RuntimeApi; + type BuildImportQueue = Self; + type InitBlockImport = ClientBlockImport; +} + +impl ManualSealNode { + pub fn new() -> Self { + Self(Default::default()) + } + + pub fn start_node( + &self, + mut config: Configuration, + para_id: ParaId, + block_time: u64, + ) -> sc_service::error::Result + where + Net: NetworkBackend, + { + let PartialComponents { + client, + backend, + mut task_manager, + import_queue, + keystore_container, + select_chain: _, + transaction_pool, + other: (_, mut telemetry, _, _), + } = Self::new_partial(&config)?; + let select_chain = LongestChain::new(backend.clone()); + + // Since this is a dev node, prevent it from connecting to peers. + config.network.default_peers_set.in_peers = 0; + config.network.default_peers_set.out_peers = 0; + let net_config = sc_network::config::FullNetworkConfiguration::<_, _, Net>::new( + &config.network, + config.prometheus_config.as_ref().map(|cfg| cfg.registry.clone()), + ); + let metrics = Net::register_notification_metrics( + config.prometheus_config.as_ref().map(|cfg| &cfg.registry), + ); + + let (network, system_rpc_tx, tx_handler_controller, sync_service) = + 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, + net_config, + block_announce_validator_builder: None, + warp_sync_config: None, + block_relay: None, + metrics, + })?; + + if config.offchain_worker.enabled { + let offchain_workers = + 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: Arc::new(network.clone()), + is_validator: config.role.is_authority(), + enable_http_requests: true, + custom_extensions: move |_| vec![], + })?; + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-work", + offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(), + ); + } + + let proposer = sc_basic_authorship::ProposerFactory::new( + task_manager.spawn_handle(), + client.clone(), + transaction_pool.clone(), + None, + None, + ); + + let (manual_seal_sink, manual_seal_stream) = futures::channel::mpsc::channel(1024); + let mut manual_seal_sink_clone = manual_seal_sink.clone(); + task_manager + .spawn_essential_handle() + .spawn("block_authoring", None, async move { + loop { + futures_timer::Delay::new(std::time::Duration::from_millis(block_time)).await; + manual_seal_sink_clone + .try_send(sc_consensus_manual_seal::EngineCommand::SealNewBlock { + create_empty: true, + finalize: true, + parent_hash: None, + sender: None, + }) + .unwrap(); + } + }); + + let client_for_cidp = client.clone(); + let params = sc_consensus_manual_seal::ManualSealParams { + block_import: client.clone(), + env: proposer, + client: client.clone(), + pool: transaction_pool.clone(), + select_chain, + commands_stream: Box::pin(manual_seal_stream), + consensus_data_provider: None, + create_inherent_data_providers: move |block: Hash, ()| { + let current_para_head = client_for_cidp + .header(block) + .expect("Header lookup should succeed") + .expect("Header passed in as parent should be present in backend."); + + let should_send_go_ahead = client_for_cidp + .runtime_api() + .collect_collation_info(block, ¤t_para_head) + .map(|info| info.new_validation_code.is_some()) + .unwrap_or_default(); + + // The API version is relevant here because the constraints in the runtime changed + // in https://github.com/paritytech/polkadot-sdk/pull/6825. In general, the logic + // here assumes that we are using the aura-ext consensushook in the parachain + // runtime. + let requires_relay_progress = client_for_cidp + .runtime_api() + .has_api_with::, _>( + block, + |version| version > 1, + ) + .ok() + .unwrap_or_default(); + + let current_para_block_head = + Some(polkadot_primitives::HeadData(current_para_head.encode())); + let client_for_xcm = client_for_cidp.clone(); + async move { + use sp_runtime::traits::UniqueSaturatedInto; + + let mocked_parachain = MockValidationDataInherentDataProvider { + // When using manual seal we start from block 0, and it's very unlikely to + // reach a block number > u32::MAX. + current_para_block: UniqueSaturatedInto::::unique_saturated_into( + *current_para_head.number(), + ), + para_id, + current_para_block_head, + relay_offset: 0, + relay_blocks_per_para_block: requires_relay_progress + .then(|| 1) + .unwrap_or_default(), + para_blocks_per_relay_epoch: 10, + relay_randomness_config: (), + xcm_config: MockXcmConfig::new(&*client_for_xcm, block, Default::default()), + raw_downward_messages: vec![], + raw_horizontal_messages: vec![], + additional_key_values: None, + upgrade_go_ahead: should_send_go_ahead.then(|| { + log::info!( + "Detected pending validation code, sending go-ahead signal." + ); + UpgradeGoAhead::GoAhead + }), + }; + Ok(( + // This is intentional, as the runtime that we expect to run against this + // will never receive the aura-related inherents/digests, and providing + // real timestamps would cause aura <> timestamp checking to fail. + sp_timestamp::InherentDataProvider::new(sp_timestamp::Timestamp::new(0)), + mocked_parachain, + )) + } + }, + }; + let authorship_future = sc_consensus_manual_seal::run_manual_seal(params); + task_manager.spawn_essential_handle().spawn_blocking( + "manual-seal", + None, + authorship_future, + ); + let rpc_extensions_builder = { + let client = client.clone(); + let transaction_pool = transaction_pool.clone(); + let backend_for_rpc = backend.clone(); + + Box::new(move |_| { + let mut module = NodeSpec::BuildRpcExtensions::build_rpc_extensions( + client.clone(), + backend_for_rpc.clone(), + transaction_pool.clone(), + )?; + module + .merge(ManualSeal::new(manual_seal_sink.clone()).into_rpc()) + .map_err(|e| sc_service::Error::Application(e.into()))?; + Ok(module) + }) + }; + + let _rpc_handlers = sc_service::spawn_tasks(sc_service::SpawnTasksParams { + network, + client: client.clone(), + keystore: keystore_container.keystore(), + task_manager: &mut task_manager, + transaction_pool: transaction_pool.clone(), + rpc_builder: rpc_extensions_builder, + backend, + system_rpc_tx, + tx_handler_controller, + sync_service, + config, + telemetry: telemetry.as_mut(), + })?; + + Ok(task_manager) + } +} diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs index 907f09263fc1..a60391aaa1e0 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs @@ -28,6 +28,11 @@ pub mod types; use cumulus_primitives_core::CollectCollationInfo; use sc_client_db::DbHash; +<<<<<<< HEAD:cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs +======= +use sc_offchain::OffchainWorkerApi; +use serde::de::DeserializeOwned; +>>>>>>> 87f4f3f0 (omni-node: add offchain worker (#7479)):cumulus/polkadot-omni-node/lib/src/common/mod.rs use sp_api::{ApiExt, CallApiAt, ConstructRuntimeApi, Metadata}; use sp_block_builder::BlockBuilder; use sp_runtime::{ @@ -65,6 +70,7 @@ pub trait NodeRuntimeApi: + SessionKeys + BlockBuilder + TaggedTransactionQueue + + OffchainWorkerApi + CollectCollationInfo + Sized { @@ -76,6 +82,11 @@ impl NodeRuntimeApi for T where + SessionKeys + BlockBuilder + TaggedTransactionQueue +<<<<<<< HEAD:cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/mod.rs +======= + + OffchainWorkerApi + + GetCoreSelectorApi +>>>>>>> 87f4f3f0 (omni-node: add offchain worker (#7479)):cumulus/polkadot-omni-node/lib/src/common/mod.rs + CollectCollationInfo { } diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs index 0c0230296eb8..cd85e17552ff 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs @@ -30,16 +30,24 @@ use cumulus_client_service::{ }; use cumulus_primitives_core::{BlockT, ParaId}; use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; +use futures::FutureExt; use parachains_common::Hash; use polkadot_primitives::CollatorPair; use prometheus_endpoint::Registry; +use sc_client_api::Backend; use sc_consensus::DefaultImportQueue; use sc_executor::{HeapAllocStrategy, DEFAULT_HEAP_ALLOC_STRATEGY}; use sc_network::{config::FullNetworkConfiguration, NetworkBackend, NetworkBlock}; use sc_service::{Configuration, ImportQueue, PartialComponents, TaskManager}; use sc_sysinfo::HwBench; use sc_telemetry::{TelemetryHandle, TelemetryWorker}; +<<<<<<< HEAD:cumulus/polkadot-parachain/polkadot-parachain-lib/src/common/spec.rs use sc_transaction_pool::FullPool; +======= +use sc_tracing::tracing::Instrument; +use sc_transaction_pool::TransactionPoolHandle; +use sc_transaction_pool_api::OffchainTransactionPoolFactory; +>>>>>>> 87f4f3f0 (omni-node: add offchain worker (#7479)):cumulus/polkadot-omni-node/lib/src/common/spec.rs use sp_keystore::KeystorePtr; use std::{future::Future, pin::Pin, sync::Arc, time::Duration}; @@ -246,6 +254,27 @@ pub(crate) trait NodeSpec { }) .await?; + if parachain_config.offchain_worker.enabled { + let offchain_workers = + sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { + runtime_api_provider: client.clone(), + keystore: Some(params.keystore_container.keystore()), + offchain_db: backend.offchain_storage(), + transaction_pool: Some(OffchainTransactionPoolFactory::new( + transaction_pool.clone(), + )), + network_provider: Arc::new(network.clone()), + is_validator: parachain_config.role.is_authority(), + enable_http_requests: false, + custom_extensions: move |_| vec![], + })?; + task_manager.spawn_handle().spawn( + "offchain-workers-runner", + "offchain-work", + offchain_workers.run(client.clone(), task_manager.spawn_handle()).boxed(), + ); + } + let rpc_builder = { let client = client.clone(); let transaction_pool = transaction_pool.clone(); diff --git a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs index 442b87b5d775..245709cf81b5 100644 --- a/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs +++ b/cumulus/polkadot-parachain/polkadot-parachain-lib/src/fake_runtime_api/utils.rs @@ -110,6 +110,12 @@ macro_rules! impl_node_runtime_apis { } } + impl sp_offchain::OffchainWorkerApi<$block> for $runtime { + fn offchain_worker(_: &<$block as BlockT>::Header) { + unimplemented!() + } + } + impl sp_session::SessionKeys<$block> for $runtime { fn generate_session_keys(_: Option>) -> Vec { unimplemented!() diff --git a/prdoc/pr_7479.prdoc b/prdoc/pr_7479.prdoc new file mode 100644 index 000000000000..444eaa420a45 --- /dev/null +++ b/prdoc/pr_7479.prdoc @@ -0,0 +1,9 @@ +title: 'omni-node: add offchain worker' +doc: +- audience: [ Runtime Dev, Node Dev, Node Operator ] + description: |- + Added support for offchain worker to omni-node-lib for both aura and manual seal nodes. + +crates: +- name: polkadot-omni-node-lib + bump: patch