diff --git a/Cargo.lock b/Cargo.lock index 9f487d928257c..dd4c447d352b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5151,6 +5151,9 @@ dependencies = [ "polkadot-primitives", "serde", "serde_json", + "sp-core 28.0.0", + "sp-keyring", + "sp-statement-store", "tokio", "zombienet-orchestrator", "zombienet-sdk", @@ -14609,6 +14612,7 @@ dependencies = [ "sp-offchain", "sp-runtime 31.0.1", "sp-session", + "sp-statement-store", "sp-storage 19.0.0", "sp-transaction-pool", "sp-version 29.0.0", @@ -15765,11 +15769,15 @@ dependencies = [ "sc-consensus", "sc-consensus-manual-seal", "sc-executor 0.32.0", + "sc-keystore", "sc-network", + "sc-network-statement", + "sc-network-sync", "sc-offchain", "sc-rpc", "sc-runtime-utilities", "sc-service", + "sc-statement-store", "sc-sysinfo", "sc-telemetry", "sc-tracing", @@ -15789,6 +15797,7 @@ dependencies = [ "sp-offchain", "sp-runtime 31.0.1", "sp-session", + "sp-statement-store", "sp-storage 19.0.0", "sp-timestamp", "sp-transaction-pool", diff --git a/cumulus/client/service/src/lib.rs b/cumulus/client/service/src/lib.rs index 4a719c66bbf00..badf22ed68440 100644 --- a/cumulus/client/service/src/lib.rs +++ b/cumulus/client/service/src/lib.rs @@ -461,6 +461,7 @@ pub struct BuildNetworkParams< pub spawn_handle: SpawnTaskHandle, pub import_queue: IQ, pub sybil_resistance_level: CollatorSybilResistance, + pub metrics: sc_network::NotificationMetrics, } /// Build the network service, the network status sinks and an RPC sender. @@ -475,6 +476,7 @@ pub async fn build_network<'a, Block, Client, RCInterface, IQ, Network>( relay_chain_interface, import_queue, sybil_resistance_level, + metrics, }: BuildNetworkParams<'a, Block, Client, Network, RCInterface, IQ>, ) -> sc_service::error::Result<( Arc, @@ -533,9 +535,6 @@ where Box::new(block_announce_validator) as Box<_> }, }; - let metrics = Network::register_notification_metrics( - parachain_config.prometheus_config.as_ref().map(|config| &config.registry), - ); sc_service::build_network(sc_service::BuildNetworkParams { config: parachain_config, diff --git a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml index 5a7604b5297c1..9c19b12319f86 100644 --- a/cumulus/parachains/runtimes/people/people-westend/Cargo.toml +++ b/cumulus/parachains/runtimes/people/people-westend/Cargo.toml @@ -50,6 +50,7 @@ sp-keyring = { workspace = true } sp-offchain = { workspace = true } sp-runtime = { workspace = true } sp-session = { workspace = true } +sp-statement-store = { workspace = true } sp-storage = { workspace = true } sp-transaction-pool = { workspace = true } sp-version = { workspace = true } @@ -141,6 +142,7 @@ std = [ "sp-offchain/std", "sp-runtime/std", "sp-session/std", + "sp-statement-store/std", "sp-storage/std", "sp-transaction-pool/std", "sp-version/std", diff --git a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs index 55b7e7a46119a..034baad40f817 100644 --- a/cumulus/parachains/runtimes/people/people-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/people/people-westend/src/lib.rs @@ -65,6 +65,10 @@ use sp_runtime::{ ApplyExtrinsicResult, }; pub use sp_runtime::{MultiAddress, Perbill, Permill, RuntimeDebug}; +use sp_statement_store::{ + runtime_api::{InvalidStatement, StatementSource, ValidStatement}, + SignatureVerificationResult, Statement, +}; #[cfg(feature = "std")] use sp_version::NativeVersion; use sp_version::RuntimeVersion; @@ -1136,6 +1140,39 @@ impl_runtime_apis! { ParachainInfo::parachain_id() } } + + impl sp_statement_store::runtime_api::ValidateStatement for Runtime { + fn validate_statement( + _source: StatementSource, + statement: Statement, + ) -> Result { + let account = match statement.verify_signature() { + SignatureVerificationResult::Valid(account) => account.into(), + SignatureVerificationResult::Invalid => { + log::debug!("Bad statement signature."); + return Err(InvalidStatement::BadProof) + }, + SignatureVerificationResult::NoSignature => { + log::debug!("Missing statement signature."); + return Err(InvalidStatement::NoProof) + }, + }; + + // For now just allow validators to store some statements. + // In the future we will allow people. + if pallet_session::Validators::::get().contains(&account) { + Ok(ValidStatement { + max_count: 2, + max_size: 1024, + }) + } else { + Ok(ValidStatement { + max_count: 0, + max_size: 0, + }) + } + } + } } cumulus_pallet_parachain_system::register_validate_block! { diff --git a/cumulus/polkadot-omni-node/lib/Cargo.toml b/cumulus/polkadot-omni-node/lib/Cargo.toml index d0e421318ae5b..fa97d36c68572 100644 --- a/cumulus/polkadot-omni-node/lib/Cargo.toml +++ b/cumulus/polkadot-omni-node/lib/Cargo.toml @@ -50,11 +50,15 @@ 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-keystore = { workspace = true, default-features = true } sc-network = { workspace = true, default-features = true } +sc-network-statement = { workspace = true, default-features = true } +sc-network-sync = { 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 = false } +sc-statement-store = { 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 } @@ -71,6 +75,7 @@ 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-statement-store = { 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 } diff --git a/cumulus/polkadot-omni-node/lib/src/cli.rs b/cumulus/polkadot-omni-node/lib/src/cli.rs index b50b5a7c4a6b5..5f9f949ce7268 100644 --- a/cumulus/polkadot-omni-node/lib/src/cli.rs +++ b/cumulus/polkadot-omni-node/lib/src/cli.rs @@ -198,6 +198,13 @@ pub struct Cli { #[arg(raw = true)] pub relay_chain_args: Vec, + /// Enable the statement store. + /// + /// The statement store is a store for statements validated using the runtime API + /// `validate_statement`. It should be enabled for chains that provide this runtime API. + #[arg(long)] + pub enable_statement_store: bool, + #[arg(skip)] pub(crate) _phantom: PhantomData, } @@ -232,6 +239,7 @@ impl Cli { .unwrap_or(self.authoring), export_pov: self.export_pov_to_path.clone(), max_pov_percentage: self.run.experimental_max_pov_percentage, + enable_statement_store: self.enable_statement_store, } } } diff --git a/cumulus/polkadot-omni-node/lib/src/common/mod.rs b/cumulus/polkadot-omni-node/lib/src/common/mod.rs index a1020f9f64f4a..322b668b6c83b 100644 --- a/cumulus/polkadot-omni-node/lib/src/common/mod.rs +++ b/cumulus/polkadot-omni-node/lib/src/common/mod.rs @@ -24,6 +24,7 @@ pub mod command; pub mod rpc; pub mod runtime; pub mod spec; +pub(crate) mod statement_store; pub mod types; use crate::cli::AuthoringPolicy; @@ -41,6 +42,7 @@ use sp_runtime::{ OpaqueExtrinsic, }; use sp_session::SessionKeys; +use sp_statement_store::runtime_api::ValidateStatement; use sp_transaction_pool::runtime_api::TaggedTransactionQueue; use std::{fmt::Debug, path::PathBuf, str::FromStr}; @@ -73,6 +75,7 @@ pub trait NodeRuntimeApi: + OffchainWorkerApi + CollectCollationInfo + GetCoreSelectorApi + + ValidateStatement + GetParachainInfo + RelayParentOffsetApi + Sized @@ -89,6 +92,7 @@ impl NodeRuntimeApi for T where + GetCoreSelectorApi + RelayParentOffsetApi + CollectCollationInfo + + ValidateStatement + GetParachainInfo { } @@ -123,4 +127,7 @@ pub struct NodeExtraArgs { /// The maximum percentage of the maximum PoV size that the collator can use. /// It will be removed once is fixed. pub max_pov_percentage: Option, + + /// If true then the statement store will be enabled. + pub enable_statement_store: bool, } diff --git a/cumulus/polkadot-omni-node/lib/src/common/rpc.rs b/cumulus/polkadot-omni-node/lib/src/common/rpc.rs index d91d9fd1e9b39..c97af2c9d7ae6 100644 --- a/cumulus/polkadot-omni-node/lib/src/common/rpc.rs +++ b/cumulus/polkadot-omni-node/lib/src/common/rpc.rs @@ -23,7 +23,10 @@ use crate::common::{ ConstructNodeRuntimeApi, }; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; -use sc_rpc::dev::{Dev, DevApiServer}; +use sc_rpc::{ + dev::{Dev, DevApiServer}, + statement::{StatementApiServer, StatementStore}, +}; use sp_runtime::traits::Block as BlockT; use std::{marker::PhantomData, sync::Arc}; use substrate_frame_rpc_system::{System, SystemApiServer}; @@ -32,11 +35,12 @@ use substrate_state_trie_migration_rpc::{StateMigration, StateMigrationApiServer /// A type representing all RPC extensions. pub type RpcExtension = jsonrpsee::RpcModule<()>; -pub(crate) trait BuildRpcExtensions { +pub(crate) trait BuildRpcExtensions { fn build_rpc_extensions( client: Arc, backend: Arc, pool: Arc, + statement_store: Option>, ) -> sc_service::error::Result; } @@ -47,6 +51,7 @@ impl ParachainClient, ParachainBackend, sc_transaction_pool::TransactionPoolHandle>, + sc_statement_store::Store, > for BuildParachainRpcExtensions where RuntimeApi: @@ -60,6 +65,7 @@ where pool: Arc< sc_transaction_pool::TransactionPoolHandle>, >, + statement_store: Option>, ) -> sc_service::error::Result { let build = || -> Result> { let mut module = RpcExtension::new(()); @@ -67,6 +73,9 @@ where module.merge(System::new(client.clone(), pool).into_rpc())?; module.merge(TransactionPayment::new(client.clone()).into_rpc())?; module.merge(StateMigration::new(client.clone(), backend).into_rpc())?; + if let Some(statement_store) = statement_store { + module.merge(StatementStore::new(statement_store).into_rpc())?; + } module.merge(Dev::new(client).into_rpc())?; Ok(module) diff --git a/cumulus/polkadot-omni-node/lib/src/common/spec.rs b/cumulus/polkadot-omni-node/lib/src/common/spec.rs index 9aebd4d5f268e..5331c7b461031 100644 --- a/cumulus/polkadot-omni-node/lib/src/common/spec.rs +++ b/cumulus/polkadot-omni-node/lib/src/common/spec.rs @@ -19,6 +19,7 @@ use crate::{ common::{ command::NodeCommandRunner, rpc::BuildRpcExtensions, + statement_store::{build_statement_store, new_statement_handler_proto}, types::{ ParachainBackend, ParachainBlockImport, ParachainClient, ParachainHostFunctions, ParachainService, @@ -44,6 +45,7 @@ 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_statement_store::Store; use sc_sysinfo::HwBench; use sc_telemetry::{TelemetryHandle, TelemetryWorker}; use sc_tracing::tracing::Instrument; @@ -277,6 +279,7 @@ pub(crate) trait NodeSpec: BaseNodeSpec { ParachainClient, ParachainBackend, TransactionPoolHandle>, + Store, >; type StartConsensus: StartConsensus< @@ -333,11 +336,19 @@ pub(crate) trait NodeSpec: BaseNodeSpec { let prometheus_registry = parachain_config.prometheus_registry().cloned(); let transaction_pool = params.transaction_pool.clone(); let import_queue_service = params.import_queue.service(); - let net_config = FullNetworkConfiguration::<_, _, Net>::new( + let mut net_config = FullNetworkConfiguration::<_, _, Net>::new( ¶chain_config.network, prometheus_registry.clone(), ); + let metrics = Net::register_notification_metrics( + parachain_config.prometheus_config.as_ref().map(|config| &config.registry), + ); + + let statement_handler_proto = node_extra_args.enable_statement_store.then(|| { + new_statement_handler_proto(&*client, ¶chain_config, &metrics, &mut net_config) + }); + let (network, system_rpc_tx, tx_handler_controller, sync_service) = build_network(BuildNetworkParams { parachain_config: ¶chain_config, @@ -349,10 +360,37 @@ pub(crate) trait NodeSpec: BaseNodeSpec { relay_chain_interface: relay_chain_interface.clone(), import_queue: params.import_queue, sybil_resistance_level: Self::SYBIL_RESISTANCE, + metrics, }) .await?; + let statement_store = statement_handler_proto + .map(|statement_handler_proto| { + build_statement_store( + ¶chain_config, + &mut task_manager, + client.clone(), + network.clone(), + sync_service.clone(), + params.keystore_container.local_keystore(), + statement_handler_proto, + ) + }) + .transpose()?; + if parachain_config.offchain_worker.enabled { + let custom_extensions = { + let statement_store = statement_store.clone(); + move |_hash| { + if let Some(statement_store) = &statement_store { + vec![Box::new(statement_store.clone().as_statement_store_ext()) + as Box<_>] + } else { + vec![] + } + } + }; + let offchain_workers = sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { runtime_api_provider: client.clone(), @@ -364,7 +402,7 @@ pub(crate) trait NodeSpec: BaseNodeSpec { network_provider: Arc::new(network.clone()), is_validator: parachain_config.role.is_authority(), enable_http_requests: true, - custom_extensions: move |_| vec![], + custom_extensions, })?; task_manager.spawn_handle().spawn( "offchain-workers-runner", @@ -377,12 +415,14 @@ pub(crate) trait NodeSpec: BaseNodeSpec { let client = client.clone(); let transaction_pool = transaction_pool.clone(); let backend_for_rpc = backend.clone(); + let statement_store = statement_store.clone(); Box::new(move |_| { Self::BuildRpcExtensions::build_rpc_extensions( client.clone(), backend_for_rpc.clone(), transaction_pool.clone(), + statement_store.clone(), ) }) }; diff --git a/cumulus/polkadot-omni-node/lib/src/common/statement_store.rs b/cumulus/polkadot-omni-node/lib/src/common/statement_store.rs new file mode 100644 index 0000000000000..604103e6c1a37 --- /dev/null +++ b/cumulus/polkadot-omni-node/lib/src/common/statement_store.rs @@ -0,0 +1,96 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Cumulus. +// SPDX-License-Identifier: Apache-2.0 + +// 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. + +use crate::common::{types::ParachainClient, ConstructNodeRuntimeApi, NodeBlock}; +use parachains_common::Hash; +use sc_network::{ + config::FullNetworkConfiguration, service::traits::NetworkService, NetworkBackend, +}; +use sc_service::{Configuration, TaskManager}; +use sc_statement_store::Store; +use std::sync::Arc; + +/// Helper function to setup the statement store in `NodeSpec::start_node`. +/// +/// Functions are tailored for internal usage, types are unnecessary opinionated for usage in +/// `NodeSpec::start_node`. + +/// Build the statement handler prototype. Register the notification protocol in the network +/// configuration. +pub(crate) fn new_statement_handler_proto< + Block: NodeBlock, + RuntimeApi, + Net: NetworkBackend, +>( + client: &ParachainClient, + parachain_config: &Configuration, + metrics: &sc_network::NotificationMetrics, + net_config: &mut FullNetworkConfiguration, +) -> sc_network_statement::StatementHandlerPrototype { + let (statement_handler_proto, statement_config) = + sc_network_statement::StatementHandlerPrototype::new::<_, _, Net>( + client.chain_info().genesis_hash, + parachain_config.chain_spec.fork_id(), + metrics.clone(), + Arc::clone(&net_config.peer_store_handle()), + ); + net_config.add_notification_protocol(statement_config); + statement_handler_proto +} + +/// Build the statement store, spawn the tasks. +pub(crate) fn build_statement_store< + Block: NodeBlock, + RuntimeApi: ConstructNodeRuntimeApi>, +>( + parachain_config: &Configuration, + task_manager: &mut TaskManager, + client: Arc>, + network: Arc, + sync_service: Arc>, + local_keystore: Arc, + statement_handler_proto: sc_network_statement::StatementHandlerPrototype, +) -> sc_service::error::Result> { + let statement_store = sc_statement_store::Store::new_shared( + ¶chain_config.data_path, + Default::default(), + client, + local_keystore, + parachain_config.prometheus_registry(), + &task_manager.spawn_handle(), + ) + .map_err(|e| sc_service::Error::Application(Box::new(e) as Box<_>))?; + 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, + sync_service, + statement_store.clone(), + parachain_config.prometheus_registry(), + statement_protocol_executor, + )?; + task_manager.spawn_handle().spawn( + "network-statement-handler", + Some("networking"), + statement_handler.run(), + ); + + Ok(statement_store) +} diff --git a/cumulus/polkadot-omni-node/lib/src/common/types.rs b/cumulus/polkadot-omni-node/lib/src/common/types.rs index 6915835137f72..bd43c697f2700 100644 --- a/cumulus/polkadot-omni-node/lib/src/common/types.rs +++ b/cumulus/polkadot-omni-node/lib/src/common/types.rs @@ -29,10 +29,14 @@ type Header = generic::Header; pub type Block = generic::Block, UncheckedExtrinsic>; #[cfg(not(feature = "runtime-benchmarks"))] -pub type ParachainHostFunctions = cumulus_client_service::ParachainHostFunctions; +pub type ParachainHostFunctions = ( + cumulus_client_service::ParachainHostFunctions, + sp_statement_store::runtime_api::HostFunctions, +); #[cfg(feature = "runtime-benchmarks")] pub type ParachainHostFunctions = ( cumulus_client_service::ParachainHostFunctions, + sp_statement_store::runtime_api::HostFunctions, frame_benchmarking::benchmarking::HostFunctions, ); diff --git a/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs b/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs index f22cbb59318d0..1354dbbfb00a0 100644 --- a/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs +++ b/cumulus/polkadot-omni-node/lib/src/fake_runtime_api/utils.rs @@ -237,6 +237,15 @@ macro_rules! impl_node_runtime_apis { unimplemented!() } } + + impl sp_statement_store::runtime_api::ValidateStatement<$block> for $runtime { + fn validate_statement( + _source: sp_statement_store::runtime_api::StatementSource, + _statement: sp_statement_store::Statement, + ) -> Result { + unimplemented!() + } + } } }; } diff --git a/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs index 00aa1d69c0427..edd19ac597939 100644 --- a/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs +++ b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs @@ -261,6 +261,7 @@ impl ManualSealNode { client.clone(), backend_for_rpc.clone(), transaction_pool.clone(), + None, )?; module .merge(ManualSeal::new(manual_seal_sink.clone()).into_rpc()) diff --git a/cumulus/test/service/src/lib.rs b/cumulus/test/service/src/lib.rs index c3b8baaa0ba25..517c127815f5a 100644 --- a/cumulus/test/service/src/lib.rs +++ b/cumulus/test/service/src/lib.rs @@ -390,6 +390,9 @@ where spawn_handle: task_manager.spawn_handle(), relay_chain_interface: relay_chain_interface.clone(), import_queue: params.import_queue, + metrics: Net::register_notification_metrics( + parachain_config.prometheus_config.as_ref().map(|config| &config.registry), + ), sybil_resistance_level: CollatorSybilResistance::Resistant, /* Either Aura that is * resistant or null that * is not producing any diff --git a/cumulus/zombienet/zombienet-sdk/Cargo.toml b/cumulus/zombienet/zombienet-sdk/Cargo.toml index 546bda1293b4c..49e5911660543 100644 --- a/cumulus/zombienet/zombienet-sdk/Cargo.toml +++ b/cumulus/zombienet/zombienet-sdk/Cargo.toml @@ -18,6 +18,9 @@ tokio = { workspace = true, features = ["rt-multi-thread"] } zombienet-sdk = { workspace = true } zombienet-orchestrator = { workspace = true } cumulus-zombienet-sdk-helpers = { workspace = true } +sp-statement-store = { workspace = true, default-features = true } +sp-keyring = { workspace = true, default-features = true } +sp-core = { workspace = true, default-features = true } [features] zombie-ci = [] diff --git a/cumulus/zombienet/zombienet-sdk/run.sh b/cumulus/zombienet/zombienet-sdk/run.sh index 377efdc5cb3f6..4f2955591dbf0 100755 --- a/cumulus/zombienet/zombienet-sdk/run.sh +++ b/cumulus/zombienet/zombienet-sdk/run.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -e -cargo build --release -p cumulus-test-service --bin test-parachain -p polkadot --bin polkadot-prepare-worker --bin polkadot-execute-worker --bin polkadot +cargo build --release -p cumulus-test-service --bin test-parachain -p polkadot --bin polkadot-prepare-worker --bin polkadot-execute-worker --bin polkadot -p polkadot-parachain-bin --bin polkadot-parachain RELEASE_DIR=$(dirname "$(cargo locate-project --workspace --message-format plain)")/target/release diff --git a/cumulus/zombienet/zombienet-sdk/tests/lib.rs b/cumulus/zombienet/zombienet-sdk/tests/lib.rs index 061db5fec20c9..256e4b3992f3a 100644 --- a/cumulus/zombienet/zombienet-sdk/tests/lib.rs +++ b/cumulus/zombienet/zombienet-sdk/tests/lib.rs @@ -9,3 +9,6 @@ mod sync_blocks; #[cfg(feature = "zombie-ci")] mod bootnodes; + +#[cfg(feature = "zombie-ci")] +mod statement_store; diff --git a/cumulus/zombienet/zombienet-sdk/tests/statement_store/mod.rs b/cumulus/zombienet/zombienet-sdk/tests/statement_store/mod.rs new file mode 100644 index 0000000000000..dc1c85441914f --- /dev/null +++ b/cumulus/zombienet/zombienet-sdk/tests/statement_store/mod.rs @@ -0,0 +1,118 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Test that people-westend enables the statement store in the node and that statements are +// propagated to peers. + +use anyhow::anyhow; +use sp_core::{Bytes, Encode}; +use zombienet_sdk::{ + subxt::{ + backend::rpc::RpcClient, rpc_params, utils::url_is_secure, OnlineClient, PolkadotConfig, + }, + NetworkConfigBuilder, +}; + +#[tokio::test(flavor = "multi_thread")] +async fn statement_store() -> Result<(), anyhow::Error> { + let _ = env_logger::try_init_from_env( + env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"), + ); + + let images = zombienet_sdk::environment::get_images_from_env(); + + let config = NetworkConfigBuilder::new() + .with_relaychain(|r| { + let r = r + .with_chain("westend-local") + .with_default_command("polkadot") + .with_default_image(images.polkadot.as_str()) + .with_default_args(vec!["-lparachain=debug".into()]) + // Have to set a `with_node` outside of the loop below, so that `r` has the right + // type. + .with_node(|node| node.with_name("validator-0")); + + (1..6).fold(r, |acc, i| acc.with_node(|node| node.with_name(&format!("validator-{i}")))) + }) + .with_parachain(|p| { + p.with_id(2400) + .with_default_command("polkadot-parachain") + .with_default_image(images.cumulus.as_str()) + .with_chain("people-westend-local") + .with_default_args(vec![ + "--force-authoring".into(), + "-lparachain=debug".into(), + "--enable-statement-store".into(), + ]) + .with_collator(|n| n.with_name("charlie")) + .with_collator(|n| n.with_name("dave")) + }) + .with_global_settings(|global_settings| match std::env::var("ZOMBIENET_SDK_BASE_DIR") { + Ok(val) => global_settings.with_base_dir(val), + _ => global_settings, + }) + .build() + .map_err(|e| { + let errs = e.into_iter().map(|e| e.to_string()).collect::>().join(" "); + anyhow!("config errs: {errs}") + })?; + + let spawn_fn = zombienet_sdk::environment::get_spawn_fn(); + let network = spawn_fn(config).await?; + + let charlie = network.get_node("charlie")?; + let dave = network.get_node("dave")?; + + let _charlie_client: OnlineClient = charlie.wait_client().await?; + let _dave_client: OnlineClient = charlie.wait_client().await?; + + let charlie_rpc = if url_is_secure(charlie.ws_uri())? { + RpcClient::from_url(&charlie.ws_uri()).await? + } else { + RpcClient::from_insecure_url(&charlie.ws_uri()).await? + }; + let dave_rpc = if url_is_secure(dave.ws_uri())? { + RpcClient::from_url(&dave.ws_uri()).await? + } else { + RpcClient::from_insecure_url(&dave.ws_uri()).await? + }; + + // Create the statement "1,2,3" signed by dave. + let mut statement = sp_statement_store::Statement::new(); + statement.set_plain_data(vec![1, 2, 3]); + let dave = sp_keyring::Sr25519Keyring::Dave; + statement.sign_sr25519_private(&dave.pair()); + let statement: Bytes = statement.encode().into(); + + // Submit the statement to charlie. + let _: () = charlie_rpc.request("statement_submit", rpc_params![statement.clone()]).await?; + + // Ensure that charlie stored the statement. + let charlie_dump: Vec = charlie_rpc.request("statement_dump", rpc_params![]).await?; + if charlie_dump != vec![statement.clone()] { + return Err(anyhow!("Charlie did not store the statement")); + } + + // Query dave until it receives the statement, stop if 20 seconds passed. + let query_start_time = std::time::SystemTime::now(); + let stop_after_secs = 20; + loop { + let dave_dump: Vec = dave_rpc.request("statement_dump", rpc_params![]).await?; + if !dave_dump.is_empty() { + if dave_dump != vec![statement.clone()] { + return Err(anyhow!("Dave statement store is not the expected one")); + } + break; + } + + let elapsed = + query_start_time.elapsed().map_err(|_| anyhow!("Failed to get elapsed time"))?; + if elapsed.as_secs() > stop_after_secs { + return Err(anyhow!("Dave did not receive the statement in time")); + } + + tokio::time::sleep(core::time::Duration::from_secs(1)).await; + } + + Ok(()) +} diff --git a/prdoc/pr_8076.prdoc b/prdoc/pr_8076.prdoc new file mode 100644 index 0000000000000..fadd47c81d811 --- /dev/null +++ b/prdoc/pr_8076.prdoc @@ -0,0 +1,24 @@ +title: 'Enable statement store with new CLI arg in polkadot-omni-node and polkadot-parachain' +doc: +- audience: Node Operator + description: |- + In `polkadot-omni-node-lib`, a new cli arg `--enable-statement-store` is introduced, if set to true then the statement store is enabled in the node. + + The statement store is an off-chain data-store for signed statements accessible via RPC and offchain worker. + It uses the runtime api to get the allowance associated to an account. + + This takes effect in `polkadot-omni-node` and `polkadot-parachain` and any node depending on `polkadot-omni-node-lib`. + + In `cumulus-client-service` the `BuildNetworkParams` now takes the metrics configuration explicitly, you can use the same configuration as before using the network backend used when calling `build_network`: + ```rust + let metrics = NetworkBackend::register_notification_metrics( + parachain_config.prometheus_config.as_ref().map(|config| &config.registry), + ); + ``` +crates: +- name: polkadot-omni-node-lib + bump: major +- name: cumulus-client-service + bump: major +- name: people-westend-runtime + bump: major diff --git a/templates/parachain/node/src/service.rs b/templates/parachain/node/src/service.rs index 3c29b3549bb8e..d825a7145b2e1 100644 --- a/templates/parachain/node/src/service.rs +++ b/templates/parachain/node/src/service.rs @@ -37,7 +37,7 @@ use prometheus_endpoint::Registry; use sc_client_api::Backend; use sc_consensus::ImportQueue; use sc_executor::{HeapAllocStrategy, WasmExecutor, DEFAULT_HEAP_ALLOC_STRATEGY}; -use sc_network::NetworkBlock; +use sc_network::{NetworkBackend, NetworkBlock}; use sc_service::{Configuration, PartialComponents, TFullBackend, TFullClient, TaskManager}; use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; @@ -296,6 +296,9 @@ pub async fn start_parachain_node( relay_chain_interface: relay_chain_interface.clone(), import_queue: params.import_queue, sybil_resistance_level: CollatorSybilResistance::Resistant, // because of Aura + metrics: sc_network::NetworkWorker::::register_notification_metrics( + parachain_config.prometheus_config.as_ref().map(|config| &config.registry), + ), }) .await?;