From e25bd4aa17d5a7ab3bf30275d37087f1164690ee Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Wed, 27 Mar 2024 15:40:51 +0100 Subject: [PATCH 01/39] Init --- .../subsystem-bench/examples/statement_distribution.yaml | 3 +++ polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs | 5 +++++ 2 files changed, 8 insertions(+) create mode 100644 polkadot/node/subsystem-bench/examples/statement_distribution.yaml diff --git a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml new file mode 100644 index 000000000000..2e9b8e832b2d --- /dev/null +++ b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml @@ -0,0 +1,3 @@ +TestConfiguration: +- objective: StatementDistribution + num_blocks: 3 \ No newline at end of file diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index 10953b6c7839..0fa989d8b405 100644 --- a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -40,6 +40,7 @@ pub enum TestObjective { DataAvailabilityWrite, /// Benchmark the approval-voting and approval-distribution subsystems. ApprovalVoting(approval::ApprovalsOptions), + StatementDistribution, } impl std::fmt::Display for TestObjective { @@ -51,6 +52,7 @@ impl std::fmt::Display for TestObjective { Self::DataAvailabilityRead(_) => "DataAvailabilityRead", Self::DataAvailabilityWrite => "DataAvailabilityWrite", Self::ApprovalVoting(_) => "ApprovalVoting", + Self::StatementDistribution => "StatementDistribution", } ) } @@ -170,6 +172,9 @@ impl BenchCli { state, )) }, + TestObjective::StatementDistribution => { + todo!(); + }, }; println!("{}", usage); } From 42f2a5534f5d01f9fb7e84e0a07d0da8470b6ce4 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 11 Apr 2024 10:00:45 +0200 Subject: [PATCH 02/39] Add a skeleton --- .../src/cli/subsystem-bench.rs | 11 ++- polkadot/node/subsystem-bench/src/lib/lib.rs | 1 + .../subsystem-bench/src/lib/statement/mod.rs | 97 +++++++++++++++++++ .../src/lib/statement/test_state.rs | 45 +++++++++ 4 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 polkadot/node/subsystem-bench/src/lib/statement/mod.rs create mode 100644 polkadot/node/subsystem-bench/src/lib/statement/test_state.rs diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index 0fa989d8b405..cb60f17b2c8b 100644 --- a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -20,7 +20,7 @@ use clap::Parser; use color_eyre::eyre; use colored::Colorize; -use polkadot_subsystem_bench::{approval, availability, configuration}; +use polkadot_subsystem_bench::{approval, availability, configuration, statement}; use pyroscope::PyroscopeAgent; use pyroscope_pprofrs::{pprof_backend, PprofConfig}; use serde::{Deserialize, Serialize}; @@ -40,6 +40,7 @@ pub enum TestObjective { DataAvailabilityWrite, /// Benchmark the approval-voting and approval-distribution subsystems. ApprovalVoting(approval::ApprovalsOptions), + // TODO StatementDistribution, } @@ -173,7 +174,13 @@ impl BenchCli { )) }, TestObjective::StatementDistribution => { - todo!(); + let state = statement::TestState::new(&test_config); + let mut env = statement::prepare_test(&state, true); + env.runtime().block_on(statement::benchmark_statement_distribution( + &benchmark_name, + &mut env, + &state, + )) }, }; println!("{}", usage); diff --git a/polkadot/node/subsystem-bench/src/lib/lib.rs b/polkadot/node/subsystem-bench/src/lib/lib.rs index ef2724abc989..90533523f289 100644 --- a/polkadot/node/subsystem-bench/src/lib/lib.rs +++ b/polkadot/node/subsystem-bench/src/lib/lib.rs @@ -25,5 +25,6 @@ pub(crate) mod environment; pub(crate) mod keyring; pub(crate) mod mock; pub(crate) mod network; +pub mod statement; pub mod usage; pub mod utils; diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs new file mode 100644 index 000000000000..a366d4f7b332 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -0,0 +1,97 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +use colored::Colorize; +use polkadot_overseer::{ + Handle as OverseerHandle, MetricsTrait, Overseer, OverseerConnector, OverseerMetrics, SpawnGlue, +}; +use sc_service::SpawnTaskHandle; +use std::time::Instant; +pub use test_state::TestState; + +const LOG_TARGET: &str = "subsystem-bench::availability"; + +use crate::{ + dummy_builder, + environment::{TestEnvironment, TestEnvironmentDependencies}, + mock::AlwaysSupportsParachains, + network::new_network, + usage::BenchmarkUsage, +}; + +mod test_state; + +/// Helper function to build an overseer with the real implementation for `ApprovalDistribution` and +/// `ApprovalVoting` subsystems and mock subsystems for all others. +fn build_overseer( + dependencies: &TestEnvironmentDependencies, +) -> (Overseer, AlwaysSupportsParachains>, OverseerHandle) { + let overseer_connector = OverseerConnector::with_event_capacity(64000); + let overseer_metrics = OverseerMetrics::try_register(&dependencies.registry).unwrap(); + let spawn_task_handle = dependencies.task_manager.spawn_handle(); + let dummy = dummy_builder!(spawn_task_handle, overseer_metrics); + let (overseer, raw_handle) = + dummy.build_with_connector(overseer_connector).expect("Should not fail"); + let overseer_handle = OverseerHandle::new(raw_handle); + + (overseer, overseer_handle) +} + +pub fn prepare_test(state: &TestState, with_prometheus_endpoint: bool) -> TestEnvironment { + let dependencies = TestEnvironmentDependencies::default(); + let (network, _network_interface, _network_receiver) = + new_network(&state.config, &dependencies, &state.test_authorities, vec![]); + let (overseer, overseer_handle) = build_overseer(&dependencies); + + TestEnvironment::new( + dependencies, + state.config.clone(), + network, + overseer, + overseer_handle, + state.test_authorities.clone(), + with_prometheus_endpoint, + ) +} + +pub async fn benchmark_statement_distribution( + benchmark_name: &str, + env: &mut TestEnvironment, + state: &TestState, +) -> BenchmarkUsage { + let config = env.config().clone(); + + env.metrics().set_n_validators(config.n_validators); + env.metrics().set_n_cores(config.n_cores); + + let test_start = Instant::now(); + for block_info in state.block_infos.iter() { + let block_num = block_info.number as usize; + gum::info!(target: LOG_TARGET, "Current block {}/{}", block_num, config.num_blocks); + env.metrics().set_current_block(block_num); + env.import_block(block_info.clone()).await; + } + + let duration: u128 = test_start.elapsed().as_millis(); + gum::info!(target: LOG_TARGET, "All blocks processed in {}", format!("{:?}ms", duration).cyan()); + gum::info!(target: LOG_TARGET, + "Avg block time: {}", + format!("{} ms", test_start.elapsed().as_millis() / env.config().num_blocks as u128).red() + ); + + env.stop().await; + env.collect_resource_usage(benchmark_name, &["statement-distribution"]) +} diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs new file mode 100644 index 000000000000..53f6182bd042 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -0,0 +1,45 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +use polkadot_node_subsystem_test_helpers::mock::new_block_import_info; +use polkadot_overseer::BlockInfo; +use polkadot_primitives::{BlockNumber, Hash}; + +use crate::configuration::{TestAuthorities, TestConfiguration}; + +pub struct TestState { + // Full test config + pub config: TestConfiguration, + // Authority keys for the network emulation. + pub test_authorities: TestAuthorities, + // Relay chain block infos + pub block_infos: Vec, +} + +impl TestState { + pub fn new(config: &TestConfiguration) -> Self { + Self { + config: config.clone(), + test_authorities: config.generate_authorities(), + block_infos: (1..=config.num_blocks) + .map(|block_num| { + let relay_block_hash = Hash::repeat_byte(block_num as u8); + new_block_import_info(relay_block_hash, block_num as BlockNumber) + }) + .collect(), + } + } +} From 3b4f88ed596489111f02dc74f27ef94cace225ae Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 11 Apr 2024 19:59:53 +0200 Subject: [PATCH 03/39] wip --- Cargo.lock | 1 + polkadot/node/subsystem-bench/Cargo.toml | 1 + .../src/cli/subsystem-bench.rs | 2 +- .../node/subsystem-bench/src/lib/mock/mod.rs | 1 + .../src/lib/mock/prospective_parachains.rs | 72 +++++++++ .../src/lib/mock/runtime_api.rs | 41 ++++- .../subsystem-bench/src/lib/statement/mod.rs | 147 ++++++++++++++---- .../src/lib/statement/test_state.rs | 137 ++++++++++++++-- 8 files changed, 361 insertions(+), 41 deletions(-) create mode 100644 polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs diff --git a/Cargo.lock b/Cargo.lock index c17b59c6af28..7252016a2464 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14194,6 +14194,7 @@ dependencies = [ "polkadot-overseer", "polkadot-primitives", "polkadot-primitives-test-helpers", + "polkadot-statement-distribution", "prometheus", "pyroscope", "pyroscope_pprofrs", diff --git a/polkadot/node/subsystem-bench/Cargo.toml b/polkadot/node/subsystem-bench/Cargo.toml index e534ac18e4b3..8b1910547f3e 100644 --- a/polkadot/node/subsystem-bench/Cargo.toml +++ b/polkadot/node/subsystem-bench/Cargo.toml @@ -28,6 +28,7 @@ polkadot-primitives = { path = "../../primitives" } polkadot-node-network-protocol = { path = "../network/protocol" } polkadot-availability-recovery = { path = "../network/availability-recovery", features = ["subsystem-benchmarks"] } polkadot-availability-distribution = { path = "../network/availability-distribution" } +polkadot-statement-distribution = { path = "../network/statement-distribution" } polkadot-node-core-av-store = { path = "../core/av-store" } polkadot-node-core-chain-api = { path = "../core/chain-api" } polkadot-availability-bitfield-distribution = { path = "../network/bitfield-distribution" } diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index cb60f17b2c8b..e081ce2999ff 100644 --- a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -175,7 +175,7 @@ impl BenchCli { }, TestObjective::StatementDistribution => { let state = statement::TestState::new(&test_config); - let mut env = statement::prepare_test(&state, true); + let (mut env, _protocol_config) = statement::prepare_test(&state, true); env.runtime().block_on(statement::benchmark_statement_distribution( &benchmark_name, &mut env, diff --git a/polkadot/node/subsystem-bench/src/lib/mock/mod.rs b/polkadot/node/subsystem-bench/src/lib/mock/mod.rs index 6dda9a47d398..54edf6193eb8 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/mod.rs @@ -22,6 +22,7 @@ pub mod av_store; pub mod chain_api; pub mod dummy; pub mod network_bridge; +pub mod prospective_parachains; pub mod runtime_api; pub struct AlwaysSupportsParachains {} diff --git a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs new file mode 100644 index 000000000000..c7ca4df34d59 --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs @@ -0,0 +1,72 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! A generic runtime api subsystem mockup suitable to be used in benchmarks. + +use futures::FutureExt; +use polkadot_node_subsystem::{ + messages::ProspectiveParachainsMessage, overseer, SpawnedSubsystem, SubsystemError, +}; +use polkadot_node_subsystem_types::OverseerSignal; + +const LOG_TARGET: &str = "subsystem-bench::prospective-parachains-mock"; + +pub struct MockProspectiveParachains {} + +impl MockProspectiveParachains { + pub fn new() -> Self { + Self {} + } +} + +#[overseer::subsystem(ProspectiveParachains, error=SubsystemError, prefix=self::overseer)] +impl MockProspectiveParachains { + fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = self.run(ctx).map(|_| Ok(())).boxed(); + + SpawnedSubsystem { name: "test-environment", future } + } +} + +#[overseer::contextbounds(ProspectiveParachains, prefix = self::overseer)] +impl MockProspectiveParachains { + async fn run(self, mut ctx: Context) { + loop { + let msg = ctx.recv().await.expect("Overseer never fails us"); + match msg { + orchestra::FromOrchestra::Signal(signal) => + if signal == OverseerSignal::Conclude { + return + }, + orchestra::FromOrchestra::Communication { msg } => { + gum::debug!(target: LOG_TARGET, msg=?msg, "recv message"); + + match msg { + ProspectiveParachainsMessage::GetMinimumRelayParents(_relay_parent, tx) => { + tx.send(vec![]).unwrap(); + }, + ProspectiveParachainsMessage::GetHypotheticalFrontier(_req, tx) => { + tx.send(vec![]).unwrap(); + }, + _ => { + unimplemented!("Unexpected chain-api message") + }, + } + }, + } + } + } +} diff --git a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs index b73d61321cd3..e8f372e37ccb 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs @@ -26,8 +26,8 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_types::OverseerSignal; use polkadot_primitives::{ - CandidateEvent, CandidateReceipt, CoreState, GroupIndex, IndexedVec, NodeFeatures, - OccupiedCore, SessionIndex, SessionInfo, ValidatorIndex, + AsyncBackingParams, CandidateEvent, CandidateReceipt, CoreState, GroupIndex, GroupRotationInfo, + IndexedVec, NodeFeatures, OccupiedCore, SessionIndex, SessionInfo, ValidatorIndex, }; use sp_consensus_babe::Epoch as BabeEpoch; use sp_core::H256; @@ -223,6 +223,43 @@ impl MockRuntimeApi { .clone() .expect("Babe epoch unpopulated"))); }, + RuntimeApiMessage::Request( + _block_hash, + RuntimeApiRequest::AsyncBackingParams(sender), + ) => { + let _ = sender.send(Ok(AsyncBackingParams { + max_candidate_depth: 1, + allowed_ancestry_len: 1, + })); + }, + RuntimeApiMessage::Request(_parent, RuntimeApiRequest::Version(tx)) => { + tx.send(Ok(RuntimeApiRequest::DISABLED_VALIDATORS_RUNTIME_REQUIREMENT)) + .unwrap(); + }, + RuntimeApiMessage::Request( + _parent, + RuntimeApiRequest::DisabledValidators(tx), + ) => { + tx.send(Ok(vec![])).unwrap(); + }, + RuntimeApiMessage::Request( + _parent, + RuntimeApiRequest::MinimumBackingVotes(_session_index, tx), + ) => { + tx.send(Ok(2)).unwrap(); + }, + RuntimeApiMessage::Request( + _parent, + RuntimeApiRequest::ValidatorGroups(tx), + ) => { + let groups = self.session_info().validator_groups.to_vec(); + let group_rotation_info = GroupRotationInfo { + session_start_block: 1, + group_rotation_frequency: 12, + now: 1, + }; + tx.send(Ok((groups, group_rotation_info))).unwrap(); + }, // Long term TODO: implement more as needed. message => { unimplemented!("Unexpected runtime-api message: {:?}", message) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index a366d4f7b332..2ac2a06c66c4 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -14,56 +14,120 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use colored::Colorize; -use polkadot_overseer::{ - Handle as OverseerHandle, MetricsTrait, Overseer, OverseerConnector, OverseerMetrics, SpawnGlue, -}; -use sc_service::SpawnTaskHandle; -use std::time::Instant; -pub use test_state::TestState; - -const LOG_TARGET: &str = "subsystem-bench::availability"; - use crate::{ dummy_builder, - environment::{TestEnvironment, TestEnvironmentDependencies}, - mock::AlwaysSupportsParachains, + environment::{TestEnvironment, TestEnvironmentDependencies, GENESIS_HASH}, + mock::{ + chain_api::{ChainApiState, MockChainApi}, + network_bridge::{MockNetworkBridgeRx, MockNetworkBridgeTx}, + prospective_parachains::MockProspectiveParachains, + runtime_api::MockRuntimeApi, + AlwaysSupportsParachains, + }, network::new_network, usage::BenchmarkUsage, }; +use colored::Colorize; +use polkadot_node_metrics::metrics::Metrics; +use polkadot_node_network_protocol::request_response::{IncomingRequest, ReqProtocolNames}; +use polkadot_node_primitives::{SignedFullStatementWithPVD, Statement}; +use polkadot_node_subsystem::messages::{AllMessages, StatementDistributionMessage}; +use polkadot_overseer::{ + Handle as OverseerHandle, Overseer, OverseerConnector, OverseerMetrics, SpawnGlue, +}; +use polkadot_primitives::{Block, Hash, SigningContext, ValidatorIndex, ValidatorPair}; +use polkadot_statement_distribution::StatementDistributionSubsystem; +use rand::SeedableRng; +use sc_keystore::LocalKeystore; +use sc_network::request_responses::ProtocolConfig; +use sc_service::SpawnTaskHandle; +use sp_core::Pair; +use std::{sync::Arc, time::Instant}; +pub use test_state::TestState; mod test_state; -/// Helper function to build an overseer with the real implementation for `ApprovalDistribution` and -/// `ApprovalVoting` subsystems and mock subsystems for all others. +const LOG_TARGET: &str = "subsystem-bench::availability"; + fn build_overseer( + state: &TestState, + network_bridge: (MockNetworkBridgeTx, MockNetworkBridgeRx), dependencies: &TestEnvironmentDependencies, -) -> (Overseer, AlwaysSupportsParachains>, OverseerHandle) { +) -> ( + Overseer, AlwaysSupportsParachains>, + OverseerHandle, + Vec, +) { let overseer_connector = OverseerConnector::with_event_capacity(64000); let overseer_metrics = OverseerMetrics::try_register(&dependencies.registry).unwrap(); let spawn_task_handle = dependencies.task_manager.spawn_handle(); - let dummy = dummy_builder!(spawn_task_handle, overseer_metrics); + let mock_runtime_api = MockRuntimeApi::new( + state.config.clone(), + state.test_authorities.clone(), + state.candidate_receipts.clone(), + Default::default(), + Default::default(), + 0, + ); + let chain_api_state = ChainApiState { block_headers: state.block_headers.clone() }; + let mock_chain_api = MockChainApi::new(chain_api_state); + let mock_prospective_parachains = MockProspectiveParachains::new(); + let (statement_req_receiver, statement_req_cfg) = IncomingRequest::get_config_receiver::< + Block, + sc_network::NetworkWorker, + >(&ReqProtocolNames::new(GENESIS_HASH, None)); + let (candidate_req_receiver, candidate_req_cfg) = IncomingRequest::get_config_receiver::< + Block, + sc_network::NetworkWorker, + >(&ReqProtocolNames::new(GENESIS_HASH, None)); + let subsystem = StatementDistributionSubsystem::new( + Arc::new(LocalKeystore::in_memory()), + statement_req_receiver, + candidate_req_receiver, + Metrics::try_register(&dependencies.registry).unwrap(), + rand::rngs::StdRng::from_entropy(), + ); + let dummy = dummy_builder!(spawn_task_handle, overseer_metrics) + .replace_runtime_api(|_| mock_runtime_api) + .replace_chain_api(|_| mock_chain_api) + .replace_prospective_parachains(|_| mock_prospective_parachains) + .replace_statement_distribution(|_| subsystem) + .replace_network_bridge_tx(|_| network_bridge.0) + .replace_network_bridge_rx(|_| network_bridge.1); let (overseer, raw_handle) = dummy.build_with_connector(overseer_connector).expect("Should not fail"); let overseer_handle = OverseerHandle::new(raw_handle); - (overseer, overseer_handle) + (overseer, overseer_handle, vec![statement_req_cfg, candidate_req_cfg]) } -pub fn prepare_test(state: &TestState, with_prometheus_endpoint: bool) -> TestEnvironment { +pub fn prepare_test( + state: &TestState, + with_prometheus_endpoint: bool, +) -> (TestEnvironment, Vec) { let dependencies = TestEnvironmentDependencies::default(); - let (network, _network_interface, _network_receiver) = + let (network, network_interface, network_receiver) = new_network(&state.config, &dependencies, &state.test_authorities, vec![]); - let (overseer, overseer_handle) = build_overseer(&dependencies); - - TestEnvironment::new( - dependencies, - state.config.clone(), - network, - overseer, - overseer_handle, + let network_bridge_tx = MockNetworkBridgeTx::new( + network.clone(), + network_interface.subsystem_sender(), state.test_authorities.clone(), - with_prometheus_endpoint, + ); + let network_bridge_rx = MockNetworkBridgeRx::new(network_receiver, None); + let (overseer, overseer_handle, cfg) = + build_overseer(state, (network_bridge_tx, network_bridge_rx), &dependencies); + + ( + TestEnvironment::new( + dependencies, + state.config.clone(), + network, + overseer, + overseer_handle, + state.test_authorities.clone(), + with_prometheus_endpoint, + ), + cfg, ) } @@ -77,12 +141,39 @@ pub async fn benchmark_statement_distribution( env.metrics().set_n_validators(config.n_validators); env.metrics().set_n_cores(config.n_cores); + let pair = ValidatorPair::generate().0; + let test_start = Instant::now(); for block_info in state.block_infos.iter() { let block_num = block_info.number as usize; gum::info!(target: LOG_TARGET, "Current block {}/{}", block_num, config.num_blocks); env.metrics().set_current_block(block_num); env.import_block(block_info.clone()).await; + + let receipts = state + .commited_candidate_receipts + .get(&block_info.hash) + .expect("Pregenerated") + .clone(); + + for receipt in receipts { + let statement = Statement::Seconded(receipt); + let context = SigningContext { parent_hash: block_info.parent_hash, session_index: 0 }; + let payload = statement.to_compact().signing_payload(&context); + let signature = pair.sign(&payload[..]); + let message = AllMessages::StatementDistribution(StatementDistributionMessage::Share( + block_info.hash, + SignedFullStatementWithPVD::new( + statement.supply_pvd(state.persisted_validation_data.clone()), + ValidatorIndex(0), + signature, + &context, + &pair.public(), + ) + .unwrap(), + )); + env.send_message(message).await; + } } let duration: u128 = test_start.elapsed().as_millis(); diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 53f6182bd042..32e92f0b15f5 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -14,11 +14,23 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use polkadot_node_subsystem_test_helpers::mock::new_block_import_info; +use crate::configuration::{TestAuthorities, TestConfiguration}; +use colored::Colorize; +use itertools::Itertools; +use polkadot_node_primitives::{AvailableData, BlockData, PoV}; +use polkadot_node_subsystem_test_helpers::{ + derive_erasure_chunks_with_proofs_and_root, mock::new_block_import_info, +}; use polkadot_overseer::BlockInfo; -use polkadot_primitives::{BlockNumber, Hash}; +use polkadot_primitives::{ + BlockNumber, CandidateReceipt, CommittedCandidateReceipt, Hash, HeadData, Header, + PersistedValidationData, +}; +use polkadot_primitives_test_helpers::{dummy_committed_candidate_receipt, dummy_hash}; +use sp_core::H256; +use std::{collections::HashMap, sync::Arc}; -use crate::configuration::{TestAuthorities, TestConfiguration}; +const LOG_TARGET: &str = "subsystem-bench::statement::test_state"; pub struct TestState { // Full test config @@ -27,19 +39,124 @@ pub struct TestState { pub test_authorities: TestAuthorities, // Relay chain block infos pub block_infos: Vec, + // Map from generated candidate receipts + pub candidate_receipts: HashMap>, + // Map from generated commited candidate receipts + pub commited_candidate_receipts: HashMap>, + // TODO + pub persisted_validation_data: PersistedValidationData, + // Relay chain block headers + pub block_headers: HashMap, } impl TestState { pub fn new(config: &TestConfiguration) -> Self { - Self { + let mut test_state = Self { config: config.clone(), test_authorities: config.generate_authorities(), - block_infos: (1..=config.num_blocks) - .map(|block_num| { - let relay_block_hash = Hash::repeat_byte(block_num as u8); - new_block_import_info(relay_block_hash, block_num as BlockNumber) - }) - .collect(), + block_infos: Default::default(), + candidate_receipts: Default::default(), + commited_candidate_receipts: Default::default(), + persisted_validation_data: PersistedValidationData { + parent_head: HeadData(vec![7, 8, 9]), + relay_parent_number: Default::default(), + max_pov_size: 1024, + relay_parent_storage_root: Default::default(), + }, + block_headers: Default::default(), + }; + + // For each unique pov we create a candidate receipt. + let pov_sizes = Vec::from(config.pov_sizes()); + let mut commited_candidate_receipt_templates: Vec = + Default::default(); + let mut pov_size_to_candidate: HashMap = Default::default(); + for (index, pov_size) in pov_sizes.iter().cloned().unique().enumerate() { + gum::info!(target: LOG_TARGET, index, pov_size, "{}", "Generating template candidate".bright_blue()); + + let mut commited_candidate_receipt = dummy_committed_candidate_receipt(dummy_hash()); + let pov = PoV { block_data: BlockData(vec![index as u8; pov_size]) }; + + let new_available_data = AvailableData { + validation_data: test_state.persisted_validation_data.clone(), + pov: Arc::new(pov), + }; + + let (_, erasure_root) = derive_erasure_chunks_with_proofs_and_root( + config.n_validators, + &new_available_data, + |_, _| {}, + ); + + commited_candidate_receipt.descriptor.erasure_root = erasure_root; + commited_candidate_receipt_templates.push(commited_candidate_receipt); + pov_size_to_candidate.insert(pov_size, index); + } + + test_state.block_infos = (1..=config.num_blocks) + .map(|block_num| { + let relay_block_hash = Hash::repeat_byte(block_num as u8); + new_block_import_info(relay_block_hash, block_num as BlockNumber) + }) + .collect(); + + test_state.block_headers = test_state + .block_infos + .iter() + .map(|info| { + ( + info.hash, + Header { + digest: Default::default(), + number: info.number, + parent_hash: info.parent_hash, + extrinsics_root: Default::default(), + state_root: Default::default(), + }, + ) + }) + .collect::>(); + + // Generate all candidates + let candidates_count = config.n_cores * config.num_blocks; + gum::info!(target: LOG_TARGET,"{}", format!("Pre-generating {} candidates.", candidates_count).bright_blue()); + let candidates = (0..candidates_count) + .map(|index| { + let pov_size = + pov_sizes.get(index % pov_sizes.len()).expect("This is a cycle; qed"); + let candidate_index = + *pov_size_to_candidate.get(&pov_size).expect("pov_size always exists; qed"); + let mut candidate_receipt = + commited_candidate_receipt_templates[candidate_index].clone(); + + // Make it unique. + candidate_receipt.descriptor.relay_parent = Hash::from_low_u64_be(index as u64); + + gum::debug!(target: LOG_TARGET, candidate_hash = ?candidate_receipt.hash(), "new candidate"); + + candidate_receipt + }) + .collect::>(); + + for info in test_state.block_infos.iter() { + for _ in 0..config.n_cores { + let receipt = candidates + .get(config.num_blocks * config.n_cores % candidates.len()) + .expect("Cycle"); + test_state + .commited_candidate_receipts + .entry(info.hash) + .or_default() + .push(receipt.clone()); + test_state.candidate_receipts.entry(info.hash).or_default().push( + CandidateReceipt { + descriptor: receipt.descriptor.clone(), + commitments_hash: receipt.commitments.hash(), + }, + ); + } } + + test_state } } From 0e474ce7ef1ae5b54f7af9b2a1d5ca0bc7ad0a6d Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Fri, 26 Apr 2024 15:51:39 +0200 Subject: [PATCH 04/39] Add first iteration --- .../examples/statement_distribution.yaml | 2 +- .../subsystem-bench/src/lib/approval/mod.rs | 5 +- .../src/lib/availability/mod.rs | 3 +- .../src/lib/mock/network_bridge.rs | 14 +- .../src/lib/mock/runtime_api.rs | 44 +++- .../node/subsystem-bench/src/lib/network.rs | 58 ++++- .../subsystem-bench/src/lib/statement/mod.rs | 201 ++++++++++++++--- .../src/lib/statement/test_state.rs | 212 ++++++++++++++---- 8 files changed, 447 insertions(+), 92 deletions(-) diff --git a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml index 2e9b8e832b2d..94d3ad0c49ec 100644 --- a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml +++ b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml @@ -1,3 +1,3 @@ TestConfiguration: - objective: StatementDistribution - num_blocks: 3 \ No newline at end of file + num_blocks: 10 diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs index 6ab5b86baede..026cf404a7f2 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs @@ -30,7 +30,7 @@ use crate::{ mock::{ chain_api::{ChainApiState, MockChainApi}, network_bridge::{MockNetworkBridgeRx, MockNetworkBridgeTx}, - runtime_api::MockRuntimeApi, + runtime_api::{MockRuntimeApi, MockRuntimeApiCoreState}, AlwaysSupportsParachains, TestSyncOracle, }, network::{ @@ -807,6 +807,7 @@ fn build_overseer( state.candidate_events_by_block(), Some(state.babe_epoch.clone()), 1, + MockRuntimeApiCoreState::Occupied, ); let mock_tx_bridge = MockNetworkBridgeTx::new( network.clone(), @@ -915,7 +916,7 @@ pub async fn bench_approvals_run( // First create the initialization messages that make sure that then node under // tests receives notifications about the topology used and the connected peers. - let mut initialization_messages = env.network().generate_peer_connected(); + let mut initialization_messages = env.network().generate_approval_distribution_peer_connected(); initialization_messages.extend(generate_new_session_topology( &state.test_authorities, ValidatorIndex(NODE_UNDER_TEST), diff --git a/polkadot/node/subsystem-bench/src/lib/availability/mod.rs b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs index 5b93c3d862de..f7d65589565b 100644 --- a/polkadot/node/subsystem-bench/src/lib/availability/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/availability/mod.rs @@ -22,7 +22,7 @@ use crate::{ av_store::{self, MockAvailabilityStore, NetworkAvailabilityState}, chain_api::{ChainApiState, MockChainApi}, network_bridge::{self, MockNetworkBridgeRx, MockNetworkBridgeTx}, - runtime_api::{self, MockRuntimeApi}, + runtime_api::{self, MockRuntimeApi, MockRuntimeApiCoreState}, AlwaysSupportsParachains, }, network::new_network, @@ -189,6 +189,7 @@ pub fn prepare_test( Default::default(), Default::default(), 0, + MockRuntimeApiCoreState::Occupied, ); let (overseer, overseer_handle) = match &mode { diff --git a/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs index ec66ad4e279c..0065407fef20 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs @@ -27,7 +27,10 @@ use polkadot_node_subsystem::{ messages::NetworkBridgeTxMessage, overseer, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_types::{ - messages::{ApprovalDistributionMessage, BitfieldDistributionMessage, NetworkBridgeEvent}, + messages::{ + ApprovalDistributionMessage, BitfieldDistributionMessage, NetworkBridgeEvent, + StatementDistributionMessage, + }, OverseerSignal, }; use sc_network::{request_responses::ProtocolConfig, RequestFailure}; @@ -141,7 +144,7 @@ impl MockNetworkBridgeTx { .expect("Should not fail"); } }, - _ => unimplemented!("Unexpected network bridge message"), + message => unimplemented!("Unexpected network bridge message {:?}", message), }, } } @@ -175,6 +178,13 @@ impl MockNetworkBridgeRx { ApprovalDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage(peer_id, polkadot_node_network_protocol::Versioned::V3(msg))) ).await; } + Versioned::V3( + polkadot_node_network_protocol::v3::ValidationProtocol::StatementDistribution(msg) + ) => { + ctx.send_message( + StatementDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage(peer_id, polkadot_node_network_protocol::Versioned::V3(msg))) + ).await; + } _ => { unimplemented!("We only talk v2 network protocol") }, diff --git a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs index e8f372e37ccb..77b759a5575e 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs @@ -27,7 +27,8 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_types::OverseerSignal; use polkadot_primitives::{ AsyncBackingParams, CandidateEvent, CandidateReceipt, CoreState, GroupIndex, GroupRotationInfo, - IndexedVec, NodeFeatures, OccupiedCore, SessionIndex, SessionInfo, ValidatorIndex, + IndexedVec, NodeFeatures, OccupiedCore, ScheduledCore, SessionIndex, SessionInfo, + ValidatorIndex, }; use sp_consensus_babe::Epoch as BabeEpoch; use sp_core::H256; @@ -49,11 +50,20 @@ pub struct RuntimeApiState { session_index: SessionIndex, } +#[derive(Clone)] +pub enum MockRuntimeApiCoreState { + Occupied, + Scheduled, + #[allow(dead_code)] + Free, +} + /// A mocked `runtime-api` subsystem. #[derive(Clone)] pub struct MockRuntimeApi { state: RuntimeApiState, config: TestConfiguration, + core_state: MockRuntimeApiCoreState, } impl MockRuntimeApi { @@ -64,6 +74,7 @@ impl MockRuntimeApi { included_candidates: HashMap>, babe_epoch: Option, session_index: SessionIndex, + core_state: MockRuntimeApiCoreState, ) -> MockRuntimeApi { Self { state: RuntimeApiState { @@ -74,6 +85,7 @@ impl MockRuntimeApi { session_index, }, config, + core_state, } } @@ -198,16 +210,26 @@ impl MockRuntimeApi { // Ensure test breaks if badly configured. assert!(index < validator_group_count); - CoreState::Occupied(OccupiedCore { - next_up_on_available: None, - occupied_since: 0, - time_out_at: 0, - next_up_on_time_out: None, - availability: BitVec::default(), - group_responsible: GroupIndex(index as u32), - candidate_hash: candidate_receipt.hash(), - candidate_descriptor: candidate_receipt.descriptor.clone(), - }) + use MockRuntimeApiCoreState::*; + match self.core_state { + Occupied => CoreState::Occupied(OccupiedCore { + next_up_on_available: None, + occupied_since: 0, + time_out_at: 0, + next_up_on_time_out: None, + availability: BitVec::default(), + group_responsible: GroupIndex(index as u32), + candidate_hash: candidate_receipt.hash(), + candidate_descriptor: candidate_receipt + .descriptor + .clone(), + }), + Scheduled => CoreState::Scheduled(ScheduledCore { + para_id: (index + 1).into(), + collator: None, + }), + Free => todo!(), + } }) .collect::>(); diff --git a/polkadot/node/subsystem-bench/src/lib/network.rs b/polkadot/node/subsystem-bench/src/lib/network.rs index 9bf2415e5a86..7d6a058059f2 100644 --- a/polkadot/node/subsystem-bench/src/lib/network.rs +++ b/polkadot/node/subsystem-bench/src/lib/network.rs @@ -53,10 +53,11 @@ use itertools::Itertools; use net_protocol::{ peer_set::{ProtocolVersion, ValidationVersion}, request_response::{Recipient, Requests, ResponseSender}, - ObservedRole, VersionedValidationProtocol, + ObservedRole, VersionedValidationProtocol, View, }; use parity_scale_codec::Encode; use polkadot_node_network_protocol::{self as net_protocol, Versioned}; +use polkadot_node_subsystem::messages::StatementDistributionMessage; use polkadot_node_subsystem_types::messages::{ApprovalDistributionMessage, NetworkBridgeEvent}; use polkadot_node_subsystem_util::metrics::prometheus::{ self, CounterVec, Opts, PrometheusError, Registry, @@ -437,6 +438,7 @@ pub struct EmulatedPeerHandle { /// Send actions to be performed by the peer. actions_tx: UnboundedSender, peer_id: PeerId, + authority_id: AuthorityDiscoveryId, } impl EmulatedPeerHandle { @@ -613,6 +615,7 @@ async fn emulated_peer_loop( } /// Creates a new peer emulator task and returns a handle to it. +#[allow(clippy::too_many_arguments)] pub fn new_peer( bandwidth: usize, spawn_task_handle: SpawnTaskHandle, @@ -621,6 +624,7 @@ pub fn new_peer( to_network_interface: UnboundedSender, latency_ms: usize, peer_id: PeerId, + authority_id: AuthorityDiscoveryId, ) -> EmulatedPeerHandle { let (messages_tx, messages_rx) = mpsc::unbounded::(); let (actions_tx, actions_rx) = mpsc::unbounded::(); @@ -649,7 +653,7 @@ pub fn new_peer( .boxed(), ); - EmulatedPeerHandle { messages_tx, actions_tx, peer_id } + EmulatedPeerHandle { messages_tx, actions_tx, peer_id, authority_id } } /// Book keeping of sent and received bytes. @@ -714,6 +718,18 @@ impl Peer { Peer::Disconnected(ref emulator) => emulator, } } + + pub fn authority_id(&self) -> AuthorityDiscoveryId { + match self { + Peer::Connected(handle) | Peer::Disconnected(handle) => handle.authority_id.clone(), + } + } + + pub fn peer_id(&self) -> PeerId { + match self { + Peer::Connected(handle) | Peer::Disconnected(handle) => handle.peer_id, + } + } } /// A ha emulated network implementation. @@ -728,8 +744,41 @@ pub struct NetworkEmulatorHandle { } impl NetworkEmulatorHandle { + pub fn generate_statement_distribution_peer_view_change(&self, view: View) -> Vec { + self.peers + .iter() + .filter(|peer| peer.is_connected()) + .map(|peer| { + AllMessages::StatementDistribution( + StatementDistributionMessage::NetworkBridgeUpdate( + NetworkBridgeEvent::PeerViewChange(peer.peer_id(), view.clone()), + ), + ) + }) + .collect_vec() + } + /// Generates peer_connected messages for all peers in `test_authorities` + pub fn generate_statement_distribution_peer_connected(&self) -> Vec { + self.peers + .iter() + .filter(|peer| peer.is_connected()) + .map(|peer| { + let network = NetworkBridgeEvent::PeerConnected( + peer.handle().peer_id, + ObservedRole::Authority, + ValidationVersion::V3.into(), + Some(vec![peer.authority_id()].into_iter().collect()), + ); + + AllMessages::StatementDistribution( + StatementDistributionMessage::NetworkBridgeUpdate(network), + ) + }) + .collect_vec() + } + /// Generates peer_connected messages for all peers in `test_authorities` - pub fn generate_peer_connected(&self) -> Vec { + pub fn generate_approval_distribution_peer_connected(&self) -> Vec { self.peers .iter() .filter(|peer| peer.is_connected()) @@ -772,7 +821,7 @@ pub fn new_network( let (stats, mut peers): (_, Vec<_>) = (0..n_peers) .zip(authorities.validator_authority_id.clone()) .map(|(peer_index, authority_id)| { - validator_authority_id_mapping.insert(authority_id, peer_index); + validator_authority_id_mapping.insert(authority_id.clone(), peer_index); let stats = Arc::new(PeerEmulatorStats::new(peer_index, metrics.clone())); ( stats.clone(), @@ -784,6 +833,7 @@ pub fn new_network( to_network_interface.clone(), random_latency(config.latency.as_ref()), *authorities.peer_ids.get(peer_index).unwrap(), + authority_id, )), ) }) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 2ac2a06c66c4..d89bcf2d0ef2 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -15,39 +15,67 @@ // along with Polkadot. If not, see . use crate::{ + configuration::TestAuthorities, dummy_builder, environment::{TestEnvironment, TestEnvironmentDependencies, GENESIS_HASH}, mock::{ chain_api::{ChainApiState, MockChainApi}, network_bridge::{MockNetworkBridgeRx, MockNetworkBridgeTx}, prospective_parachains::MockProspectiveParachains, - runtime_api::MockRuntimeApi, + runtime_api::{MockRuntimeApi, MockRuntimeApiCoreState}, AlwaysSupportsParachains, }, network::new_network, usage::BenchmarkUsage, + NODE_UNDER_TEST, }; use colored::Colorize; +use itertools::Itertools; use polkadot_node_metrics::metrics::Metrics; -use polkadot_node_network_protocol::request_response::{IncomingRequest, ReqProtocolNames}; +use polkadot_node_network_protocol::{ + grid_topology::{SessionGridTopology, TopologyPeerInfo}, + request_response::{IncomingRequest, ReqProtocolNames}, + view, View, +}; use polkadot_node_primitives::{SignedFullStatementWithPVD, Statement}; -use polkadot_node_subsystem::messages::{AllMessages, StatementDistributionMessage}; +use polkadot_node_subsystem::messages::{ + network_bridge_event::NewGossipTopology, AllMessages, NetworkBridgeEvent, + StatementDistributionMessage, +}; use polkadot_overseer::{ Handle as OverseerHandle, Overseer, OverseerConnector, OverseerMetrics, SpawnGlue, }; -use polkadot_primitives::{Block, Hash, SigningContext, ValidatorIndex, ValidatorPair}; +use polkadot_primitives::{ + AuthorityDiscoveryId, Block, Hash, SigningContext, ValidatorId, ValidatorIndex, +}; use polkadot_statement_distribution::StatementDistributionSubsystem; use rand::SeedableRng; use sc_keystore::LocalKeystore; use sc_network::request_responses::ProtocolConfig; +use sc_network_types::PeerId; use sc_service::SpawnTaskHandle; use sp_core::Pair; -use std::{sync::Arc, time::Instant}; +use sp_keystore::{Keystore, KeystorePtr}; +use sp_runtime::RuntimeAppPublic; +use std::{ + sync::{atomic::Ordering, Arc}, + time::{Duration, Instant}, +}; pub use test_state::TestState; +use tokio::time::sleep; mod test_state; -const LOG_TARGET: &str = "subsystem-bench::availability"; +const LOG_TARGET: &str = "subsystem-bench::statement"; + +pub fn make_keystore() -> KeystorePtr { + let keystore: KeystorePtr = Arc::new(LocalKeystore::in_memory()); + Keystore::sr25519_generate_new(&*keystore, ValidatorId::ID, Some("//Node0")) + .expect("Insert key into keystore"); + Keystore::sr25519_generate_new(&*keystore, AuthorityDiscoveryId::ID, Some("//Node0")) + .expect("Insert key into keystore"); + keystore +} fn build_overseer( state: &TestState, @@ -68,6 +96,7 @@ fn build_overseer( Default::default(), Default::default(), 0, + MockRuntimeApiCoreState::Scheduled, ); let chain_api_state = ChainApiState { block_headers: state.block_headers.clone() }; let mock_chain_api = MockChainApi::new(chain_api_state); @@ -80,8 +109,9 @@ fn build_overseer( Block, sc_network::NetworkWorker, >(&ReqProtocolNames::new(GENESIS_HASH, None)); + let keystore = make_keystore(); let subsystem = StatementDistributionSubsystem::new( - Arc::new(LocalKeystore::in_memory()), + keystore.clone(), statement_req_receiver, candidate_req_receiver, Metrics::try_register(&dependencies.registry).unwrap(), @@ -106,8 +136,12 @@ pub fn prepare_test( with_prometheus_endpoint: bool, ) -> (TestEnvironment, Vec) { let dependencies = TestEnvironmentDependencies::default(); - let (network, network_interface, network_receiver) = - new_network(&state.config, &dependencies, &state.test_authorities, vec![]); + let (network, network_interface, network_receiver) = new_network( + &state.config, + &dependencies, + &state.test_authorities, + vec![Arc::new(state.clone())], + ); let network_bridge_tx = MockNetworkBridgeTx::new( network.clone(), network_interface.subsystem_sender(), @@ -131,6 +165,52 @@ pub fn prepare_test( ) } +pub fn generate_peer_view_change(block_hash: Hash, peer_id: PeerId) -> AllMessages { + let network = NetworkBridgeEvent::PeerViewChange(peer_id, View::new([block_hash], 0)); + + AllMessages::StatementDistribution(StatementDistributionMessage::NetworkBridgeUpdate(network)) +} + +pub fn generate_new_session_topology( + test_authorities: &TestAuthorities, + test_node: ValidatorIndex, +) -> Vec { + let topology = generate_topology(test_authorities); + + let event = NetworkBridgeEvent::NewGossipTopology(NewGossipTopology { + session: 0, + topology, + local_index: Some(test_node), + }); + vec![AllMessages::StatementDistribution(StatementDistributionMessage::NetworkBridgeUpdate( + event, + ))] +} + +/// Generates a topology to be used for this benchmark. +pub fn generate_topology(test_authorities: &TestAuthorities) -> SessionGridTopology { + let keyrings = test_authorities + .validator_authority_id + .clone() + .into_iter() + .zip(test_authorities.peer_ids.clone()) + .collect_vec(); + + let topology = keyrings + .clone() + .into_iter() + .enumerate() + .map(|(index, (discovery_id, peer_id))| TopologyPeerInfo { + peer_ids: vec![peer_id], + validator_index: ValidatorIndex(index as u32), + discovery_id, + }) + .collect_vec(); + let shuffled = (0..keyrings.len()).collect_vec(); + + SessionGridTopology::new(shuffled, topology) +} + pub async fn benchmark_statement_distribution( benchmark_name: &str, env: &mut TestEnvironment, @@ -141,38 +221,101 @@ pub async fn benchmark_statement_distribution( env.metrics().set_n_validators(config.n_validators); env.metrics().set_n_cores(config.n_cores); - let pair = ValidatorPair::generate().0; + // First create the initialization messages that make sure that then node under + // tests receives notifications about the topology used and the connected peers. + let mut initialization_messages = + env.network().generate_statement_distribution_peer_connected(); + initialization_messages.extend(generate_new_session_topology( + &state.test_authorities, + ValidatorIndex(NODE_UNDER_TEST), + )); + for message in initialization_messages { + env.send_message(message).await; + } + let pair = state.validator_pairs.get(NODE_UNDER_TEST as usize).unwrap(); let test_start = Instant::now(); for block_info in state.block_infos.iter() { let block_num = block_info.number as usize; - gum::info!(target: LOG_TARGET, "Current block {}/{}", block_num, config.num_blocks); + gum::info!(target: LOG_TARGET, "Current block {}/{} {}", block_num, config.num_blocks, block_info.hash); env.metrics().set_current_block(block_num); env.import_block(block_info.clone()).await; - let receipts = state + for update in env + .network() + .generate_statement_distribution_peer_view_change(view![block_info.hash]) + { + env.send_message(update).await; + } + + let receipt = state .commited_candidate_receipts .get(&block_info.hash) .expect("Pregenerated") .clone(); + let candidate_hash = receipt.hash(); + let statement = Statement::Seconded(receipt.clone()); + let context = SigningContext { parent_hash: block_info.hash, session_index: 0 }; + let payload = statement.to_compact().signing_payload(&context); + let signature = pair.sign(&payload[..]); + let message = AllMessages::StatementDistribution(StatementDistributionMessage::Share( + block_info.hash, + SignedFullStatementWithPVD::new( + statement.supply_pvd(state.persisted_validation_data.clone()), + ValidatorIndex(0), + signature, + &context, + &pair.public(), + ) + .unwrap(), + )); + env.send_message(message).await; + + loop { + let seconded_count = state + .seconded_count + .get(&candidate_hash) + .expect("Pregenerated") + .load(Ordering::SeqCst); + gum::info!(target: LOG_TARGET, seconded_count = ?seconded_count); + if seconded_count < 4 { + sleep(Duration::from_millis(50)).await; + } else { + break; + } + } + + loop { + let statements_count = state + .statements_count + .get(&candidate_hash) + .expect("Pregenerated") + .load(Ordering::SeqCst); + gum::info!(target: LOG_TARGET, statements_count = ?statements_count); + if statements_count < 9 { + sleep(Duration::from_millis(50)).await; + } else { + break; + } + } + + let message = AllMessages::StatementDistribution(StatementDistributionMessage::Backed( + candidate_hash, + )); + env.send_message(message).await; - for receipt in receipts { - let statement = Statement::Seconded(receipt); - let context = SigningContext { parent_hash: block_info.parent_hash, session_index: 0 }; - let payload = statement.to_compact().signing_payload(&context); - let signature = pair.sign(&payload[..]); - let message = AllMessages::StatementDistribution(StatementDistributionMessage::Share( - block_info.hash, - SignedFullStatementWithPVD::new( - statement.supply_pvd(state.persisted_validation_data.clone()), - ValidatorIndex(0), - signature, - &context, - &pair.public(), - ) - .unwrap(), - )); - env.send_message(message).await; + loop { + let known_count = state + .known_count + .get(&candidate_hash) + .expect("Pregenerated") + .load(Ordering::SeqCst); + gum::info!(target: LOG_TARGET, known_count = ?known_count); + if known_count < 16 { + sleep(Duration::from_millis(50)).await; + } else { + break; + } } } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 32e92f0b15f5..4fc2b1ebb20a 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -14,24 +14,40 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use crate::configuration::{TestAuthorities, TestConfiguration}; +use crate::{ + configuration::{TestAuthorities, TestConfiguration}, + network::{HandleNetworkMessage, NetworkMessage}, +}; use colored::Colorize; +use futures::SinkExt; use itertools::Itertools; +use polkadot_node_network_protocol::{ + v3::{BackedCandidateAcknowledgement, StatementDistributionMessage, ValidationProtocol}, + Versioned, +}; use polkadot_node_primitives::{AvailableData, BlockData, PoV}; use polkadot_node_subsystem_test_helpers::{ derive_erasure_chunks_with_proofs_and_root, mock::new_block_import_info, }; use polkadot_overseer::BlockInfo; use polkadot_primitives::{ - BlockNumber, CandidateReceipt, CommittedCandidateReceipt, Hash, HeadData, Header, - PersistedValidationData, + BlockNumber, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CompactStatement, + Hash, HeadData, Header, PersistedValidationData, SignedStatement, SigningContext, + ValidatorIndex, ValidatorPair, }; use polkadot_primitives_test_helpers::{dummy_committed_candidate_receipt, dummy_hash}; -use sp_core::H256; -use std::{collections::HashMap, sync::Arc}; +use sp_core::{Pair, H256}; +use std::{ + collections::HashMap, + sync::{ + atomic::{AtomicBool, AtomicU32, Ordering}, + Arc, + }, +}; const LOG_TARGET: &str = "subsystem-bench::statement::test_state"; +#[derive(Clone)] pub struct TestState { // Full test config pub config: TestConfiguration, @@ -42,11 +58,18 @@ pub struct TestState { // Map from generated candidate receipts pub candidate_receipts: HashMap>, // Map from generated commited candidate receipts - pub commited_candidate_receipts: HashMap>, + pub commited_candidate_receipts: HashMap, // TODO pub persisted_validation_data: PersistedValidationData, // Relay chain block headers pub block_headers: HashMap, + // TODO + pub validator_pairs: Vec, + // TODO + pub seconded_tracker: HashMap>>, + pub seconded_count: HashMap>, + pub statements_count: HashMap>, + pub known_count: HashMap>, } impl TestState { @@ -64,6 +87,11 @@ impl TestState { relay_parent_storage_root: Default::default(), }, block_headers: Default::default(), + validator_pairs: Default::default(), + seconded_tracker: Default::default(), + seconded_count: Default::default(), + statements_count: Default::default(), + known_count: Default::default(), }; // For each unique pov we create a candidate receipt. @@ -117,46 +145,146 @@ impl TestState { }) .collect::>(); - // Generate all candidates - let candidates_count = config.n_cores * config.num_blocks; - gum::info!(target: LOG_TARGET,"{}", format!("Pre-generating {} candidates.", candidates_count).bright_blue()); - let candidates = (0..candidates_count) - .map(|index| { - let pov_size = - pov_sizes.get(index % pov_sizes.len()).expect("This is a cycle; qed"); - let candidate_index = - *pov_size_to_candidate.get(&pov_size).expect("pov_size always exists; qed"); - let mut candidate_receipt = - commited_candidate_receipt_templates[candidate_index].clone(); - - // Make it unique. - candidate_receipt.descriptor.relay_parent = Hash::from_low_u64_be(index as u64); + for (index, info) in test_state.block_infos.iter().enumerate() { + let pov_size = pov_sizes.get(index % pov_sizes.len()).expect("This is a cycle; qed"); + let candidate_index = + *pov_size_to_candidate.get(pov_size).expect("pov_size always exists; qed"); + let mut receipt = commited_candidate_receipt_templates[candidate_index].clone(); + receipt.descriptor.relay_parent = info.hash; + gum::debug!(target: LOG_TARGET, candidate_hash = ?receipt.hash(), "new candidate"); - gum::debug!(target: LOG_TARGET, candidate_hash = ?candidate_receipt.hash(), "new candidate"); + let descriptor = receipt.descriptor.clone(); + let commitments_hash = receipt.commitments.hash(); + let candidate_hash = receipt.hash(); + test_state.commited_candidate_receipts.insert(info.hash, receipt); + test_state + .candidate_receipts + .insert(info.hash, vec![CandidateReceipt { descriptor, commitments_hash }]); - candidate_receipt - }) - .collect::>(); - - for info in test_state.block_infos.iter() { - for _ in 0..config.n_cores { - let receipt = candidates - .get(config.num_blocks * config.n_cores % candidates.len()) - .expect("Cycle"); - test_state - .commited_candidate_receipts - .entry(info.hash) - .or_default() - .push(receipt.clone()); - test_state.candidate_receipts.entry(info.hash).or_default().push( - CandidateReceipt { - descriptor: receipt.descriptor.clone(), - commitments_hash: receipt.commitments.hash(), - }, - ); - } + test_state.seconded_tracker.insert( + candidate_hash, + HashMap::from_iter( + (1..=config.max_validators_per_core) + .map(|index| (index as u32, Arc::new(AtomicBool::new(false)))), + ), + ); + test_state.seconded_count.insert(candidate_hash, Arc::new(AtomicU32::new(1))); // one seconded in node under test + test_state.statements_count.insert(candidate_hash, Arc::new(AtomicU32::new(0))); + test_state.known_count.insert(candidate_hash, Arc::new(AtomicU32::new(0))); } + test_state.validator_pairs = test_state + .test_authorities + .key_seeds + .iter() + .map(|seed| ValidatorPair::from_string_with_seed(seed, None).unwrap().0) + .collect(); + test_state } } + +impl HandleNetworkMessage for TestState { + fn handle( + &self, + message: NetworkMessage, + node_sender: &mut futures::channel::mpsc::UnboundedSender, + ) -> Option { + match message { + NetworkMessage::MessageFromNode( + authority_id, + Versioned::V3(ValidationProtocol::StatementDistribution( + StatementDistributionMessage::Statement(relay_parent, statement), + )), + ) => { + let index = self + .test_authorities + .validator_authority_id + .iter() + .position(|v| v == &authority_id) + .expect("Should exist") as u32; + let candidate_hash = *statement.unchecked_payload().candidate_hash(); + self.statements_count + .get(&candidate_hash) + .expect("Pregenerated") + .as_ref() + .fetch_add(1, Ordering::SeqCst); + + let known = self + .seconded_tracker + .get(&candidate_hash) + .expect("Pregenerated") + .get(&index) + .expect("Pregenerated") + .as_ref(); + + if known.load(Ordering::SeqCst) { + return None + } else { + known.store(true, Ordering::SeqCst); + } + let statement = CompactStatement::Seconded(candidate_hash); + let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; + let payload = statement.signing_payload(&context); + let pair = self.validator_pairs.get(index as usize).expect("Must exist"); + let signature = pair.sign(&payload[..]); + let statement = SignedStatement::new( + statement, + ValidatorIndex(index), + signature, + &context, + &pair.public(), + ) + .unwrap(); + let unchecked = statement.as_unchecked().clone(); + + node_sender + .start_send_unpin(NetworkMessage::MessageFromPeer( + *self.test_authorities.peer_ids.get(index as usize).expect("Must exist"), + Versioned::V3(ValidationProtocol::StatementDistribution( + StatementDistributionMessage::Statement(relay_parent, unchecked), + )), + )) + .unwrap(); + self.seconded_count + .get(&candidate_hash) + .expect("Pregenerated") + .as_ref() + .fetch_add(1, Ordering::SeqCst); + None + }, + NetworkMessage::MessageFromNode( + authority_id, + Versioned::V3(ValidationProtocol::StatementDistribution( + StatementDistributionMessage::BackedCandidateManifest(manifest), + )), + ) => { + let index = self + .test_authorities + .validator_authority_id + .iter() + .position(|v| v == &authority_id) + .expect("Should exist"); + let ack = BackedCandidateAcknowledgement { + candidate_hash: manifest.candidate_hash, + statement_knowledge: manifest.statement_knowledge, + }; + node_sender + .start_send_unpin(NetworkMessage::MessageFromPeer( + *self.test_authorities.peer_ids.get(index).expect("Must exist"), + Versioned::V3(ValidationProtocol::StatementDistribution( + StatementDistributionMessage::BackedCandidateKnown(ack), + )), + )) + .unwrap(); + self.known_count + .get(&manifest.candidate_hash) + .expect("Pregenerated") + .as_ref() + .fetch_add(1, Ordering::SeqCst); + None + }, + _ => Some(message), + } + } +} From 4ff06ab73759e06c1402c3724e381f341c81b90f Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Fri, 3 May 2024 17:09:21 +0200 Subject: [PATCH 05/39] update --- .../examples/statement_distribution.yaml | 1 + .../src/cli/subsystem-bench.rs | 4 +- .../src/lib/mock/candidate_backing.rs | 101 ++++++++++++++++++ .../node/subsystem-bench/src/lib/mock/mod.rs | 1 + .../src/lib/mock/network_bridge.rs | 11 +- .../src/lib/mock/prospective_parachains.rs | 15 ++- .../node/subsystem-bench/src/lib/network.rs | 25 ++++- .../subsystem-bench/src/lib/statement/mod.rs | 89 ++++++++------- .../src/lib/statement/test_state.rs | 65 +++++------ 9 files changed, 227 insertions(+), 85 deletions(-) create mode 100644 polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs diff --git a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml index 94d3ad0c49ec..9fa7c6d0be37 100644 --- a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml +++ b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml @@ -1,3 +1,4 @@ TestConfiguration: - objective: StatementDistribution num_blocks: 10 + connectivity: 100 diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index e081ce2999ff..632f9b72a2ff 100644 --- a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -175,11 +175,13 @@ impl BenchCli { }, TestObjective::StatementDistribution => { let state = statement::TestState::new(&test_config); - let (mut env, _protocol_config) = statement::prepare_test(&state, true); + let (mut env, _protocol_config, to_subsystems) = + statement::prepare_test(&state, true); env.runtime().block_on(statement::benchmark_statement_distribution( &benchmark_name, &mut env, &state, + to_subsystems, )) }, }; diff --git a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs new file mode 100644 index 000000000000..16814d2d052b --- /dev/null +++ b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs @@ -0,0 +1,101 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! A generic candidate backing subsystem mockup suitable to be used in benchmarks. + +use futures::{channel::mpsc::UnboundedSender, FutureExt, SinkExt}; +use overseer::AllMessages; +use polkadot_node_primitives::{SignedFullStatementWithPVD, Statement, StatementWithPVD}; +use polkadot_node_subsystem::{ + messages::CandidateBackingMessage, overseer, SpawnedSubsystem, SubsystemError, +}; +use polkadot_node_subsystem_types::OverseerSignal; +use polkadot_primitives::{SigningContext, ValidatorIndex, ValidatorPair}; +use sp_core::Pair; + +const LOG_TARGET: &str = "subsystem-bench::candidate-backing-mock"; + +pub struct MockCandidateBacking { + to_subsystems: UnboundedSender, + pair: ValidatorPair, +} + +impl MockCandidateBacking { + pub fn new(to_subsystems: UnboundedSender, pair: ValidatorPair) -> Self { + Self { to_subsystems, pair } + } +} + +#[overseer::subsystem(CandidateBacking, error=SubsystemError, prefix=self::overseer)] +impl MockCandidateBacking { + fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = self.run(ctx).map(|_| Ok(())).boxed(); + + SpawnedSubsystem { name: "test-environment", future } + } +} + +#[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] +impl MockCandidateBacking { + async fn run(mut self, mut ctx: Context) { + loop { + let msg = ctx.recv().await.expect("Overseer never fails us"); + match msg { + orchestra::FromOrchestra::Signal(signal) => + if signal == OverseerSignal::Conclude { + return + }, + orchestra::FromOrchestra::Communication { msg } => { + gum::trace!(target: LOG_TARGET, msg=?msg, "recv message"); + + match msg { + CandidateBackingMessage::Statement(relay_parent, statement) => + match statement.payload() { + StatementWithPVD::Seconded(receipt, pvd) => { + let candidate_hash = receipt.hash(); + let statement = Statement::Valid(candidate_hash); + let context = SigningContext { + parent_hash: relay_parent, + session_index: 0, + }; + let payload = statement.to_compact().signing_payload(&context); + let signature = self.pair.sign(&payload[..]); + let message = AllMessages::StatementDistribution( + polkadot_node_subsystem::messages::StatementDistributionMessage::Share( + relay_parent, + SignedFullStatementWithPVD::new( + statement.supply_pvd(pvd.clone()), + ValidatorIndex(0), + signature, + &context, + &self.pair.public(), + ) + .unwrap(), + ), + ); + let _ = self.to_subsystems.send(message).await; + }, + StatementWithPVD::Valid(_) => todo!(), + }, + _ => { + unimplemented!("Unexpected chain-api message") + }, + } + }, + } + } + } +} diff --git a/polkadot/node/subsystem-bench/src/lib/mock/mod.rs b/polkadot/node/subsystem-bench/src/lib/mock/mod.rs index 54edf6193eb8..12766374bfa9 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/mod.rs @@ -19,6 +19,7 @@ use polkadot_node_subsystem_types::Hash; use sp_consensus::SyncOracle; pub mod av_store; +pub mod candidate_backing; pub mod chain_api; pub mod dummy; pub mod network_bridge; diff --git a/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs index 0065407fef20..dc48f6178f20 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs @@ -109,8 +109,15 @@ impl MockNetworkBridgeTx { NetworkBridgeTxMessage::SendRequests(requests, _if_disconnected) => { for request in requests { gum::debug!(target: LOG_TARGET, request = ?request, "Processing request"); - let peer_id = - request.authority_id().expect("all nodes are authorities").clone(); + let peer_id = match request.authority_id() { + Some(v) => v.clone(), + None => self + .test_authorities + .peer_id_to_authority + .get(request.peer_id().expect("Should exist")) + .expect("Should exist") + .clone(), + }; if !self.network.is_peer_connected(&peer_id) { // Attempting to send a request to a disconnected peer. diff --git a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs index c7ca4df34d59..506b13732a37 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs @@ -14,13 +14,14 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! A generic runtime api subsystem mockup suitable to be used in benchmarks. +//! A generic prospective parachains subsystem mockup suitable to be used in benchmarks. use futures::FutureExt; use polkadot_node_subsystem::{ messages::ProspectiveParachainsMessage, overseer, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_types::OverseerSignal; +use polkadot_primitives::Hash; const LOG_TARGET: &str = "subsystem-bench::prospective-parachains-mock"; @@ -58,8 +59,16 @@ impl MockProspectiveParachains { ProspectiveParachainsMessage::GetMinimumRelayParents(_relay_parent, tx) => { tx.send(vec![]).unwrap(); }, - ProspectiveParachainsMessage::GetHypotheticalFrontier(_req, tx) => { - tx.send(vec![]).unwrap(); + ProspectiveParachainsMessage::GetHypotheticalFrontier(req, tx) => { + tx.send( + req.candidates + .into_iter() + .map(|candidate| { + (candidate, vec![(Hash::repeat_byte(0), vec![0])]) + }) + .collect(), + ) + .unwrap(); }, _ => { unimplemented!("Unexpected chain-api message") diff --git a/polkadot/node/subsystem-bench/src/lib/network.rs b/polkadot/node/subsystem-bench/src/lib/network.rs index 7d6a058059f2..b0375599a4ef 100644 --- a/polkadot/node/subsystem-bench/src/lib/network.rs +++ b/polkadot/node/subsystem-bench/src/lib/network.rs @@ -149,6 +149,7 @@ impl RateLimit { /// A wrapper for both gossip and request/response protocols along with the destination /// peer(`AuthorityDiscoveryId``). +#[derive(Debug)] pub enum NetworkMessage { /// A gossip message from peer to node. MessageFromPeer(PeerId, VersionedValidationProtocol), @@ -431,7 +432,7 @@ impl NetworkInterface { } /// A handle for controlling an emulated peer. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct EmulatedPeerHandle { /// Send messages to be processed by the peer. messages_tx: UnboundedSender, @@ -693,7 +694,7 @@ impl PeerEmulatorStats { } /// The state of a peer on the emulated network. -#[derive(Clone)] +#[derive(Clone, Debug)] enum Peer { Connected(EmulatedPeerHandle), Disconnected(EmulatedPeerHandle), @@ -1021,6 +1022,8 @@ impl Metrics { pub trait RequestExt { /// Get the authority id if any from the request. fn authority_id(&self) -> Option<&AuthorityDiscoveryId>; + /// Get the peer id if any from the request. + fn peer_id(&self) -> Option<&PeerId>; /// Consume self and return the response sender. fn into_response_sender(self) -> ResponseSender; /// Allows to change the `ResponseSender` in place. @@ -1046,12 +1049,26 @@ impl RequestExt for Requests { None } }, + // Requested by PeerId + Requests::AttestedCandidateV2(_) => None, request => { unimplemented!("RequestAuthority not implemented for {:?}", request) }, } } + fn peer_id(&self) -> Option<&PeerId> { + match self { + Requests::AttestedCandidateV2(request) => match &request.peer { + Recipient::Authority(_) => None, + Recipient::Peer(peer_id) => Some(peer_id), + }, + request => { + unimplemented!("peer_id() is not implemented for {:?}", request) + }, + } + } + fn into_response_sender(self) -> ResponseSender { match self { Requests::ChunkFetchingV1(outgoing_request) => outgoing_request.pending_response, @@ -1068,6 +1085,8 @@ impl RequestExt for Requests { std::mem::replace(&mut outgoing_request.pending_response, new_sender), Requests::AvailableDataFetchingV1(outgoing_request) => std::mem::replace(&mut outgoing_request.pending_response, new_sender), + Requests::AttestedCandidateV2(outgoing_request) => + std::mem::replace(&mut outgoing_request.pending_response, new_sender), _ => unimplemented!("unsupported request type"), } } @@ -1078,6 +1097,8 @@ impl RequestExt for Requests { Requests::ChunkFetchingV1(outgoing_request) => outgoing_request.payload.encoded_size(), Requests::AvailableDataFetchingV1(outgoing_request) => outgoing_request.payload.encoded_size(), + Requests::AttestedCandidateV2(outgoing_request) => + outgoing_request.payload.encoded_size(), _ => unimplemented!("received an unexpected request"), } } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index d89bcf2d0ef2..d412a1bd6783 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -19,6 +19,7 @@ use crate::{ dummy_builder, environment::{TestEnvironment, TestEnvironmentDependencies, GENESIS_HASH}, mock::{ + candidate_backing::MockCandidateBacking, chain_api::{ChainApiState, MockChainApi}, network_bridge::{MockNetworkBridgeRx, MockNetworkBridgeTx}, prospective_parachains::MockProspectiveParachains, @@ -30,14 +31,14 @@ use crate::{ NODE_UNDER_TEST, }; use colored::Colorize; +use futures::{channel::mpsc, StreamExt}; use itertools::Itertools; use polkadot_node_metrics::metrics::Metrics; use polkadot_node_network_protocol::{ grid_topology::{SessionGridTopology, TopologyPeerInfo}, request_response::{IncomingRequest, ReqProtocolNames}, - view, View, + v3, view, Versioned, View, }; -use polkadot_node_primitives::{SignedFullStatementWithPVD, Statement}; use polkadot_node_subsystem::messages::{ network_bridge_event::NewGossipTopology, AllMessages, NetworkBridgeEvent, StatementDistributionMessage, @@ -46,7 +47,8 @@ use polkadot_overseer::{ Handle as OverseerHandle, Overseer, OverseerConnector, OverseerMetrics, SpawnGlue, }; use polkadot_primitives::{ - AuthorityDiscoveryId, Block, Hash, SigningContext, ValidatorId, ValidatorIndex, + AuthorityDiscoveryId, Block, CompactStatement, Hash, SignedStatement, SigningContext, + ValidatorId, ValidatorIndex, }; use polkadot_statement_distribution::StatementDistributionSubsystem; use rand::SeedableRng; @@ -85,6 +87,7 @@ fn build_overseer( Overseer, AlwaysSupportsParachains>, OverseerHandle, Vec, + mpsc::UnboundedReceiver, ) { let overseer_connector = OverseerConnector::with_event_capacity(64000); let overseer_metrics = OverseerMetrics::try_register(&dependencies.registry).unwrap(); @@ -101,6 +104,11 @@ fn build_overseer( let chain_api_state = ChainApiState { block_headers: state.block_headers.clone() }; let mock_chain_api = MockChainApi::new(chain_api_state); let mock_prospective_parachains = MockProspectiveParachains::new(); + let (tx, rx) = mpsc::unbounded(); + let mock_candidate_backing = MockCandidateBacking::new( + tx, + state.validator_pairs.get(NODE_UNDER_TEST as usize).unwrap().clone(), + ); let (statement_req_receiver, statement_req_cfg) = IncomingRequest::get_config_receiver::< Block, sc_network::NetworkWorker, @@ -121,6 +129,7 @@ fn build_overseer( .replace_runtime_api(|_| mock_runtime_api) .replace_chain_api(|_| mock_chain_api) .replace_prospective_parachains(|_| mock_prospective_parachains) + .replace_candidate_backing(|_| mock_candidate_backing) .replace_statement_distribution(|_| subsystem) .replace_network_bridge_tx(|_| network_bridge.0) .replace_network_bridge_rx(|_| network_bridge.1); @@ -128,13 +137,13 @@ fn build_overseer( dummy.build_with_connector(overseer_connector).expect("Should not fail"); let overseer_handle = OverseerHandle::new(raw_handle); - (overseer, overseer_handle, vec![statement_req_cfg, candidate_req_cfg]) + (overseer, overseer_handle, vec![statement_req_cfg, candidate_req_cfg], rx) } pub fn prepare_test( state: &TestState, with_prometheus_endpoint: bool, -) -> (TestEnvironment, Vec) { +) -> (TestEnvironment, Vec, mpsc::UnboundedReceiver) { let dependencies = TestEnvironmentDependencies::default(); let (network, network_interface, network_receiver) = new_network( &state.config, @@ -148,7 +157,7 @@ pub fn prepare_test( state.test_authorities.clone(), ); let network_bridge_rx = MockNetworkBridgeRx::new(network_receiver, None); - let (overseer, overseer_handle, cfg) = + let (overseer, overseer_handle, cfg, to_subsystems) = build_overseer(state, (network_bridge_tx, network_bridge_rx), &dependencies); ( @@ -162,6 +171,7 @@ pub fn prepare_test( with_prometheus_endpoint, ), cfg, + to_subsystems, ) } @@ -215,6 +225,7 @@ pub async fn benchmark_statement_distribution( benchmark_name: &str, env: &mut TestEnvironment, state: &TestState, + mut to_subsystems: mpsc::UnboundedReceiver, ) -> BenchmarkUsage { let config = env.config().clone(); @@ -232,7 +243,7 @@ pub async fn benchmark_statement_distribution( for message in initialization_messages { env.send_message(message).await; } - let pair = state.validator_pairs.get(NODE_UNDER_TEST as usize).unwrap(); + let pair = state.validator_pairs.get((NODE_UNDER_TEST + 1) as usize).unwrap(); let test_start = Instant::now(); for block_info in state.block_infos.iter() { @@ -253,50 +264,34 @@ pub async fn benchmark_statement_distribution( .get(&block_info.hash) .expect("Pregenerated") .clone(); + + let relay_parent = block_info.hash; let candidate_hash = receipt.hash(); - let statement = Statement::Seconded(receipt.clone()); - let context = SigningContext { parent_hash: block_info.hash, session_index: 0 }; - let payload = statement.to_compact().signing_payload(&context); + let statement = CompactStatement::Seconded(candidate_hash); + let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; + let payload = statement.signing_payload(&context); let signature = pair.sign(&payload[..]); - let message = AllMessages::StatementDistribution(StatementDistributionMessage::Share( - block_info.hash, - SignedFullStatementWithPVD::new( - statement.supply_pvd(state.persisted_validation_data.clone()), - ValidatorIndex(0), - signature, - &context, - &pair.public(), - ) - .unwrap(), - )); + let statement = SignedStatement::new( + statement, + ValidatorIndex(NODE_UNDER_TEST + 1), + signature, + &context, + &pair.public(), + ) + .unwrap() + .as_unchecked() + .to_owned(); + let message = AllMessages::StatementDistribution( + StatementDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( + *state.test_authorities.peer_ids.get((NODE_UNDER_TEST + 1) as usize).unwrap(), + Versioned::V3(v3::StatementDistributionMessage::Statement(relay_parent, statement)), + )), + ); env.send_message(message).await; - loop { - let seconded_count = state - .seconded_count - .get(&candidate_hash) - .expect("Pregenerated") - .load(Ordering::SeqCst); - gum::info!(target: LOG_TARGET, seconded_count = ?seconded_count); - if seconded_count < 4 { - sleep(Duration::from_millis(50)).await; - } else { - break; - } - } - - loop { - let statements_count = state - .statements_count - .get(&candidate_hash) - .expect("Pregenerated") - .load(Ordering::SeqCst); - gum::info!(target: LOG_TARGET, statements_count = ?statements_count); - if statements_count < 9 { - sleep(Duration::from_millis(50)).await; - } else { - break; - } + if let Some(msg) = to_subsystems.next().await { + gum::info!(msg = ?msg); + env.send_message(msg).await; } let message = AllMessages::StatementDistribution(StatementDistributionMessage::Backed( diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 4fc2b1ebb20a..ac4783108210 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -21,7 +21,9 @@ use crate::{ use colored::Colorize; use futures::SinkExt; use itertools::Itertools; +use parity_scale_codec::Encode; use polkadot_node_network_protocol::{ + request_response::{v2::AttestedCandidateResponse, Requests}, v3::{BackedCandidateAcknowledgement, StatementDistributionMessage, ValidationProtocol}, Versioned, }; @@ -31,11 +33,13 @@ use polkadot_node_subsystem_test_helpers::{ }; use polkadot_overseer::BlockInfo; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CompactStatement, - Hash, HeadData, Header, PersistedValidationData, SignedStatement, SigningContext, - ValidatorIndex, ValidatorPair, + BlockNumber, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, Hash, Header, + PersistedValidationData, ValidatorPair, }; -use polkadot_primitives_test_helpers::{dummy_committed_candidate_receipt, dummy_hash}; +use polkadot_primitives_test_helpers::{ + dummy_committed_candidate_receipt, dummy_hash, dummy_head_data, dummy_pvd, +}; +use sc_network::ProtocolName; use sp_core::{Pair, H256}; use std::{ collections::HashMap, @@ -80,12 +84,7 @@ impl TestState { block_infos: Default::default(), candidate_receipts: Default::default(), commited_candidate_receipts: Default::default(), - persisted_validation_data: PersistedValidationData { - parent_head: HeadData(vec![7, 8, 9]), - relay_parent_number: Default::default(), - max_pov_size: 1024, - relay_parent_storage_root: Default::default(), - }, + persisted_validation_data: dummy_pvd(dummy_head_data(), 0), block_headers: Default::default(), validator_pairs: Default::default(), seconded_tracker: Default::default(), @@ -117,6 +116,8 @@ impl TestState { ); commited_candidate_receipt.descriptor.erasure_root = erasure_root; + commited_candidate_receipt.descriptor.persisted_validation_data_hash = + test_state.persisted_validation_data.hash(); commited_candidate_receipt_templates.push(commited_candidate_receipt); pov_size_to_candidate.insert(pov_size, index); } @@ -165,10 +166,10 @@ impl TestState { candidate_hash, HashMap::from_iter( (1..=config.max_validators_per_core) - .map(|index| (index as u32, Arc::new(AtomicBool::new(false)))), + .map(|index| (index as u32, Arc::new(AtomicBool::new(index == 1)))), ), ); - test_state.seconded_count.insert(candidate_hash, Arc::new(AtomicU32::new(1))); // one seconded in node under test + test_state.seconded_count.insert(candidate_hash, Arc::new(AtomicU32::new(1))); test_state.statements_count.insert(candidate_hash, Arc::new(AtomicU32::new(0))); test_state.known_count.insert(candidate_hash, Arc::new(AtomicU32::new(0))); } @@ -191,6 +192,24 @@ impl HandleNetworkMessage for TestState { node_sender: &mut futures::channel::mpsc::UnboundedSender, ) -> Option { match message { + NetworkMessage::RequestFromNode(_authority_id, Requests::AttestedCandidateV2(req)) => { + let payload = req.payload; + let candidate_receipt = self + .commited_candidate_receipts + .values() + .find(|v| v.hash() == payload.candidate_hash) + .expect("Pregenerated") + .clone(); + let persisted_validation_data = self.persisted_validation_data.clone(); + + let res = AttestedCandidateResponse { + candidate_receipt, + persisted_validation_data, + statements: vec![], + }; + let _ = req.pending_response.send(Ok((res.encode(), ProtocolName::from("")))); + None + }, NetworkMessage::MessageFromNode( authority_id, Versioned::V3(ValidationProtocol::StatementDistribution( @@ -202,7 +221,7 @@ impl HandleNetworkMessage for TestState { .validator_authority_id .iter() .position(|v| v == &authority_id) - .expect("Should exist") as u32; + .expect("Should exist"); let candidate_hash = *statement.unchecked_payload().candidate_hash(); self.statements_count .get(&candidate_hash) @@ -214,7 +233,7 @@ impl HandleNetworkMessage for TestState { .seconded_tracker .get(&candidate_hash) .expect("Pregenerated") - .get(&index) + .get(&(index as u32)) .expect("Pregenerated") .as_ref(); @@ -223,26 +242,12 @@ impl HandleNetworkMessage for TestState { } else { known.store(true, Ordering::SeqCst); } - let statement = CompactStatement::Seconded(candidate_hash); - let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; - let payload = statement.signing_payload(&context); - let pair = self.validator_pairs.get(index as usize).expect("Must exist"); - let signature = pair.sign(&payload[..]); - let statement = SignedStatement::new( - statement, - ValidatorIndex(index), - signature, - &context, - &pair.public(), - ) - .unwrap(); - let unchecked = statement.as_unchecked().clone(); node_sender .start_send_unpin(NetworkMessage::MessageFromPeer( - *self.test_authorities.peer_ids.get(index as usize).expect("Must exist"), + *self.test_authorities.peer_ids.get(index).expect("Must exist"), Versioned::V3(ValidationProtocol::StatementDistribution( - StatementDistributionMessage::Statement(relay_parent, unchecked), + StatementDistributionMessage::Statement(relay_parent, statement), )), )) .unwrap(); From c1905899f85807e18f7721f028feda376ea197b0 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Fri, 3 May 2024 19:34:12 +0200 Subject: [PATCH 06/39] update --- .../src/lib/mock/candidate_backing.rs | 42 ++++++++++-- .../src/lib/mock/network_bridge.rs | 16 +++++ .../subsystem-bench/src/lib/statement/mod.rs | 16 +++-- .../src/lib/statement/test_state.rs | 65 +++++++++---------- 4 files changed, 90 insertions(+), 49 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs index 16814d2d052b..27a965d75773 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs @@ -23,19 +23,25 @@ use polkadot_node_subsystem::{ messages::CandidateBackingMessage, overseer, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_types::OverseerSignal; -use polkadot_primitives::{SigningContext, ValidatorIndex, ValidatorPair}; +use polkadot_primitives::{PersistedValidationData, SigningContext, ValidatorIndex, ValidatorPair}; use sp_core::Pair; +use crate::NODE_UNDER_TEST; const LOG_TARGET: &str = "subsystem-bench::candidate-backing-mock"; pub struct MockCandidateBacking { to_subsystems: UnboundedSender, pair: ValidatorPair, + pvd: PersistedValidationData, } impl MockCandidateBacking { - pub fn new(to_subsystems: UnboundedSender, pair: ValidatorPair) -> Self { - Self { to_subsystems, pair } + pub fn new( + to_subsystems: UnboundedSender, + pair: ValidatorPair, + pvd: PersistedValidationData, + ) -> Self { + Self { to_subsystems, pair, pvd } } } @@ -64,7 +70,7 @@ impl MockCandidateBacking { match msg { CandidateBackingMessage::Statement(relay_parent, statement) => match statement.payload() { - StatementWithPVD::Seconded(receipt, pvd) => { + StatementWithPVD::Seconded(receipt, _pvd) => { let candidate_hash = receipt.hash(); let statement = Statement::Valid(candidate_hash); let context = SigningContext { @@ -77,8 +83,8 @@ impl MockCandidateBacking { polkadot_node_subsystem::messages::StatementDistributionMessage::Share( relay_parent, SignedFullStatementWithPVD::new( - statement.supply_pvd(pvd.clone()), - ValidatorIndex(0), + statement.supply_pvd(self.pvd.clone()), + ValidatorIndex(NODE_UNDER_TEST), signature, &context, &self.pair.public(), @@ -88,7 +94,29 @@ impl MockCandidateBacking { ); let _ = self.to_subsystems.send(message).await; }, - StatementWithPVD::Valid(_) => todo!(), + StatementWithPVD::Valid(candidate_hash) => { + let statement = Statement::Valid(candidate_hash.clone()); + let context = SigningContext { + parent_hash: relay_parent, + session_index: 0, + }; + let payload = statement.to_compact().signing_payload(&context); + let signature = self.pair.sign(&payload[..]); + let message = AllMessages::StatementDistribution( + polkadot_node_subsystem::messages::StatementDistributionMessage::Share( + relay_parent, + SignedFullStatementWithPVD::new( + statement.supply_pvd(self.pvd.clone()), + ValidatorIndex(NODE_UNDER_TEST), + signature, + &context, + &self.pair.public(), + ) + .unwrap(), + ), + ); + let _ = self.to_subsystems.send(message).await; + }, }, _ => { unimplemented!("Unexpected chain-api message") diff --git a/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs index dc48f6178f20..276f987116be 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs @@ -151,6 +151,22 @@ impl MockNetworkBridgeTx { .expect("Should not fail"); } }, + NetworkBridgeTxMessage::SendValidationMessages(messages) => { + for (peers, message) in messages { + for peer in peers { + self.to_network_interface + .unbounded_send(NetworkMessage::MessageFromNode( + self.test_authorities + .peer_id_to_authority + .get(&peer) + .unwrap() + .clone(), + message.clone(), + )) + .expect("Should not fail"); + } + } + }, message => unimplemented!("Unexpected network bridge message {:?}", message), }, } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index d412a1bd6783..9f254e4f28c4 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -108,6 +108,7 @@ fn build_overseer( let mock_candidate_backing = MockCandidateBacking::new( tx, state.validator_pairs.get(NODE_UNDER_TEST as usize).unwrap().clone(), + state.persisted_validation_data.clone(), ); let (statement_req_receiver, statement_req_cfg) = IncomingRequest::get_config_receiver::< Block, @@ -300,14 +301,17 @@ pub async fn benchmark_statement_distribution( env.send_message(message).await; loop { - let known_count = state - .known_count + let count = state + .statements_tracker .get(&candidate_hash) .expect("Pregenerated") - .load(Ordering::SeqCst); - gum::info!(target: LOG_TARGET, known_count = ?known_count); - if known_count < 16 { - sleep(Duration::from_millis(50)).await; + .values() + .filter(|v| v.load(Ordering::SeqCst)) + .collect::>() + .len(); + gum::info!(target: LOG_TARGET, count = ?count); + if count < 100 { + sleep(Duration::from_millis(1000)).await; } else { break; } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index ac4783108210..8fe81bde5b99 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -33,8 +33,9 @@ use polkadot_node_subsystem_test_helpers::{ }; use polkadot_overseer::BlockInfo; use polkadot_primitives::{ - BlockNumber, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, Hash, Header, - PersistedValidationData, ValidatorPair, + BlockNumber, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CompactStatement, + Hash, Header, PersistedValidationData, SignedStatement, SigningContext, ValidatorIndex, + ValidatorPair, }; use polkadot_primitives_test_helpers::{ dummy_committed_candidate_receipt, dummy_hash, dummy_head_data, dummy_pvd, @@ -44,7 +45,7 @@ use sp_core::{Pair, H256}; use std::{ collections::HashMap, sync::{ - atomic::{AtomicBool, AtomicU32, Ordering}, + atomic::{AtomicBool, Ordering}, Arc, }, }; @@ -70,10 +71,7 @@ pub struct TestState { // TODO pub validator_pairs: Vec, // TODO - pub seconded_tracker: HashMap>>, - pub seconded_count: HashMap>, - pub statements_count: HashMap>, - pub known_count: HashMap>, + pub statements_tracker: HashMap>>, } impl TestState { @@ -87,10 +85,7 @@ impl TestState { persisted_validation_data: dummy_pvd(dummy_head_data(), 0), block_headers: Default::default(), validator_pairs: Default::default(), - seconded_tracker: Default::default(), - seconded_count: Default::default(), - statements_count: Default::default(), - known_count: Default::default(), + statements_tracker: Default::default(), }; // For each unique pov we create a candidate receipt. @@ -162,16 +157,13 @@ impl TestState { .candidate_receipts .insert(info.hash, vec![CandidateReceipt { descriptor, commitments_hash }]); - test_state.seconded_tracker.insert( + test_state.statements_tracker.insert( candidate_hash, HashMap::from_iter( - (1..=config.max_validators_per_core) - .map(|index| (index as u32, Arc::new(AtomicBool::new(index == 1)))), + (0..config.n_validators) + .map(|index| (index as u32, Arc::new(AtomicBool::new(index <= 1)))), ), ); - test_state.seconded_count.insert(candidate_hash, Arc::new(AtomicU32::new(1))); - test_state.statements_count.insert(candidate_hash, Arc::new(AtomicU32::new(0))); - test_state.known_count.insert(candidate_hash, Arc::new(AtomicU32::new(0))); } test_state.validator_pairs = test_state @@ -223,26 +215,37 @@ impl HandleNetworkMessage for TestState { .position(|v| v == &authority_id) .expect("Should exist"); let candidate_hash = *statement.unchecked_payload().candidate_hash(); - self.statements_count - .get(&candidate_hash) - .expect("Pregenerated") - .as_ref() - .fetch_add(1, Ordering::SeqCst); - let known = self - .seconded_tracker + let sent = self + .statements_tracker .get(&candidate_hash) .expect("Pregenerated") .get(&(index as u32)) .expect("Pregenerated") .as_ref(); - if known.load(Ordering::SeqCst) { + if sent.load(Ordering::SeqCst) { return None } else { - known.store(true, Ordering::SeqCst); + sent.store(true, Ordering::SeqCst); } + let statement = CompactStatement::Valid(candidate_hash); + let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; + let payload = statement.signing_payload(&context); + let pair = self.validator_pairs.get(index).unwrap(); + let signature = pair.sign(&payload[..]); + let statement = SignedStatement::new( + statement, + ValidatorIndex(index as u32), + signature, + &context, + &pair.public(), + ) + .unwrap() + .as_unchecked() + .to_owned(); + node_sender .start_send_unpin(NetworkMessage::MessageFromPeer( *self.test_authorities.peer_ids.get(index).expect("Must exist"), @@ -251,11 +254,6 @@ impl HandleNetworkMessage for TestState { )), )) .unwrap(); - self.seconded_count - .get(&candidate_hash) - .expect("Pregenerated") - .as_ref() - .fetch_add(1, Ordering::SeqCst); None }, NetworkMessage::MessageFromNode( @@ -282,11 +280,6 @@ impl HandleNetworkMessage for TestState { )), )) .unwrap(); - self.known_count - .get(&manifest.candidate_hash) - .expect("Pregenerated") - .as_ref() - .fetch_add(1, Ordering::SeqCst); None }, _ => Some(message), From 9151571fa545053e708733ad645f7620a4573ed4 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Mon, 6 May 2024 18:06:31 +0200 Subject: [PATCH 07/39] update --- .../examples/statement_distribution.yaml | 1 + .../src/lib/mock/candidate_backing.rs | 2 +- .../src/lib/mock/prospective_parachains.rs | 18 +++-- .../subsystem-bench/src/lib/statement/mod.rs | 54 ++++++--------- .../src/lib/statement/test_state.rs | 68 +++++++++++++++---- 5 files changed, 90 insertions(+), 53 deletions(-) diff --git a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml index 9fa7c6d0be37..dc61c4b75c44 100644 --- a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml +++ b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml @@ -2,3 +2,4 @@ TestConfiguration: - objective: StatementDistribution num_blocks: 10 connectivity: 100 + n_validators: 300 diff --git a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs index 27a965d75773..4c5820137e9f 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs @@ -95,7 +95,7 @@ impl MockCandidateBacking { let _ = self.to_subsystems.send(message).await; }, StatementWithPVD::Valid(candidate_hash) => { - let statement = Statement::Valid(candidate_hash.clone()); + let statement = Statement::Valid(*candidate_hash); let context = SigningContext { parent_hash: relay_parent, session_index: 0, diff --git a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs index 506b13732a37..2b3288205426 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs @@ -18,18 +18,21 @@ use futures::FutureExt; use polkadot_node_subsystem::{ - messages::ProspectiveParachainsMessage, overseer, SpawnedSubsystem, SubsystemError, + messages::{HypotheticalCandidate, ProspectiveParachainsMessage}, + overseer, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_types::OverseerSignal; use polkadot_primitives::Hash; const LOG_TARGET: &str = "subsystem-bench::prospective-parachains-mock"; -pub struct MockProspectiveParachains {} +pub struct MockProspectiveParachains { + candidates: Vec, +} impl MockProspectiveParachains { - pub fn new() -> Self { - Self {} + pub fn new(candidates: Vec) -> Self { + Self { candidates } } } @@ -59,10 +62,11 @@ impl MockProspectiveParachains { ProspectiveParachainsMessage::GetMinimumRelayParents(_relay_parent, tx) => { tx.send(vec![]).unwrap(); }, - ProspectiveParachainsMessage::GetHypotheticalFrontier(req, tx) => { + ProspectiveParachainsMessage::GetHypotheticalFrontier(_req, tx) => { tx.send( - req.candidates - .into_iter() + self.candidates + .iter() + .cloned() .map(|candidate| { (candidate, vec![(Hash::repeat_byte(0), vec![0])]) }) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 9f254e4f28c4..51725f0e039d 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -40,23 +40,19 @@ use polkadot_node_network_protocol::{ v3, view, Versioned, View, }; use polkadot_node_subsystem::messages::{ - network_bridge_event::NewGossipTopology, AllMessages, NetworkBridgeEvent, - StatementDistributionMessage, + network_bridge_event::NewGossipTopology, AllMessages, HypotheticalCandidate, + NetworkBridgeEvent, StatementDistributionMessage, }; use polkadot_overseer::{ Handle as OverseerHandle, Overseer, OverseerConnector, OverseerMetrics, SpawnGlue, }; -use polkadot_primitives::{ - AuthorityDiscoveryId, Block, CompactStatement, Hash, SignedStatement, SigningContext, - ValidatorId, ValidatorIndex, -}; +use polkadot_primitives::{AuthorityDiscoveryId, Block, Hash, ValidatorId, ValidatorIndex}; use polkadot_statement_distribution::StatementDistributionSubsystem; use rand::SeedableRng; use sc_keystore::LocalKeystore; use sc_network::request_responses::ProtocolConfig; use sc_network_types::PeerId; use sc_service::SpawnTaskHandle; -use sp_core::Pair; use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::RuntimeAppPublic; use std::{ @@ -103,7 +99,17 @@ fn build_overseer( ); let chain_api_state = ChainApiState { block_headers: state.block_headers.clone() }; let mock_chain_api = MockChainApi::new(chain_api_state); - let mock_prospective_parachains = MockProspectiveParachains::new(); + let mock_prospective_parachains = MockProspectiveParachains::new( + state + .commited_candidate_receipts + .values() + .map(|receipt| HypotheticalCandidate::Complete { + candidate_hash: receipt.hash(), + receipt: Arc::new(receipt.clone()), + persisted_validation_data: state.persisted_validation_data.clone(), + }) + .collect(), + ); let (tx, rx) = mpsc::unbounded(); let mock_candidate_backing = MockCandidateBacking::new( tx, @@ -244,10 +250,9 @@ pub async fn benchmark_statement_distribution( for message in initialization_messages { env.send_message(message).await; } - let pair = state.validator_pairs.get((NODE_UNDER_TEST + 1) as usize).unwrap(); let test_start = Instant::now(); - for block_info in state.block_infos.iter() { + for (index, block_info) in state.block_infos.iter().enumerate() { let block_num = block_info.number as usize; gum::info!(target: LOG_TARGET, "Current block {}/{} {}", block_num, config.num_blocks, block_info.hash); env.metrics().set_current_block(block_num); @@ -260,32 +265,15 @@ pub async fn benchmark_statement_distribution( env.send_message(update).await; } - let receipt = state - .commited_candidate_receipts - .get(&block_info.hash) - .expect("Pregenerated") - .clone(); - - let relay_parent = block_info.hash; - let candidate_hash = receipt.hash(); - let statement = CompactStatement::Seconded(candidate_hash); - let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; - let payload = statement.signing_payload(&context); - let signature = pair.sign(&payload[..]); - let statement = SignedStatement::new( - statement, - ValidatorIndex(NODE_UNDER_TEST + 1), - signature, - &context, - &pair.public(), - ) - .unwrap() - .as_unchecked() - .to_owned(); + let statement = state.seconded.get(index).unwrap().to_owned(); + let candidate_hash = *statement.unchecked_payload().candidate_hash(); let message = AllMessages::StatementDistribution( StatementDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( *state.test_authorities.peer_ids.get((NODE_UNDER_TEST + 1) as usize).unwrap(), - Versioned::V3(v3::StatementDistributionMessage::Statement(relay_parent, statement)), + Versioned::V3(v3::StatementDistributionMessage::Statement( + block_info.hash, + statement, + )), )), ); env.send_message(message).await; diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 8fe81bde5b99..9aefac097d6f 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -17,14 +17,19 @@ use crate::{ configuration::{TestAuthorities, TestConfiguration}, network::{HandleNetworkMessage, NetworkMessage}, + NODE_UNDER_TEST, }; +use bitvec::order::Lsb0; use colored::Colorize; use futures::SinkExt; use itertools::Itertools; use parity_scale_codec::Encode; use polkadot_node_network_protocol::{ request_response::{v2::AttestedCandidateResponse, Requests}, - v3::{BackedCandidateAcknowledgement, StatementDistributionMessage, ValidationProtocol}, + v3::{ + BackedCandidateAcknowledgement, StatementDistributionMessage, StatementFilter, + ValidationProtocol, + }, Versioned, }; use polkadot_node_primitives::{AvailableData, BlockData, PoV}; @@ -34,8 +39,8 @@ use polkadot_node_subsystem_test_helpers::{ use polkadot_overseer::BlockInfo; use polkadot_primitives::{ BlockNumber, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CompactStatement, - Hash, Header, PersistedValidationData, SignedStatement, SigningContext, ValidatorIndex, - ValidatorPair, + Hash, Header, PersistedValidationData, SignedStatement, SigningContext, UncheckedSigned, + ValidatorIndex, ValidatorPair, }; use polkadot_primitives_test_helpers::{ dummy_committed_candidate_receipt, dummy_hash, dummy_head_data, dummy_pvd, @@ -72,6 +77,8 @@ pub struct TestState { pub validator_pairs: Vec, // TODO pub statements_tracker: HashMap>>, + // TODO + pub seconded: Vec>, } impl TestState { @@ -86,6 +93,7 @@ impl TestState { block_headers: Default::default(), validator_pairs: Default::default(), statements_tracker: Default::default(), + seconded: Default::default(), }; // For each unique pov we create a candidate receipt. @@ -141,6 +149,16 @@ impl TestState { }) .collect::>(); + test_state.validator_pairs = test_state + .test_authorities + .key_seeds + .iter() + .map(|seed| ValidatorPair::from_string_with_seed(seed, None).unwrap().0) + .collect(); + + let pair = test_state.validator_pairs.get((NODE_UNDER_TEST + 1) as usize).unwrap(); + let validator_index = ValidatorIndex(NODE_UNDER_TEST + 1); + for (index, info) in test_state.block_infos.iter().enumerate() { let pov_size = pov_sizes.get(index % pov_sizes.len()).expect("This is a cycle; qed"); let candidate_index = @@ -152,7 +170,7 @@ impl TestState { let descriptor = receipt.descriptor.clone(); let commitments_hash = receipt.commitments.hash(); let candidate_hash = receipt.hash(); - test_state.commited_candidate_receipts.insert(info.hash, receipt); + test_state.commited_candidate_receipts.insert(info.hash, receipt.clone()); test_state .candidate_receipts .insert(info.hash, vec![CandidateReceipt { descriptor, commitments_hash }]); @@ -164,14 +182,25 @@ impl TestState { .map(|index| (index as u32, Arc::new(AtomicBool::new(index <= 1)))), ), ); - } - test_state.validator_pairs = test_state - .test_authorities - .key_seeds - .iter() - .map(|seed| ValidatorPair::from_string_with_seed(seed, None).unwrap().0) - .collect(); + let relay_parent = info.hash; + let candidate_hash = receipt.hash(); + let statement = CompactStatement::Seconded(candidate_hash); + let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; + let payload = statement.signing_payload(&context); + let signature = pair.sign(&payload[..]); + let statement = SignedStatement::new( + statement, + validator_index, + signature, + &context, + &pair.public(), + ) + .unwrap() + .as_unchecked() + .to_owned(); + test_state.seconded.push(statement); + } test_state } @@ -193,6 +222,12 @@ impl HandleNetworkMessage for TestState { .expect("Pregenerated") .clone(); let persisted_validation_data = self.persisted_validation_data.clone(); + let seconded = self + .seconded + .iter() + .find(|v| *v.unchecked_payload().candidate_hash() == payload.candidate_hash) + .unwrap() + .to_owned(); let res = AttestedCandidateResponse { candidate_receipt, @@ -215,6 +250,7 @@ impl HandleNetworkMessage for TestState { .position(|v| v == &authority_id) .expect("Should exist"); let candidate_hash = *statement.unchecked_payload().candidate_hash(); + gum::debug!("ValidatorIndex({}) received {:?}", index, statement); let sent = self .statements_tracker @@ -230,6 +266,10 @@ impl HandleNetworkMessage for TestState { sent.store(true, Ordering::SeqCst); } + if index > self.config.max_validators_per_core - 1 { + return None + } + let statement = CompactStatement::Valid(candidate_hash); let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; let payload = statement.signing_payload(&context); @@ -270,8 +310,12 @@ impl HandleNetworkMessage for TestState { .expect("Should exist"); let ack = BackedCandidateAcknowledgement { candidate_hash: manifest.candidate_hash, - statement_knowledge: manifest.statement_knowledge, + statement_knowledge: StatementFilter { + seconded_in_group: bitvec::bitvec![u8, Lsb0; 0,1,0,0,0], + validated_in_group: bitvec::bitvec![u8, Lsb0; 0,0,1,0,0], + }, }; + gum::debug!("ValidatorIndex({}) sends {:?}", index, ack); node_sender .start_send_unpin(NetworkMessage::MessageFromPeer( *self.test_authorities.peer_ids.get(index).expect("Must exist"), From 0ec77eb67cf433a6ed0c25b4d85ff224f7670e0a Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Wed, 8 May 2024 13:30:11 +0200 Subject: [PATCH 08/39] remove redundant --- .../subsystem-bench/src/lib/statement/test_state.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 9aefac097d6f..4064e679c24b 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -21,7 +21,6 @@ use crate::{ }; use bitvec::order::Lsb0; use colored::Colorize; -use futures::SinkExt; use itertools::Itertools; use parity_scale_codec::Encode; use polkadot_node_network_protocol::{ @@ -222,13 +221,6 @@ impl HandleNetworkMessage for TestState { .expect("Pregenerated") .clone(); let persisted_validation_data = self.persisted_validation_data.clone(); - let seconded = self - .seconded - .iter() - .find(|v| *v.unchecked_payload().candidate_hash() == payload.candidate_hash) - .unwrap() - .to_owned(); - let res = AttestedCandidateResponse { candidate_receipt, persisted_validation_data, @@ -287,7 +279,7 @@ impl HandleNetworkMessage for TestState { .to_owned(); node_sender - .start_send_unpin(NetworkMessage::MessageFromPeer( + .start_send(NetworkMessage::MessageFromPeer( *self.test_authorities.peer_ids.get(index).expect("Must exist"), Versioned::V3(ValidationProtocol::StatementDistribution( StatementDistributionMessage::Statement(relay_parent, statement), @@ -317,7 +309,7 @@ impl HandleNetworkMessage for TestState { }; gum::debug!("ValidatorIndex({}) sends {:?}", index, ack); node_sender - .start_send_unpin(NetworkMessage::MessageFromPeer( + .start_send(NetworkMessage::MessageFromPeer( *self.test_authorities.peer_ids.get(index).expect("Must exist"), Versioned::V3(ValidationProtocol::StatementDistribution( StatementDistributionMessage::BackedCandidateKnown(ack), From d4abb602d74a6bb8195a16f667f626551281cdfb Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Wed, 8 May 2024 15:07:07 +0200 Subject: [PATCH 09/39] Generate candidates for all cores --- .../src/lib/mock/prospective_parachains.rs | 15 +- .../subsystem-bench/src/lib/statement/mod.rs | 57 +++-- .../src/lib/statement/test_state.rs | 205 ++++++++---------- 3 files changed, 138 insertions(+), 139 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs index 2b3288205426..f67660cc7745 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs @@ -18,21 +18,18 @@ use futures::FutureExt; use polkadot_node_subsystem::{ - messages::{HypotheticalCandidate, ProspectiveParachainsMessage}, - overseer, SpawnedSubsystem, SubsystemError, + messages::ProspectiveParachainsMessage, overseer, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_types::OverseerSignal; use polkadot_primitives::Hash; const LOG_TARGET: &str = "subsystem-bench::prospective-parachains-mock"; -pub struct MockProspectiveParachains { - candidates: Vec, -} +pub struct MockProspectiveParachains {} impl MockProspectiveParachains { - pub fn new(candidates: Vec) -> Self { - Self { candidates } + pub fn new() -> Self { + Self {} } } @@ -62,9 +59,9 @@ impl MockProspectiveParachains { ProspectiveParachainsMessage::GetMinimumRelayParents(_relay_parent, tx) => { tx.send(vec![]).unwrap(); }, - ProspectiveParachainsMessage::GetHypotheticalFrontier(_req, tx) => { + ProspectiveParachainsMessage::GetHypotheticalFrontier(req, tx) => { tx.send( - self.candidates + req.candidates .iter() .cloned() .map(|candidate| { diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 51725f0e039d..61f722570ab8 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -40,19 +40,23 @@ use polkadot_node_network_protocol::{ v3, view, Versioned, View, }; use polkadot_node_subsystem::messages::{ - network_bridge_event::NewGossipTopology, AllMessages, HypotheticalCandidate, - NetworkBridgeEvent, StatementDistributionMessage, + network_bridge_event::NewGossipTopology, AllMessages, NetworkBridgeEvent, + StatementDistributionMessage, }; use polkadot_overseer::{ Handle as OverseerHandle, Overseer, OverseerConnector, OverseerMetrics, SpawnGlue, }; -use polkadot_primitives::{AuthorityDiscoveryId, Block, Hash, ValidatorId, ValidatorIndex}; +use polkadot_primitives::{ + AuthorityDiscoveryId, Block, CompactStatement, Hash, SignedStatement, SigningContext, + UncheckedSigned, ValidatorId, ValidatorIndex, ValidatorPair, +}; use polkadot_statement_distribution::StatementDistributionSubsystem; use rand::SeedableRng; use sc_keystore::LocalKeystore; use sc_network::request_responses::ProtocolConfig; use sc_network_types::PeerId; use sc_service::SpawnTaskHandle; +use sp_core::{Pair, H256}; use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::RuntimeAppPublic; use std::{ @@ -99,22 +103,12 @@ fn build_overseer( ); let chain_api_state = ChainApiState { block_headers: state.block_headers.clone() }; let mock_chain_api = MockChainApi::new(chain_api_state); - let mock_prospective_parachains = MockProspectiveParachains::new( - state - .commited_candidate_receipts - .values() - .map(|receipt| HypotheticalCandidate::Complete { - candidate_hash: receipt.hash(), - receipt: Arc::new(receipt.clone()), - persisted_validation_data: state.persisted_validation_data.clone(), - }) - .collect(), - ); + let mock_prospective_parachains = MockProspectiveParachains::new(); let (tx, rx) = mpsc::unbounded(); let mock_candidate_backing = MockCandidateBacking::new( tx, state.validator_pairs.get(NODE_UNDER_TEST as usize).unwrap().clone(), - state.persisted_validation_data.clone(), + state.pvd.clone(), ); let (statement_req_receiver, statement_req_cfg) = IncomingRequest::get_config_receiver::< Block, @@ -228,6 +222,27 @@ pub fn generate_topology(test_authorities: &TestAuthorities) -> SessionGridTopol SessionGridTopology::new(shuffled, topology) } +fn sign_statement( + statement: CompactStatement, + relay_parent: H256, + validator_index: ValidatorIndex, + pair: &ValidatorPair, +) -> UncheckedSigned { + let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; + let payload = statement.signing_payload(&context); + + SignedStatement::new( + statement, + validator_index, + pair.sign(&payload[..]), + &context, + &pair.public(), + ) + .unwrap() + .as_unchecked() + .to_owned() +} + pub async fn benchmark_statement_distribution( benchmark_name: &str, env: &mut TestEnvironment, @@ -252,7 +267,7 @@ pub async fn benchmark_statement_distribution( } let test_start = Instant::now(); - for (index, block_info) in state.block_infos.iter().enumerate() { + for block_info in state.block_infos.iter() { let block_num = block_info.number as usize; gum::info!(target: LOG_TARGET, "Current block {}/{} {}", block_num, config.num_blocks, block_info.hash); env.metrics().set_current_block(block_num); @@ -265,8 +280,14 @@ pub async fn benchmark_statement_distribution( env.send_message(update).await; } - let statement = state.seconded.get(index).unwrap().to_owned(); - let candidate_hash = *statement.unchecked_payload().candidate_hash(); + let candidate = state.candidate_receipts.get(&block_info.hash).unwrap().first().unwrap(); + let candidate_hash = candidate.hash(); + let statement = sign_statement( + CompactStatement::Seconded(candidate.hash()), + block_info.hash, + ValidatorIndex(1), + state.validator_pairs.get(1).unwrap(), + ); let message = AllMessages::StatementDistribution( StatementDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( *state.test_authorities.peer_ids.get((NODE_UNDER_TEST + 1) as usize).unwrap(), diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 4064e679c24b..171f602ccad8 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -17,10 +17,8 @@ use crate::{ configuration::{TestAuthorities, TestConfiguration}, network::{HandleNetworkMessage, NetworkMessage}, - NODE_UNDER_TEST, }; use bitvec::order::Lsb0; -use colored::Colorize; use itertools::Itertools; use parity_scale_codec::Encode; use polkadot_node_network_protocol::{ @@ -38,8 +36,8 @@ use polkadot_node_subsystem_test_helpers::{ use polkadot_overseer::BlockInfo; use polkadot_primitives::{ BlockNumber, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CompactStatement, - Hash, Header, PersistedValidationData, SignedStatement, SigningContext, UncheckedSigned, - ValidatorIndex, ValidatorPair, + Hash, Header, PersistedValidationData, SignedStatement, SigningContext, ValidatorIndex, + ValidatorPair, }; use polkadot_primitives_test_helpers::{ dummy_committed_candidate_receipt, dummy_hash, dummy_head_data, dummy_pvd, @@ -54,8 +52,6 @@ use std::{ }, }; -const LOG_TARGET: &str = "subsystem-bench::statement::test_state"; - #[derive(Clone)] pub struct TestState { // Full test config @@ -67,142 +63,126 @@ pub struct TestState { // Map from generated candidate receipts pub candidate_receipts: HashMap>, // Map from generated commited candidate receipts - pub commited_candidate_receipts: HashMap, + pub commited_candidate_receipts: HashMap>, // TODO - pub persisted_validation_data: PersistedValidationData, + pub pvd: PersistedValidationData, // Relay chain block headers pub block_headers: HashMap, // TODO pub validator_pairs: Vec, // TODO pub statements_tracker: HashMap>>, - // TODO - pub seconded: Vec>, } impl TestState { pub fn new(config: &TestConfiguration) -> Self { - let mut test_state = Self { + let mut state = Self { config: config.clone(), test_authorities: config.generate_authorities(), - block_infos: Default::default(), + block_infos: (1..=config.num_blocks).map(generate_block_info).collect(), candidate_receipts: Default::default(), commited_candidate_receipts: Default::default(), - persisted_validation_data: dummy_pvd(dummy_head_data(), 0), + pvd: dummy_pvd(dummy_head_data(), 0), block_headers: Default::default(), validator_pairs: Default::default(), statements_tracker: Default::default(), - seconded: Default::default(), }; - // For each unique pov we create a candidate receipt. - let pov_sizes = Vec::from(config.pov_sizes()); - let mut commited_candidate_receipt_templates: Vec = - Default::default(); - let mut pov_size_to_candidate: HashMap = Default::default(); - for (index, pov_size) in pov_sizes.iter().cloned().unique().enumerate() { - gum::info!(target: LOG_TARGET, index, pov_size, "{}", "Generating template candidate".bright_blue()); - - let mut commited_candidate_receipt = dummy_committed_candidate_receipt(dummy_hash()); - let pov = PoV { block_data: BlockData(vec![index as u8; pov_size]) }; - - let new_available_data = AvailableData { - validation_data: test_state.persisted_validation_data.clone(), - pov: Arc::new(pov), - }; - - let (_, erasure_root) = derive_erasure_chunks_with_proofs_and_root( - config.n_validators, - &new_available_data, - |_, _| {}, - ); + state.block_headers = state.block_infos.iter().map(generate_block_header).collect(); + state.validator_pairs = + state.test_authorities.key_seeds.iter().map(generate_pair).collect(); - commited_candidate_receipt.descriptor.erasure_root = erasure_root; - commited_candidate_receipt.descriptor.persisted_validation_data_hash = - test_state.persisted_validation_data.hash(); - commited_candidate_receipt_templates.push(commited_candidate_receipt); - pov_size_to_candidate.insert(pov_size, index); - } + // For each unique pov we create a candidate receipt. + let pov_sizes = Vec::from(config.pov_sizes()); // For n_cores + let pov_size_to_candidate = generate_pov_size_to_candidate(&pov_sizes); + let receipt_templates = + generate_receipt_templates(&pov_size_to_candidate, config.n_validators, &state.pvd); - test_state.block_infos = (1..=config.num_blocks) - .map(|block_num| { - let relay_block_hash = Hash::repeat_byte(block_num as u8); - new_block_import_info(relay_block_hash, block_num as BlockNumber) - }) - .collect(); + for block_info in state.block_infos.iter() { + for core_idx in 0..config.n_cores { + let pov_size = pov_sizes.get(core_idx).expect("This is a cycle; qed"); + let candidate_index = + *pov_size_to_candidate.get(pov_size).expect("pov_size always exists; qed"); + let mut receipt = receipt_templates[candidate_index].clone(); + receipt.descriptor.relay_parent = block_info.hash; - test_state.block_headers = test_state - .block_infos - .iter() - .map(|info| { - ( - info.hash, - Header { - digest: Default::default(), - number: info.number, - parent_hash: info.parent_hash, - extrinsics_root: Default::default(), - state_root: Default::default(), + state.candidate_receipts.entry(block_info.hash).or_default().push( + CandidateReceipt { + descriptor: receipt.descriptor.clone(), + commitments_hash: receipt.commitments.hash(), }, - ) - }) - .collect::>(); + ); + state.statements_tracker.entry(receipt.hash()).or_default().extend( + (0..config.n_validators) + .map(|index| (index as u32, Arc::new(AtomicBool::new(index <= 1)))) + .collect::>(), + ); + state + .commited_candidate_receipts + .entry(block_info.hash) + .or_default() + .push(receipt); + } + } - test_state.validator_pairs = test_state - .test_authorities - .key_seeds - .iter() - .map(|seed| ValidatorPair::from_string_with_seed(seed, None).unwrap().0) - .collect(); + state + } +} - let pair = test_state.validator_pairs.get((NODE_UNDER_TEST + 1) as usize).unwrap(); - let validator_index = ValidatorIndex(NODE_UNDER_TEST + 1); +fn generate_block_info(block_num: usize) -> BlockInfo { + new_block_import_info(Hash::repeat_byte(block_num as u8), block_num as BlockNumber) +} - for (index, info) in test_state.block_infos.iter().enumerate() { - let pov_size = pov_sizes.get(index % pov_sizes.len()).expect("This is a cycle; qed"); - let candidate_index = - *pov_size_to_candidate.get(pov_size).expect("pov_size always exists; qed"); - let mut receipt = commited_candidate_receipt_templates[candidate_index].clone(); - receipt.descriptor.relay_parent = info.hash; - gum::debug!(target: LOG_TARGET, candidate_hash = ?receipt.hash(), "new candidate"); +fn generate_block_header(info: &BlockInfo) -> (H256, Header) { + ( + info.hash, + Header { + digest: Default::default(), + number: info.number, + parent_hash: info.parent_hash, + extrinsics_root: Default::default(), + state_root: Default::default(), + }, + ) +} - let descriptor = receipt.descriptor.clone(); - let commitments_hash = receipt.commitments.hash(); - let candidate_hash = receipt.hash(); - test_state.commited_candidate_receipts.insert(info.hash, receipt.clone()); - test_state - .candidate_receipts - .insert(info.hash, vec![CandidateReceipt { descriptor, commitments_hash }]); +fn generate_pov_size_to_candidate(pov_sizes: &[usize]) -> HashMap { + pov_sizes + .iter() + .cloned() + .unique() + .enumerate() + .map(|(index, pov_size)| (pov_size, index)) + .collect() +} - test_state.statements_tracker.insert( - candidate_hash, - HashMap::from_iter( - (0..config.n_validators) - .map(|index| (index as u32, Arc::new(AtomicBool::new(index <= 1)))), - ), +fn generate_receipt_templates( + pov_size_to_candidate: &HashMap, + n_validators: usize, + pvd: &PersistedValidationData, +) -> Vec { + pov_size_to_candidate + .iter() + .map(|(&pov_size, &index)| { + let mut receipt = dummy_committed_candidate_receipt(dummy_hash()); + let (_, erasure_root) = derive_erasure_chunks_with_proofs_and_root( + n_validators, + &AvailableData { + validation_data: pvd.clone(), + pov: Arc::new(PoV { block_data: BlockData(vec![index as u8; pov_size]) }), + }, + |_, _| {}, ); + receipt.descriptor.persisted_validation_data_hash = pvd.hash(); + receipt.descriptor.erasure_root = erasure_root; + receipt + }) + .collect() +} - let relay_parent = info.hash; - let candidate_hash = receipt.hash(); - let statement = CompactStatement::Seconded(candidate_hash); - let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; - let payload = statement.signing_payload(&context); - let signature = pair.sign(&payload[..]); - let statement = SignedStatement::new( - statement, - validator_index, - signature, - &context, - &pair.public(), - ) - .unwrap() - .as_unchecked() - .to_owned(); - test_state.seconded.push(statement); - } - - test_state - } +#[allow(clippy::ptr_arg)] +fn generate_pair(seed: &String) -> ValidatorPair { + ValidatorPair::from_string_with_seed(seed, None).unwrap().0 } impl HandleNetworkMessage for TestState { @@ -217,10 +197,11 @@ impl HandleNetworkMessage for TestState { let candidate_receipt = self .commited_candidate_receipts .values() + .flatten() .find(|v| v.hash() == payload.candidate_hash) .expect("Pregenerated") .clone(); - let persisted_validation_data = self.persisted_validation_data.clone(); + let persisted_validation_data = self.pvd.clone(); let res = AttestedCandidateResponse { candidate_receipt, persisted_validation_data, From 99890cd8a659b63474c3fe226a11691bc55412a1 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Wed, 8 May 2024 15:53:30 +0200 Subject: [PATCH 10/39] Don't rely on full connectivity --- .../subsystem-bench/src/lib/statement/mod.rs | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 61f722570ab8..78cf242725e1 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -23,7 +23,7 @@ use crate::{ chain_api::{ChainApiState, MockChainApi}, network_bridge::{MockNetworkBridgeRx, MockNetworkBridgeTx}, prospective_parachains::MockProspectiveParachains, - runtime_api::{MockRuntimeApi, MockRuntimeApiCoreState}, + runtime_api::{session_info_for_peers, MockRuntimeApi, MockRuntimeApiCoreState}, AlwaysSupportsParachains, }, network::new_network, @@ -250,6 +250,12 @@ pub async fn benchmark_statement_distribution( mut to_subsystems: mpsc::UnboundedReceiver, ) -> BenchmarkUsage { let config = env.config().clone(); + let session_info = session_info_for_peers(&config, &state.test_authorities); + let groups = session_info.validator_groups; + let node_group = groups + .iter() + .find(|group| group.iter().any(|v| v.0 == NODE_UNDER_TEST)) + .expect("Pregenerated"); env.metrics().set_n_validators(config.n_validators); env.metrics().set_n_cores(config.n_cores); @@ -280,17 +286,28 @@ pub async fn benchmark_statement_distribution( env.send_message(update).await; } + let seconding = node_group + .iter() + .find(|v| { + let authority_id = + state.test_authorities.validator_authority_id.get(v.0 as usize).unwrap(); + v.0 != NODE_UNDER_TEST && env.network().is_peer_connected(authority_id) + }) + .expect("Validator group should contain enough connected validators") + .to_owned(); + let seconding_idx = seconding.0 as usize; + let seconding_peer_id = *state.test_authorities.peer_ids.get(seconding_idx).unwrap(); let candidate = state.candidate_receipts.get(&block_info.hash).unwrap().first().unwrap(); let candidate_hash = candidate.hash(); let statement = sign_statement( CompactStatement::Seconded(candidate.hash()), block_info.hash, - ValidatorIndex(1), - state.validator_pairs.get(1).unwrap(), + seconding, + state.validator_pairs.get(seconding_idx).unwrap(), ); let message = AllMessages::StatementDistribution( StatementDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( - *state.test_authorities.peer_ids.get((NODE_UNDER_TEST + 1) as usize).unwrap(), + seconding_peer_id, Versioned::V3(v3::StatementDistributionMessage::Statement( block_info.hash, statement, From 6cf9f78c8b836948f4c013998009007b59247860 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Mon, 13 May 2024 15:01:41 +0200 Subject: [PATCH 11/39] Receive manifests from neighbors --- .../examples/statement_distribution.yaml | 2 +- .../src/cli/subsystem-bench.rs | 2 +- .../src/lib/mock/candidate_backing.rs | 120 ++++--- .../src/lib/mock/prospective_parachains.rs | 40 +-- .../subsystem-bench/src/lib/statement/mod.rs | 302 ++++++++++++++---- .../src/lib/statement/test_state.rs | 124 ++++++- 6 files changed, 440 insertions(+), 150 deletions(-) diff --git a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml index dc61c4b75c44..8dc0b46ebb4d 100644 --- a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml +++ b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml @@ -1,5 +1,5 @@ TestConfiguration: - objective: StatementDistribution - num_blocks: 10 + num_blocks: 1 connectivity: 100 n_validators: 300 diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index 632f9b72a2ff..cf0cd6a3f9d6 100644 --- a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -203,7 +203,7 @@ fn main() -> eyre::Result<()> { .filter(Some("hyper"), log::LevelFilter::Info) // Avoid `Terminating due to subsystem exit subsystem` warnings .filter(Some("polkadot_overseer"), log::LevelFilter::Error) - .filter(None, log::LevelFilter::Info) + .filter(None, log::LevelFilter::Debug) .format_timestamp_millis() .try_init() .unwrap(); diff --git a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs index 4c5820137e9f..ee0c8ff4d066 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs @@ -16,6 +16,7 @@ //! A generic candidate backing subsystem mockup suitable to be used in benchmarks. +use crate::NODE_UNDER_TEST; use futures::{channel::mpsc::UnboundedSender, FutureExt, SinkExt}; use overseer::AllMessages; use polkadot_node_primitives::{SignedFullStatementWithPVD, Statement, StatementWithPVD}; @@ -23,9 +24,11 @@ use polkadot_node_subsystem::{ messages::CandidateBackingMessage, overseer, SpawnedSubsystem, SubsystemError, }; use polkadot_node_subsystem_types::OverseerSignal; -use polkadot_primitives::{PersistedValidationData, SigningContext, ValidatorIndex, ValidatorPair}; +use polkadot_primitives::{ + CandidateHash, PersistedValidationData, SigningContext, ValidatorIndex, ValidatorPair, +}; use sp_core::Pair; -use crate::NODE_UNDER_TEST; +use std::collections::HashMap; const LOG_TARGET: &str = "subsystem-bench::candidate-backing-mock"; @@ -33,6 +36,7 @@ pub struct MockCandidateBacking { to_subsystems: UnboundedSender, pair: ValidatorPair, pvd: PersistedValidationData, + node_group: Vec, } impl MockCandidateBacking { @@ -40,8 +44,9 @@ impl MockCandidateBacking { to_subsystems: UnboundedSender, pair: ValidatorPair, pvd: PersistedValidationData, + node_group: Vec, ) -> Self { - Self { to_subsystems, pair, pvd } + Self { to_subsystems, pair, pvd, node_group } } } @@ -57,6 +62,8 @@ impl MockCandidateBacking { #[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] impl MockCandidateBacking { async fn run(mut self, mut ctx: Context) { + let mut tracker: HashMap = Default::default(); + loop { let msg = ctx.recv().await.expect("Overseer never fails us"); match msg { @@ -68,58 +75,75 @@ impl MockCandidateBacking { gum::trace!(target: LOG_TARGET, msg=?msg, "recv message"); match msg { - CandidateBackingMessage::Statement(relay_parent, statement) => + // If a statement is not from a node group - ignore it. + // For the first seconded send Share + // For the seconde statement send Backed + CandidateBackingMessage::Statement(relay_parent, statement) => { + let validator_id = statement.validator_index(); + let is_from_node_group = self.node_group.contains(&validator_id); match statement.payload() { StatementWithPVD::Seconded(receipt, _pvd) => { let candidate_hash = receipt.hash(); - let statement = Statement::Valid(candidate_hash); - let context = SigningContext { - parent_hash: relay_parent, - session_index: 0, - }; - let payload = statement.to_compact().signing_payload(&context); - let signature = self.pair.sign(&payload[..]); - let message = AllMessages::StatementDistribution( - polkadot_node_subsystem::messages::StatementDistributionMessage::Share( - relay_parent, - SignedFullStatementWithPVD::new( - statement.supply_pvd(self.pvd.clone()), - ValidatorIndex(NODE_UNDER_TEST), - signature, - &context, - &self.pair.public(), - ) - .unwrap(), - ), - ); - let _ = self.to_subsystems.send(message).await; + tracker + .entry(candidate_hash) + .and_modify(|v| { + *v += 1; + }) + .or_insert(1); + + let received = *tracker.get(&candidate_hash).unwrap(); + if received == 1 && is_from_node_group { + let statement = Statement::Valid(candidate_hash); + let context = SigningContext { + parent_hash: relay_parent, + session_index: 0, + }; + let payload = + statement.to_compact().signing_payload(&context); + let signature = self.pair.sign(&payload[..]); + let message = AllMessages::StatementDistribution( + polkadot_node_subsystem::messages::StatementDistributionMessage::Share( + relay_parent, + SignedFullStatementWithPVD::new( + statement.supply_pvd(self.pvd.clone()), + ValidatorIndex(NODE_UNDER_TEST), + signature, + &context, + &self.pair.public(), + ) + .unwrap(), + ) + ); + let _ = self.to_subsystems.send(message).await; + } + + if received == 2 { + let message = AllMessages::StatementDistribution( + polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(candidate_hash), + ); + let _ = self.to_subsystems.send(message).await; + } }, StatementWithPVD::Valid(candidate_hash) => { - let statement = Statement::Valid(*candidate_hash); - let context = SigningContext { - parent_hash: relay_parent, - session_index: 0, - }; - let payload = statement.to_compact().signing_payload(&context); - let signature = self.pair.sign(&payload[..]); - let message = AllMessages::StatementDistribution( - polkadot_node_subsystem::messages::StatementDistributionMessage::Share( - relay_parent, - SignedFullStatementWithPVD::new( - statement.supply_pvd(self.pvd.clone()), - ValidatorIndex(NODE_UNDER_TEST), - signature, - &context, - &self.pair.public(), - ) - .unwrap(), - ), - ); - let _ = self.to_subsystems.send(message).await; + tracker + .entry(*candidate_hash) + .and_modify(|v| { + *v += 1; + }) + .or_insert(1); + + let received = *tracker.get(candidate_hash).unwrap(); + if received == 2 { + let message = AllMessages::StatementDistribution( + polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(*candidate_hash), + ); + let _ = self.to_subsystems.send(message).await; + } }, - }, + } + }, _ => { - unimplemented!("Unexpected chain-api message") + unimplemented!("Unexpected candidate-backing message") }, } }, diff --git a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs index f67660cc7745..ea6553062232 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs @@ -52,29 +52,23 @@ impl MockProspectiveParachains { if signal == OverseerSignal::Conclude { return }, - orchestra::FromOrchestra::Communication { msg } => { - gum::debug!(target: LOG_TARGET, msg=?msg, "recv message"); - - match msg { - ProspectiveParachainsMessage::GetMinimumRelayParents(_relay_parent, tx) => { - tx.send(vec![]).unwrap(); - }, - ProspectiveParachainsMessage::GetHypotheticalFrontier(req, tx) => { - tx.send( - req.candidates - .iter() - .cloned() - .map(|candidate| { - (candidate, vec![(Hash::repeat_byte(0), vec![0])]) - }) - .collect(), - ) - .unwrap(); - }, - _ => { - unimplemented!("Unexpected chain-api message") - }, - } + orchestra::FromOrchestra::Communication { msg } => match msg { + ProspectiveParachainsMessage::GetMinimumRelayParents(_relay_parent, tx) => { + tx.send(vec![]).unwrap(); + }, + ProspectiveParachainsMessage::GetHypotheticalFrontier(req, tx) => { + tx.send( + req.candidates + .iter() + .cloned() + .map(|candidate| (candidate, vec![(Hash::repeat_byte(0), vec![0])])) + .collect(), + ) + .unwrap(); + }, + _ => { + unimplemented!("Unexpected chain-api message") + }, }, } } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 78cf242725e1..42612096ccac 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -23,21 +23,23 @@ use crate::{ chain_api::{ChainApiState, MockChainApi}, network_bridge::{MockNetworkBridgeRx, MockNetworkBridgeTx}, prospective_parachains::MockProspectiveParachains, - runtime_api::{session_info_for_peers, MockRuntimeApi, MockRuntimeApiCoreState}, + runtime_api::{MockRuntimeApi, MockRuntimeApiCoreState}, AlwaysSupportsParachains, }, network::new_network, usage::BenchmarkUsage, NODE_UNDER_TEST, }; +use bitvec::{order::Lsb0, vec::BitVec}; use colored::Colorize; -use futures::{channel::mpsc, StreamExt}; +use futures::{channel::mpsc, FutureExt, StreamExt}; use itertools::Itertools; use polkadot_node_metrics::metrics::Metrics; use polkadot_node_network_protocol::{ grid_topology::{SessionGridTopology, TopologyPeerInfo}, request_response::{IncomingRequest, ReqProtocolNames}, - v3, view, Versioned, View, + v3::{self, BackedCandidateManifest, StatementFilter}, + view, Versioned, View, }; use polkadot_node_subsystem::messages::{ network_bridge_event::NewGossipTopology, AllMessages, NetworkBridgeEvent, @@ -47,8 +49,7 @@ use polkadot_overseer::{ Handle as OverseerHandle, Overseer, OverseerConnector, OverseerMetrics, SpawnGlue, }; use polkadot_primitives::{ - AuthorityDiscoveryId, Block, CompactStatement, Hash, SignedStatement, SigningContext, - UncheckedSigned, ValidatorId, ValidatorIndex, ValidatorPair, + AuthorityDiscoveryId, Block, GroupIndex, Hash, Id, ValidatorId, ValidatorIndex, }; use polkadot_statement_distribution::StatementDistributionSubsystem; use rand::SeedableRng; @@ -56,7 +57,6 @@ use sc_keystore::LocalKeystore; use sc_network::request_responses::ProtocolConfig; use sc_network_types::PeerId; use sc_service::SpawnTaskHandle; -use sp_core::{Pair, H256}; use sp_keystore::{Keystore, KeystorePtr}; use sp_runtime::RuntimeAppPublic; use std::{ @@ -64,7 +64,6 @@ use std::{ time::{Duration, Instant}, }; pub use test_state::TestState; -use tokio::time::sleep; mod test_state; @@ -109,6 +108,7 @@ fn build_overseer( tx, state.validator_pairs.get(NODE_UNDER_TEST as usize).unwrap().clone(), state.pvd.clone(), + state.node_group.clone(), ); let (statement_req_receiver, statement_req_cfg) = IncomingRequest::get_config_receiver::< Block, @@ -183,14 +183,12 @@ pub fn generate_peer_view_change(block_hash: Hash, peer_id: PeerId) -> AllMessag } pub fn generate_new_session_topology( - test_authorities: &TestAuthorities, + topology: &SessionGridTopology, test_node: ValidatorIndex, ) -> Vec { - let topology = generate_topology(test_authorities); - let event = NetworkBridgeEvent::NewGossipTopology(NewGossipTopology { session: 0, - topology, + topology: topology.clone(), local_index: Some(test_node), }); vec![AllMessages::StatementDistribution(StatementDistributionMessage::NetworkBridgeUpdate( @@ -222,39 +220,26 @@ pub fn generate_topology(test_authorities: &TestAuthorities) -> SessionGridTopol SessionGridTopology::new(shuffled, topology) } -fn sign_statement( - statement: CompactStatement, - relay_parent: H256, - validator_index: ValidatorIndex, - pair: &ValidatorPair, -) -> UncheckedSigned { - let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; - let payload = statement.signing_payload(&context); - - SignedStatement::new( - statement, - validator_index, - pair.sign(&payload[..]), - &context, - &pair.public(), - ) - .unwrap() - .as_unchecked() - .to_owned() -} - pub async fn benchmark_statement_distribution( benchmark_name: &str, env: &mut TestEnvironment, state: &TestState, mut to_subsystems: mpsc::UnboundedReceiver, ) -> BenchmarkUsage { + let mut state = state.clone(); + state.connected_validators = state + .test_authorities + .validator_authority_id + .iter() + .enumerate() + .filter_map(|(i, id)| if env.network().is_peer_connected(id) { Some(i) } else { None }) + .collect_vec(); + let config = env.config().clone(); - let session_info = session_info_for_peers(&config, &state.test_authorities); - let groups = session_info.validator_groups; - let node_group = groups + let groups = state.session_info.validator_groups.clone(); + let (node_group_index, node_group) = groups .iter() - .find(|group| group.iter().any(|v| v.0 == NODE_UNDER_TEST)) + .find_position(|group| group.iter().any(|v| v.0 == NODE_UNDER_TEST)) .expect("Pregenerated"); env.metrics().set_n_validators(config.n_validators); @@ -262,12 +247,11 @@ pub async fn benchmark_statement_distribution( // First create the initialization messages that make sure that then node under // tests receives notifications about the topology used and the connected peers. + let topology = generate_topology(&state.test_authorities); let mut initialization_messages = env.network().generate_statement_distribution_peer_connected(); - initialization_messages.extend(generate_new_session_topology( - &state.test_authorities, - ValidatorIndex(NODE_UNDER_TEST), - )); + initialization_messages + .extend(generate_new_session_topology(&topology, ValidatorIndex(NODE_UNDER_TEST))); for message in initialization_messages { env.send_message(message).await; } @@ -289,9 +273,7 @@ pub async fn benchmark_statement_distribution( let seconding = node_group .iter() .find(|v| { - let authority_id = - state.test_authorities.validator_authority_id.get(v.0 as usize).unwrap(); - v.0 != NODE_UNDER_TEST && env.network().is_peer_connected(authority_id) + v.0 != NODE_UNDER_TEST && state.connected_validators.contains(&(v.0 as usize)) }) .expect("Validator group should contain enough connected validators") .to_owned(); @@ -299,12 +281,13 @@ pub async fn benchmark_statement_distribution( let seconding_peer_id = *state.test_authorities.peer_ids.get(seconding_idx).unwrap(); let candidate = state.candidate_receipts.get(&block_info.hash).unwrap().first().unwrap(); let candidate_hash = candidate.hash(); - let statement = sign_statement( - CompactStatement::Seconded(candidate.hash()), - block_info.hash, - seconding, - state.validator_pairs.get(seconding_idx).unwrap(), - ); + let statement = state + .statements + .get(&candidate_hash) + .unwrap() + .get(seconding_idx) + .unwrap() + .clone(); let message = AllMessages::StatementDistribution( StatementDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( seconding_peer_id, @@ -316,30 +299,211 @@ pub async fn benchmark_statement_distribution( ); env.send_message(message).await; - if let Some(msg) = to_subsystems.next().await { - gum::info!(msg = ?msg); - env.send_message(msg).await; + let neighbors = + topology.compute_grid_neighbors_for(ValidatorIndex(NODE_UNDER_TEST)).unwrap(); + + let connected_neighbors_x = neighbors + .validator_indices_x + .iter() + .filter(|&v| state.connected_validators.contains(&(v.0 as usize))) + .cloned() + .collect_vec(); + + let connected_neighbors_y = neighbors + .validator_indices_y + .iter() + .filter(|&v| state.connected_validators.contains(&(v.0 as usize))) + .cloned() + .collect_vec(); + + let mut message_tracker = (0..groups.len()).map(|i| i == node_group_index).collect_vec(); + + let one_hop_validators = connected_neighbors_x + .iter() + .chain(connected_neighbors_y.iter()) + .filter(|v| !node_group.contains(v)); + + // 1-hop messages + for validator_index in one_hop_validators { + let group_index = + groups.iter().position(|group| group.contains(validator_index)).unwrap(); + let sent = message_tracker.get_mut(group_index).unwrap(); + if *sent { + continue + } + + *sent = true; + let seconding_index = validator_index.0 as usize; + let seconding_peer_id = *state.test_authorities.peer_ids.get(seconding_index).unwrap(); + let candidate_hash = state + .candidate_receipts + .get(&block_info.hash) + .unwrap() + .get(group_index) + .unwrap() + .hash(); + let manifest = BackedCandidateManifest { + relay_parent: block_info.hash, + candidate_hash, + group_index: GroupIndex(group_index as u32), + para_id: Id::new(group_index as u32 + 1), + parent_head_data_hash: state.pvd.parent_head.hash(), + statement_knowledge: StatementFilter { + seconded_in_group: BitVec::from_iter( + groups + .get(GroupIndex(group_index as u32)) + .unwrap() + .iter() + .map(|v| state.connected_validators.contains(&(v.0 as usize))), + ), + validated_in_group: bitvec::bitvec![u8, Lsb0; 0, 0, 0, 0, 0], + }, + }; + let message = AllMessages::StatementDistribution( + StatementDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( + seconding_peer_id, + Versioned::V3(v3::StatementDistributionMessage::BackedCandidateManifest( + manifest, + )), + )), + ); + env.send_message(message).await; } - let message = AllMessages::StatementDistribution(StatementDistributionMessage::Backed( - candidate_hash, - )); - env.send_message(message).await; + // 2-hop mssages X + for validator_index in connected_neighbors_x.iter().filter(|v| !node_group.contains(v)) { + let validator_neighbor = topology + .compute_grid_neighbors_for(*validator_index) + .unwrap() + .validator_indices_y + .into_iter() + .next() + .unwrap(); + let group_index = + groups.iter().position(|group| group.contains(&validator_neighbor)).unwrap(); + + let sent = message_tracker.get_mut(group_index).unwrap(); + if *sent { + continue + } + *sent = true; + let seconding_index = validator_index.0 as usize; + let seconding_peer_id = *state.test_authorities.peer_ids.get(seconding_index).unwrap(); + let candidate_hash = state + .candidate_receipts + .get(&block_info.hash) + .unwrap() + .get(group_index) + .unwrap() + .hash(); + let manifest = BackedCandidateManifest { + relay_parent: block_info.hash, + candidate_hash, + group_index: GroupIndex(group_index as u32), + para_id: Id::new(group_index as u32 + 1), + parent_head_data_hash: state.pvd.parent_head.hash(), + statement_knowledge: StatementFilter { + seconded_in_group: BitVec::from_iter( + groups + .get(GroupIndex(group_index as u32)) + .unwrap() + .iter() + .map(|v| state.connected_validators.contains(&(v.0 as usize))), + ), + validated_in_group: bitvec::bitvec![u8, Lsb0; 0, 0, 0, 0, 0], + }, + }; + let message = AllMessages::StatementDistribution( + StatementDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( + seconding_peer_id, + Versioned::V3(v3::StatementDistributionMessage::BackedCandidateManifest( + manifest, + )), + )), + ); + env.send_message(message).await; + } + + // 2-hop mssages Y + for validator_index in connected_neighbors_y.iter().filter(|v| !node_group.contains(v)) { + let validator_neighbor = topology + .compute_grid_neighbors_for(*validator_index) + .unwrap() + .validator_indices_x + .into_iter() + .next() + .unwrap(); + let group_index = + groups.iter().position(|group| group.contains(&validator_neighbor)).unwrap(); + + let sent = message_tracker.get_mut(group_index).unwrap(); + if *sent { + continue + } + *sent = true; + let seconding_index = validator_index.0 as usize; + let seconding_peer_id = *state.test_authorities.peer_ids.get(seconding_index).unwrap(); + let candidate_hash = state + .candidate_receipts + .get(&block_info.hash) + .unwrap() + .get(group_index) + .unwrap() + .hash(); + let manifest = BackedCandidateManifest { + relay_parent: block_info.hash, + candidate_hash, + group_index: GroupIndex(group_index as u32), + para_id: Id::new(group_index as u32 + 1), + parent_head_data_hash: state.pvd.parent_head.hash(), + statement_knowledge: StatementFilter { + seconded_in_group: BitVec::from_iter( + groups + .get(GroupIndex(group_index as u32)) + .unwrap() + .iter() + .map(|v| state.connected_validators.contains(&(v.0 as usize))), + ), + validated_in_group: bitvec::bitvec![u8, Lsb0; 0, 0, 0, 0, 0], + }, + }; + let message = AllMessages::StatementDistribution( + StatementDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( + seconding_peer_id, + Versioned::V3(v3::StatementDistributionMessage::BackedCandidateManifest( + manifest, + )), + )), + ); + env.send_message(message).await; + } + + let mut timeout = futures_timer::Delay::new(Duration::from_millis(100)).fuse(); loop { - let count = state - .statements_tracker - .get(&candidate_hash) - .expect("Pregenerated") - .values() - .filter(|v| v.load(Ordering::SeqCst)) - .collect::>() - .len(); - gum::info!(target: LOG_TARGET, count = ?count); - if count < 100 { - sleep(Duration::from_millis(1000)).await; - } else { - break; + futures::select! { + msg = to_subsystems.next() => { + if let Some(msg) = msg { + gum::info!(msg = ?msg); + env.send_message(msg).await; + } + }, + _ = timeout => { + let manifest_count = state + .manifests_tracker + .values() + .filter(|v| v.load(Ordering::SeqCst)) + .collect::>() + .len(); + let message_count = message_tracker.iter().filter(|&&v| v).collect_vec().len(); + let all_groups = message_tracker.len(); + gum::info!(target: LOG_TARGET, ?message_count, ?manifest_count, ?all_groups); + if manifest_count < config.n_cores { + timeout = futures_timer::Delay::new(Duration::from_millis(100)).fuse(); + } else { + break; + } + } } } } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 171f602ccad8..d43d88241735 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -16,7 +16,9 @@ use crate::{ configuration::{TestAuthorities, TestConfiguration}, + mock::runtime_api::session_info_for_peers, network::{HandleNetworkMessage, NetworkMessage}, + NODE_UNDER_TEST, }; use bitvec::order::Lsb0; use itertools::Itertools; @@ -36,8 +38,8 @@ use polkadot_node_subsystem_test_helpers::{ use polkadot_overseer::BlockInfo; use polkadot_primitives::{ BlockNumber, CandidateHash, CandidateReceipt, CommittedCandidateReceipt, CompactStatement, - Hash, Header, PersistedValidationData, SignedStatement, SigningContext, ValidatorIndex, - ValidatorPair, + Hash, Header, Id, PersistedValidationData, SessionInfo, SignedStatement, SigningContext, + UncheckedSigned, ValidatorIndex, ValidatorPair, }; use polkadot_primitives_test_helpers::{ dummy_committed_candidate_receipt, dummy_hash, dummy_head_data, dummy_pvd, @@ -52,6 +54,8 @@ use std::{ }, }; +const LOG_TARGET: &str = "subsystem-bench::test-state"; + #[derive(Clone)] pub struct TestState { // Full test config @@ -72,13 +76,26 @@ pub struct TestState { pub validator_pairs: Vec, // TODO pub statements_tracker: HashMap>>, + pub manifests_tracker: HashMap>, + pub session_info: SessionInfo, + pub statements: HashMap>>, + pub node_group: Vec, + pub connected_validators: Vec, } impl TestState { pub fn new(config: &TestConfiguration) -> Self { + let test_authorities = config.generate_authorities(); + let session_info = session_info_for_peers(config, &test_authorities); + let node_group = session_info + .validator_groups + .iter() + .find(|g| g.contains(&ValidatorIndex(NODE_UNDER_TEST))) + .unwrap() + .clone(); let mut state = Self { config: config.clone(), - test_authorities: config.generate_authorities(), + test_authorities, block_infos: (1..=config.num_blocks).map(generate_block_info).collect(), candidate_receipts: Default::default(), commited_candidate_receipts: Default::default(), @@ -86,6 +103,11 @@ impl TestState { block_headers: Default::default(), validator_pairs: Default::default(), statements_tracker: Default::default(), + manifests_tracker: Default::default(), + session_info, + node_group, + statements: Default::default(), + connected_validators: Default::default(), }; state.block_headers = state.block_infos.iter().map(generate_block_header).collect(); @@ -104,6 +126,7 @@ impl TestState { let candidate_index = *pov_size_to_candidate.get(pov_size).expect("pov_size always exists; qed"); let mut receipt = receipt_templates[candidate_index].clone(); + receipt.descriptor.para_id = Id::new(core_idx as u32 + 1); receipt.descriptor.relay_parent = block_info.hash; state.candidate_receipts.entry(block_info.hash).or_default().push( @@ -117,6 +140,7 @@ impl TestState { .map(|index| (index as u32, Arc::new(AtomicBool::new(index <= 1)))) .collect::>(), ); + state.manifests_tracker.insert(receipt.hash(), Arc::new(AtomicBool::new(false))); state .commited_candidate_receipts .entry(block_info.hash) @@ -125,10 +149,52 @@ impl TestState { } } + let groups = state.session_info.validator_groups.clone(); + + for block_info in state.block_infos.iter() { + for (index, group) in groups.iter().enumerate() { + let candidate = + state.candidate_receipts.get(&block_info.hash).unwrap().get(index).unwrap(); + let statements = group + .iter() + .map(|&v| { + sign_statement( + CompactStatement::Seconded(candidate.hash()), + block_info.hash, + v, + state.validator_pairs.get(v.0 as usize).unwrap(), + ) + }) + .collect_vec(); + state.statements.insert(candidate.hash(), statements); + } + } + state } } +fn sign_statement( + statement: CompactStatement, + relay_parent: H256, + validator_index: ValidatorIndex, + pair: &ValidatorPair, +) -> UncheckedSigned { + let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; + let payload = statement.signing_payload(&context); + + SignedStatement::new( + statement, + validator_index, + pair.sign(&payload[..]), + &context, + &pair.public(), + ) + .unwrap() + .as_unchecked() + .to_owned() +} + fn generate_block_info(block_num: usize) -> BlockInfo { new_block_import_info(Hash::repeat_byte(block_num as u8), block_num as BlockNumber) } @@ -192,7 +258,13 @@ impl HandleNetworkMessage for TestState { node_sender: &mut futures::channel::mpsc::UnboundedSender, ) -> Option { match message { - NetworkMessage::RequestFromNode(_authority_id, Requests::AttestedCandidateV2(req)) => { + NetworkMessage::RequestFromNode(authority_id, Requests::AttestedCandidateV2(req)) => { + let index = self + .test_authorities + .validator_authority_id + .iter() + .position(|v| v == &authority_id) + .expect("Should exist"); let payload = req.payload; let candidate_receipt = self .commited_candidate_receipts @@ -202,10 +274,16 @@ impl HandleNetworkMessage for TestState { .expect("Pregenerated") .clone(); let persisted_validation_data = self.pvd.clone(); + let mut statements = self.statements.get(&payload.candidate_hash).unwrap().clone(); + if self.node_group.contains(&ValidatorIndex(index as u32)) && + statements.iter().any(|s| s.unchecked_validator_index().0 == index as u32) + { + statements.retain(|s| s.unchecked_validator_index().0 == index as u32) + } let res = AttestedCandidateResponse { candidate_receipt, persisted_validation_data, - statements: vec![], + statements, }; let _ = req.pending_response.send(Ok((res.encode(), ProtocolName::from("")))); None @@ -223,7 +301,6 @@ impl HandleNetworkMessage for TestState { .position(|v| v == &authority_id) .expect("Should exist"); let candidate_hash = *statement.unchecked_payload().candidate_hash(); - gum::debug!("ValidatorIndex({}) received {:?}", index, statement); let sent = self .statements_tracker @@ -239,7 +316,9 @@ impl HandleNetworkMessage for TestState { sent.store(true, Ordering::SeqCst); } - if index > self.config.max_validators_per_core - 1 { + let group_statements = self.statements.get(&candidate_hash).unwrap(); + if !group_statements.iter().any(|s| s.unchecked_validator_index().0 == index as u32) + { return None } @@ -281,6 +360,7 @@ impl HandleNetworkMessage for TestState { .iter() .position(|v| v == &authority_id) .expect("Should exist"); + gum::info!(target: LOG_TARGET, index = ?index, "Received BackedCandidateManifest"); let ack = BackedCandidateAcknowledgement { candidate_hash: manifest.candidate_hash, statement_knowledge: StatementFilter { @@ -288,7 +368,6 @@ impl HandleNetworkMessage for TestState { validated_in_group: bitvec::bitvec![u8, Lsb0; 0,0,1,0,0], }, }; - gum::debug!("ValidatorIndex({}) sends {:?}", index, ack); node_sender .start_send(NetworkMessage::MessageFromPeer( *self.test_authorities.peer_ids.get(index).expect("Must exist"), @@ -297,6 +376,35 @@ impl HandleNetworkMessage for TestState { )), )) .unwrap(); + + self.manifests_tracker + .get(&manifest.candidate_hash) + .expect("Pregenerated") + .as_ref() + .store(true, Ordering::SeqCst); + + None + }, + NetworkMessage::MessageFromNode( + authority_id, + Versioned::V3(ValidationProtocol::StatementDistribution( + StatementDistributionMessage::BackedCandidateKnown(ack), + )), + ) => { + let index = self + .test_authorities + .validator_authority_id + .iter() + .position(|v| v == &authority_id) + .expect("Should exist"); + gum::info!(target: LOG_TARGET, index = ?index, "Received BackedCandidateKnown"); + + self.manifests_tracker + .get(&ack.candidate_hash) + .expect("Pregenerated") + .as_ref() + .store(true, Ordering::SeqCst); + None }, _ => Some(message), From e7fb5435ae193d3e34eabf4fe488003393dfeef2 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Mon, 13 May 2024 15:47:05 +0200 Subject: [PATCH 12/39] Optimize neighbors messages --- .../examples/statement_distribution.yaml | 3 +- .../src/cli/subsystem-bench.rs | 2 +- .../src/lib/mock/prospective_parachains.rs | 2 - .../subsystem-bench/src/lib/statement/mod.rs | 181 ++++++------------ 4 files changed, 63 insertions(+), 125 deletions(-) diff --git a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml index 8dc0b46ebb4d..9c99ce5aba81 100644 --- a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml +++ b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml @@ -1,5 +1,4 @@ TestConfiguration: - objective: StatementDistribution - num_blocks: 1 - connectivity: 100 + num_blocks: 10 n_validators: 300 diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index cf0cd6a3f9d6..632f9b72a2ff 100644 --- a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -203,7 +203,7 @@ fn main() -> eyre::Result<()> { .filter(Some("hyper"), log::LevelFilter::Info) // Avoid `Terminating due to subsystem exit subsystem` warnings .filter(Some("polkadot_overseer"), log::LevelFilter::Error) - .filter(None, log::LevelFilter::Debug) + .filter(None, log::LevelFilter::Info) .format_timestamp_millis() .try_init() .unwrap(); diff --git a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs index ea6553062232..943855a3431d 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs @@ -23,8 +23,6 @@ use polkadot_node_subsystem::{ use polkadot_node_subsystem_types::OverseerSignal; use polkadot_primitives::Hash; -const LOG_TARGET: &str = "subsystem-bench::prospective-parachains-mock"; - pub struct MockProspectiveParachains {} impl MockProspectiveParachains { diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 42612096ccac..c198979ac411 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -257,7 +257,8 @@ pub async fn benchmark_statement_distribution( } let test_start = Instant::now(); - for block_info in state.block_infos.iter() { + for (iteration, block_info) in state.block_infos.iter().enumerate() { + let iteration = iteration + 1; let block_num = block_info.number as usize; gum::info!(target: LOG_TARGET, "Current block {}/{} {}", block_num, config.num_blocks, block_info.hash); env.metrics().set_current_block(block_num); @@ -318,131 +319,72 @@ pub async fn benchmark_statement_distribution( let mut message_tracker = (0..groups.len()).map(|i| i == node_group_index).collect_vec(); - let one_hop_validators = connected_neighbors_x + let one_hop_initiators = connected_neighbors_x .iter() .chain(connected_neighbors_y.iter()) - .filter(|v| !node_group.contains(v)); - - // 1-hop messages - for validator_index in one_hop_validators { - let group_index = - groups.iter().position(|group| group.contains(validator_index)).unwrap(); - let sent = message_tracker.get_mut(group_index).unwrap(); - if *sent { - continue - } - - *sent = true; - let seconding_index = validator_index.0 as usize; - let seconding_peer_id = *state.test_authorities.peer_ids.get(seconding_index).unwrap(); - let candidate_hash = state - .candidate_receipts - .get(&block_info.hash) - .unwrap() - .get(group_index) - .unwrap() - .hash(); - let manifest = BackedCandidateManifest { - relay_parent: block_info.hash, - candidate_hash, - group_index: GroupIndex(group_index as u32), - para_id: Id::new(group_index as u32 + 1), - parent_head_data_hash: state.pvd.parent_head.hash(), - statement_knowledge: StatementFilter { - seconded_in_group: BitVec::from_iter( - groups - .get(GroupIndex(group_index as u32)) - .unwrap() + .filter(|v| !node_group.contains(v)) + .map(|validator_index| { + let peer_id = + *state.test_authorities.peer_ids.get(validator_index.0 as usize).unwrap(); + let group_index = + groups.iter().position(|group| group.contains(validator_index)).unwrap(); + (peer_id, group_index) + }) + .collect_vec(); + let two_hop_x_initiators = connected_neighbors_x + .iter() + .filter(|v| !node_group.contains(v)) + .flat_map(|validator_index| { + let peer_id = + *state.test_authorities.peer_ids.get(validator_index.0 as usize).unwrap(); + topology + .compute_grid_neighbors_for(*validator_index) + .unwrap() + .validator_indices_y + .iter() + .map(|validator_neighbor| { + let group_index = groups .iter() - .map(|v| state.connected_validators.contains(&(v.0 as usize))), - ), - validated_in_group: bitvec::bitvec![u8, Lsb0; 0, 0, 0, 0, 0], - }, - }; - let message = AllMessages::StatementDistribution( - StatementDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( - seconding_peer_id, - Versioned::V3(v3::StatementDistributionMessage::BackedCandidateManifest( - manifest, - )), - )), - ); - env.send_message(message).await; - } - - // 2-hop mssages X - for validator_index in connected_neighbors_x.iter().filter(|v| !node_group.contains(v)) { - let validator_neighbor = topology - .compute_grid_neighbors_for(*validator_index) - .unwrap() - .validator_indices_y - .into_iter() - .next() - .unwrap(); - let group_index = - groups.iter().position(|group| group.contains(&validator_neighbor)).unwrap(); - - let sent = message_tracker.get_mut(group_index).unwrap(); - if *sent { - continue - } - *sent = true; - let seconding_index = validator_index.0 as usize; - let seconding_peer_id = *state.test_authorities.peer_ids.get(seconding_index).unwrap(); - let candidate_hash = state - .candidate_receipts - .get(&block_info.hash) - .unwrap() - .get(group_index) - .unwrap() - .hash(); - let manifest = BackedCandidateManifest { - relay_parent: block_info.hash, - candidate_hash, - group_index: GroupIndex(group_index as u32), - para_id: Id::new(group_index as u32 + 1), - parent_head_data_hash: state.pvd.parent_head.hash(), - statement_knowledge: StatementFilter { - seconded_in_group: BitVec::from_iter( - groups - .get(GroupIndex(group_index as u32)) - .unwrap() + .position(|group| group.contains(validator_neighbor)) + .unwrap(); + (peer_id, group_index) + }) + .collect_vec() + }) + .collect_vec(); + let two_hop_y_initiators = connected_neighbors_y + .iter() + .filter(|v| !node_group.contains(v)) + .flat_map(|validator_index| { + let peer_id = + *state.test_authorities.peer_ids.get(validator_index.0 as usize).unwrap(); + topology + .compute_grid_neighbors_for(*validator_index) + .unwrap() + .validator_indices_x + .iter() + .map(|validator_neighbor| { + let group_index = groups .iter() - .map(|v| state.connected_validators.contains(&(v.0 as usize))), - ), - validated_in_group: bitvec::bitvec![u8, Lsb0; 0, 0, 0, 0, 0], - }, - }; - let message = AllMessages::StatementDistribution( - StatementDistributionMessage::NetworkBridgeUpdate(NetworkBridgeEvent::PeerMessage( - seconding_peer_id, - Versioned::V3(v3::StatementDistributionMessage::BackedCandidateManifest( - manifest, - )), - )), - ); - env.send_message(message).await; - } - - // 2-hop mssages Y - for validator_index in connected_neighbors_y.iter().filter(|v| !node_group.contains(v)) { - let validator_neighbor = topology - .compute_grid_neighbors_for(*validator_index) - .unwrap() - .validator_indices_x - .into_iter() - .next() - .unwrap(); - let group_index = - groups.iter().position(|group| group.contains(&validator_neighbor)).unwrap(); + .position(|group| group.contains(validator_neighbor)) + .unwrap(); + (peer_id, group_index) + }) + .collect_vec() + }) + .collect_vec(); + for (seconding_peer_id, group_index) in one_hop_initiators + .into_iter() + .chain(two_hop_x_initiators) + .chain(two_hop_y_initiators) + { let sent = message_tracker.get_mut(group_index).unwrap(); if *sent { continue } *sent = true; - let seconding_index = validator_index.0 as usize; - let seconding_peer_id = *state.test_authorities.peer_ids.get(seconding_index).unwrap(); + let candidate_hash = state .candidate_receipts .get(&block_info.hash) @@ -496,10 +438,9 @@ pub async fn benchmark_statement_distribution( .collect::>() .len(); let message_count = message_tracker.iter().filter(|&&v| v).collect_vec().len(); - let all_groups = message_tracker.len(); - gum::info!(target: LOG_TARGET, ?message_count, ?manifest_count, ?all_groups); - if manifest_count < config.n_cores { - timeout = futures_timer::Delay::new(Duration::from_millis(100)).fuse(); + gum::debug!(target: LOG_TARGET, ?message_count, ?manifest_count); + if manifest_count < config.n_cores * iteration { + timeout = futures_timer::Delay::new(Duration::from_millis(50)).fuse(); } else { break; } From dcba6d079a94008fa8953a6f910da0b4b22bc578 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Tue, 14 May 2024 17:44:36 +0200 Subject: [PATCH 13/39] Polish --- polkadot/node/subsystem-bench/src/lib/lib.rs | 5 +- .../node/subsystem-bench/src/lib/network.rs | 11 +++- .../subsystem-bench/src/lib/statement/mod.rs | 51 +++++++++---------- .../src/lib/statement/test_state.rs | 15 ++++-- 4 files changed, 49 insertions(+), 33 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/lib.rs b/polkadot/node/subsystem-bench/src/lib/lib.rs index 90533523f289..5d7872531c3c 100644 --- a/polkadot/node/subsystem-bench/src/lib/lib.rs +++ b/polkadot/node/subsystem-bench/src/lib/lib.rs @@ -14,8 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -// The validator index that represent the node that is under test. +// The validator index that represents the node that is under test. pub const NODE_UNDER_TEST: u32 = 0; +// The validator index that represents the peer in same group as the node under test +// Always connected +pub const PEER_IN_NODE_GROUP: u32 = 1; pub mod approval; pub mod availability; diff --git a/polkadot/node/subsystem-bench/src/lib/network.rs b/polkadot/node/subsystem-bench/src/lib/network.rs index b0375599a4ef..e4e018cf045c 100644 --- a/polkadot/node/subsystem-bench/src/lib/network.rs +++ b/polkadot/node/subsystem-bench/src/lib/network.rs @@ -36,7 +36,7 @@ use crate::{ configuration::{random_latency, TestAuthorities, TestConfiguration}, environment::TestEnvironmentDependencies, - NODE_UNDER_TEST, + NODE_UNDER_TEST, PEER_IN_NODE_GROUP, }; use colored::Colorize; use futures::{ @@ -847,8 +847,15 @@ pub fn new_network( peers_indices.partial_shuffle(&mut thread_rng(), connected_count); // Node under test is always mark as disconnected. + let mut skip = 1; peers[NODE_UNDER_TEST as usize].disconnect(); - for peer in to_disconnect.iter().skip(1) { + if to_disconnect.contains(&(PEER_IN_NODE_GROUP as usize)) { + skip -= 1; + } + for peer in to_disconnect.iter().skip(skip) { + if *peer == PEER_IN_NODE_GROUP as usize { + continue + } peers[*peer].disconnect(); } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index c198979ac411..52c752f5193f 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -28,7 +28,7 @@ use crate::{ }, network::new_network, usage::BenchmarkUsage, - NODE_UNDER_TEST, + NODE_UNDER_TEST, PEER_IN_NODE_GROUP, }; use bitvec::{order::Lsb0, vec::BitVec}; use colored::Colorize; @@ -237,9 +237,9 @@ pub async fn benchmark_statement_distribution( let config = env.config().clone(); let groups = state.session_info.validator_groups.clone(); - let (node_group_index, node_group) = groups + let node_group_index = groups .iter() - .find_position(|group| group.iter().any(|v| v.0 == NODE_UNDER_TEST)) + .position(|group| group.iter().any(|v| v.0 == NODE_UNDER_TEST)) .expect("Pregenerated"); env.metrics().set_n_validators(config.n_validators); @@ -257,8 +257,8 @@ pub async fn benchmark_statement_distribution( } let test_start = Instant::now(); - for (iteration, block_info) in state.block_infos.iter().enumerate() { - let iteration = iteration + 1; + let mut total_message_count = 0; + for block_info in state.block_infos.iter() { let block_num = block_info.number as usize; gum::info!(target: LOG_TARGET, "Current block {}/{} {}", block_num, config.num_blocks, block_info.hash); env.metrics().set_current_block(block_num); @@ -271,22 +271,15 @@ pub async fn benchmark_statement_distribution( env.send_message(update).await; } - let seconding = node_group - .iter() - .find(|v| { - v.0 != NODE_UNDER_TEST && state.connected_validators.contains(&(v.0 as usize)) - }) - .expect("Validator group should contain enough connected validators") - .to_owned(); - let seconding_idx = seconding.0 as usize; - let seconding_peer_id = *state.test_authorities.peer_ids.get(seconding_idx).unwrap(); + let seconding_peer_id = + *state.test_authorities.peer_ids.get(PEER_IN_NODE_GROUP as usize).unwrap(); let candidate = state.candidate_receipts.get(&block_info.hash).unwrap().first().unwrap(); let candidate_hash = candidate.hash(); let statement = state .statements .get(&candidate_hash) .unwrap() - .get(seconding_idx) + .get(PEER_IN_NODE_GROUP as usize) .unwrap() .clone(); let message = AllMessages::StatementDistribution( @@ -299,6 +292,8 @@ pub async fn benchmark_statement_distribution( )), ); env.send_message(message).await; + // Just sent for the node group + let mut message_tracker = (0..groups.len()).map(|i| i == node_group_index).collect_vec(); let neighbors = topology.compute_grid_neighbors_for(ValidatorIndex(NODE_UNDER_TEST)).unwrap(); @@ -317,12 +312,9 @@ pub async fn benchmark_statement_distribution( .cloned() .collect_vec(); - let mut message_tracker = (0..groups.len()).map(|i| i == node_group_index).collect_vec(); - let one_hop_initiators = connected_neighbors_x .iter() .chain(connected_neighbors_y.iter()) - .filter(|v| !node_group.contains(v)) .map(|validator_index| { let peer_id = *state.test_authorities.peer_ids.get(validator_index.0 as usize).unwrap(); @@ -333,7 +325,6 @@ pub async fn benchmark_statement_distribution( .collect_vec(); let two_hop_x_initiators = connected_neighbors_x .iter() - .filter(|v| !node_group.contains(v)) .flat_map(|validator_index| { let peer_id = *state.test_authorities.peer_ids.get(validator_index.0 as usize).unwrap(); @@ -354,7 +345,6 @@ pub async fn benchmark_statement_distribution( .collect_vec(); let two_hop_y_initiators = connected_neighbors_y .iter() - .filter(|v| !node_group.contains(v)) .flat_map(|validator_index| { let peer_id = *state.test_authorities.peer_ids.get(validator_index.0 as usize).unwrap(); @@ -420,8 +410,13 @@ pub async fn benchmark_statement_distribution( env.send_message(message).await; } - let mut timeout = futures_timer::Delay::new(Duration::from_millis(100)).fuse(); + let message_count = message_tracker.iter().filter(|&&v| v).collect_vec().len(); + if message_count < 60 { + gum::error!(message_tracker = ?message_tracker.iter().enumerate().collect_vec()); + } + total_message_count += message_count; + let mut timeout = message_check_delay(); loop { futures::select! { msg = to_subsystems.next() => { @@ -437,12 +432,12 @@ pub async fn benchmark_statement_distribution( .filter(|v| v.load(Ordering::SeqCst)) .collect::>() .len(); - let message_count = message_tracker.iter().filter(|&&v| v).collect_vec().len(); - gum::debug!(target: LOG_TARGET, ?message_count, ?manifest_count); - if manifest_count < config.n_cores * iteration { - timeout = futures_timer::Delay::new(Duration::from_millis(50)).fuse(); - } else { + + gum::info!(target: LOG_TARGET, ?total_message_count, ?manifest_count); + if manifest_count == total_message_count { break; + } else { + timeout = message_check_delay(); } } } @@ -459,3 +454,7 @@ pub async fn benchmark_statement_distribution( env.stop().await; env.collect_resource_usage(benchmark_name, &["statement-distribution"]) } + +fn message_check_delay() -> futures::prelude::future::Fuse { + futures_timer::Delay::new(Duration::from_millis(50)).fuse() +} diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index d43d88241735..5b05fced85b5 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -18,9 +18,9 @@ use crate::{ configuration::{TestAuthorities, TestConfiguration}, mock::runtime_api::session_info_for_peers, network::{HandleNetworkMessage, NetworkMessage}, - NODE_UNDER_TEST, + NODE_UNDER_TEST, PEER_IN_NODE_GROUP, }; -use bitvec::order::Lsb0; +use bitvec::vec::BitVec; use itertools::Itertools; use parity_scale_codec::Encode; use polkadot_node_network_protocol::{ @@ -360,12 +360,19 @@ impl HandleNetworkMessage for TestState { .iter() .position(|v| v == &authority_id) .expect("Should exist"); + gum::info!(target: LOG_TARGET, index = ?index, "Received BackedCandidateManifest"); let ack = BackedCandidateAcknowledgement { candidate_hash: manifest.candidate_hash, statement_knowledge: StatementFilter { - seconded_in_group: bitvec::bitvec![u8, Lsb0; 0,1,0,0,0], - validated_in_group: bitvec::bitvec![u8, Lsb0; 0,0,1,0,0], + seconded_in_group: BitVec::from_iter( + (0..self.config.max_validators_per_core) + .map(|v| v == PEER_IN_NODE_GROUP as usize), + ), + validated_in_group: BitVec::from_iter( + (0..self.config.max_validators_per_core) + .map(|v| v == NODE_UNDER_TEST as usize), + ), }, }; node_sender From 8ccf3cce9388d36805b76546b8407bfdd6a86dec Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Tue, 14 May 2024 17:53:50 +0200 Subject: [PATCH 14/39] Update --- .../subsystem-bench/examples/statement_distribution.yaml | 3 ++- .../node/subsystem-bench/src/lib/statement/test_state.rs | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml index 9c99ce5aba81..e86669ffefc3 100644 --- a/polkadot/node/subsystem-bench/examples/statement_distribution.yaml +++ b/polkadot/node/subsystem-bench/examples/statement_distribution.yaml @@ -1,4 +1,5 @@ TestConfiguration: - objective: StatementDistribution num_blocks: 10 - n_validators: 300 + n_cores: 100 + n_validators: 500 diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 5b05fced85b5..c74e11a7e262 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -366,12 +366,10 @@ impl HandleNetworkMessage for TestState { candidate_hash: manifest.candidate_hash, statement_knowledge: StatementFilter { seconded_in_group: BitVec::from_iter( - (0..self.config.max_validators_per_core) - .map(|v| v == PEER_IN_NODE_GROUP as usize), + (0..self.node_group.len()).map(|v| v == PEER_IN_NODE_GROUP as usize), ), validated_in_group: BitVec::from_iter( - (0..self.config.max_validators_per_core) - .map(|v| v == NODE_UNDER_TEST as usize), + (0..self.node_group.len()).map(|v| v == NODE_UNDER_TEST as usize), ), }, }; From 560a5d57c87563245644572c4a074ba366ab6889 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Tue, 14 May 2024 19:14:37 +0200 Subject: [PATCH 15/39] Reset trackers --- .../subsystem-bench/src/lib/statement/mod.rs | 8 +++---- .../src/lib/statement/test_state.rs | 23 ++++++++++++++----- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 52c752f5193f..aeccce80e4fb 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -410,11 +410,7 @@ pub async fn benchmark_statement_distribution( env.send_message(message).await; } - let message_count = message_tracker.iter().filter(|&&v| v).collect_vec().len(); - if message_count < 60 { - gum::error!(message_tracker = ?message_tracker.iter().enumerate().collect_vec()); - } - total_message_count += message_count; + total_message_count += message_tracker.iter().filter(|&&v| v).collect_vec().len(); let mut timeout = message_check_delay(); loop { @@ -444,6 +440,8 @@ pub async fn benchmark_statement_distribution( } } + state.reset_trackers(); + let duration: u128 = test_start.elapsed().as_millis(); gum::info!(target: LOG_TARGET, "All blocks processed in {}", format!("{:?}ms", duration).cyan()); gum::info!(target: LOG_TARGET, diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index c74e11a7e262..2a67f7aa3fe1 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -75,7 +75,7 @@ pub struct TestState { // TODO pub validator_pairs: Vec, // TODO - pub statements_tracker: HashMap>>, + pub statements_tracker: HashMap>>, pub manifests_tracker: HashMap>, pub session_info: SessionInfo, pub statements: HashMap>>, @@ -137,8 +137,8 @@ impl TestState { ); state.statements_tracker.entry(receipt.hash()).or_default().extend( (0..config.n_validators) - .map(|index| (index as u32, Arc::new(AtomicBool::new(index <= 1)))) - .collect::>(), + .map(|index| Arc::new(AtomicBool::new(index <= 1))) + .collect_vec(), ); state.manifests_tracker.insert(receipt.hash(), Arc::new(AtomicBool::new(false))); state @@ -172,6 +172,17 @@ impl TestState { state } + + pub fn reset_trackers(&self) { + self.statements_tracker.values().for_each(|v| { + v.iter() + .enumerate() + .for_each(|(index, v)| v.as_ref().store(index <= 1, Ordering::SeqCst)) + }); + self.manifests_tracker + .values() + .for_each(|v| v.as_ref().store(false, Ordering::SeqCst)); + } } fn sign_statement( @@ -305,9 +316,9 @@ impl HandleNetworkMessage for TestState { let sent = self .statements_tracker .get(&candidate_hash) - .expect("Pregenerated") - .get(&(index as u32)) - .expect("Pregenerated") + .unwrap() + .get(index) + .unwrap() .as_ref(); if sent.load(Ordering::SeqCst) { From daf901f7e6043aa4eb1630a7e0f128dde7d0c10e Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Tue, 14 May 2024 19:15:03 +0200 Subject: [PATCH 16/39] Add benchmarks --- .gitlab/pipeline/publish.yml | 4 + .gitlab/pipeline/test.yml | 7 ++ Cargo.lock | 1 + .../network/statement-distribution/Cargo.toml | 10 +++ ...statement-distribution-regression-bench.rs | 79 +++++++++++++++++++ 5 files changed, 101 insertions(+) create mode 100644 polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs diff --git a/.gitlab/pipeline/publish.yml b/.gitlab/pipeline/publish.yml index 68712610ad23..fb899f66f195 100644 --- a/.gitlab/pipeline/publish.yml +++ b/.gitlab/pipeline/publish.yml @@ -76,6 +76,8 @@ publish-subsystem-benchmarks: artifacts: true - job: subsystem-benchmark-approval-voting artifacts: true + - job: subsystem-benchmark-statement-distribution + artifacts: true - job: publish-rustdoc artifacts: false script: @@ -119,6 +121,8 @@ trigger_workflow: artifacts: true - job: subsystem-benchmark-approval-voting artifacts: true + - job: subsystem-benchmark-statement-distribution + artifacts: true script: - echo "Triggering workflow" - > diff --git a/.gitlab/pipeline/test.yml b/.gitlab/pipeline/test.yml index c17a3ce35eaf..e87fa68a599a 100644 --- a/.gitlab/pipeline/test.yml +++ b/.gitlab/pipeline/test.yml @@ -546,3 +546,10 @@ subsystem-benchmark-approval-voting: script: - cargo bench -p polkadot-node-core-approval-voting --bench approval-voting-regression-bench --features subsystem-benchmarks allow_failure: true + +subsystem-benchmark-statement-distribution: + extends: + - .subsystem-benchmark-template + script: + - cargo bench -p polkadot-statement-distribution --bench statement-distribution-regression-bench --features subsystem-benchmarks + allow_failure: true diff --git a/Cargo.lock b/Cargo.lock index f8f922530d7d..bff769fef96f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14132,6 +14132,7 @@ dependencies = [ "polkadot-node-subsystem-util", "polkadot-primitives", "polkadot-primitives-test-helpers", + "polkadot-subsystem-bench", "rand_chacha 0.3.1", "sc-keystore", "sc-network", diff --git a/polkadot/node/network/statement-distribution/Cargo.toml b/polkadot/node/network/statement-distribution/Cargo.toml index d8ae031cbf36..3e9ddf1603ac 100644 --- a/polkadot/node/network/statement-distribution/Cargo.toml +++ b/polkadot/node/network/statement-distribution/Cargo.toml @@ -42,3 +42,13 @@ sc-network = { path = "../../../../substrate/client/network" } futures-timer = "3.0.2" polkadot-primitives-test-helpers = { path = "../../../primitives/test-helpers" } rand_chacha = "0.3" +polkadot-subsystem-bench = { path = "../../subsystem-bench" } + +[[bench]] +name = "statement-distribution-regression-bench" +path = "benches/statement-distribution-regression-bench.rs" +harness = false +required-features = ["subsystem-benchmarks"] + +[features] +subsystem-benchmarks = [] diff --git a/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs b/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs new file mode 100644 index 000000000000..29990f0c3a30 --- /dev/null +++ b/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs @@ -0,0 +1,79 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// This file is part of Polkadot. + +// Polkadot 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. + +// Polkadot 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 Polkadot. If not, see . + +//! statement-distribution regression tests +//! +//! Statement distribution benchmark based on Kusama parameters and scale. + +use polkadot_subsystem_bench::{ + configuration::TestConfiguration, + statement::{benchmark_statement_distribution, prepare_test, TestState}, + usage::BenchmarkUsage, + utils::save_to_file, +}; +use std::io::Write; + +const BENCH_COUNT: usize = 50; + +fn main() -> Result<(), String> { + let mut messages = vec![]; + let mut config = TestConfiguration::default(); + config.n_cores = 100; + config.n_validators = 500; + config.num_blocks = 10; + config.connectivity = 100; + config.generate_pov_sizes(); + let state = TestState::new(&config); + + println!("Benchmarking..."); + let usages: Vec = (0..BENCH_COUNT) + .map(|n| { + print!("\r[{}{}]", "#".repeat(n), "_".repeat(BENCH_COUNT - n)); + std::io::stdout().flush().unwrap(); + let (mut env, _cfgs, to_subsystems) = prepare_test(&state, false); + env.runtime().block_on(benchmark_statement_distribution( + "statement-distribution", + &mut env, + &state, + to_subsystems, + )) + }) + .collect(); + println!("\rDone!{}", " ".repeat(BENCH_COUNT)); + + let average_usage = BenchmarkUsage::average(&usages); + save_to_file( + "charts/statement-distribution-regression-bench.json", + average_usage.to_chart_json().map_err(|e| e.to_string())?, + ) + .map_err(|e| e.to_string())?; + println!("{}", average_usage); + + // We expect no variance for received and sent + // but use 0.001 because we operate with floats + messages.extend(average_usage.check_network_usage(&[ + ("Received from peers", 106.0680, 0.001), + ("Sent to peers", 374.5640, 0.001), + ])); + messages.extend(average_usage.check_cpu_usage(&[("statement-distribution", 0.0320, 0.1)])); + + if messages.is_empty() { + Ok(()) + } else { + eprintln!("{}", messages.join("\n")); + Err("Regressions found".to_string()) + } +} From 4685d9f7b72a4544191dfc303a7fa7bd14a4c6f1 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Wed, 15 May 2024 17:38:22 +0200 Subject: [PATCH 17/39] Update test state --- .../subsystem-bench/src/lib/statement/mod.rs | 19 +++++++------ .../src/lib/statement/test_state.rs | 28 ++----------------- 2 files changed, 13 insertions(+), 34 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index aeccce80e4fb..0901fdebf2fa 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -30,7 +30,7 @@ use crate::{ usage::BenchmarkUsage, NODE_UNDER_TEST, PEER_IN_NODE_GROUP, }; -use bitvec::{order::Lsb0, vec::BitVec}; +use bitvec::vec::BitVec; use colored::Colorize; use futures::{channel::mpsc, FutureExt, StreamExt}; use itertools::Itertools; @@ -226,8 +226,9 @@ pub async fn benchmark_statement_distribution( state: &TestState, mut to_subsystems: mpsc::UnboundedReceiver, ) -> BenchmarkUsage { - let mut state = state.clone(); - state.connected_validators = state + state.reset_trackers(); + + let connected_validators = state .test_authorities .validator_authority_id .iter() @@ -301,14 +302,14 @@ pub async fn benchmark_statement_distribution( let connected_neighbors_x = neighbors .validator_indices_x .iter() - .filter(|&v| state.connected_validators.contains(&(v.0 as usize))) + .filter(|&v| connected_validators.contains(&(v.0 as usize))) .cloned() .collect_vec(); let connected_neighbors_y = neighbors .validator_indices_y .iter() - .filter(|&v| state.connected_validators.contains(&(v.0 as usize))) + .filter(|&v| connected_validators.contains(&(v.0 as usize))) .cloned() .collect_vec(); @@ -394,9 +395,11 @@ pub async fn benchmark_statement_distribution( .get(GroupIndex(group_index as u32)) .unwrap() .iter() - .map(|v| state.connected_validators.contains(&(v.0 as usize))), + .map(|v| connected_validators.contains(&(v.0 as usize))), + ), + validated_in_group: BitVec::from_iter( + groups.get(GroupIndex(group_index as u32)).unwrap().iter().map(|_| false), ), - validated_in_group: bitvec::bitvec![u8, Lsb0; 0, 0, 0, 0, 0], }, }; let message = AllMessages::StatementDistribution( @@ -440,8 +443,6 @@ pub async fn benchmark_statement_distribution( } } - state.reset_trackers(); - let duration: u128 = test_start.elapsed().as_millis(); gum::info!(target: LOG_TARGET, "All blocks processed in {}", format!("{:?}ms", duration).cyan()); gum::info!(target: LOG_TARGET, diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 2a67f7aa3fe1..5a600b4ce28b 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -80,7 +80,6 @@ pub struct TestState { pub session_info: SessionInfo, pub statements: HashMap>>, pub node_group: Vec, - pub connected_validators: Vec, } impl TestState { @@ -107,7 +106,6 @@ impl TestState { session_info, node_group, statements: Default::default(), - connected_validators: Default::default(), }; state.block_headers = state.block_infos.iter().map(generate_block_header).collect(); @@ -269,13 +267,7 @@ impl HandleNetworkMessage for TestState { node_sender: &mut futures::channel::mpsc::UnboundedSender, ) -> Option { match message { - NetworkMessage::RequestFromNode(authority_id, Requests::AttestedCandidateV2(req)) => { - let index = self - .test_authorities - .validator_authority_id - .iter() - .position(|v| v == &authority_id) - .expect("Should exist"); + NetworkMessage::RequestFromNode(_authority_id, Requests::AttestedCandidateV2(req)) => { let payload = req.payload; let candidate_receipt = self .commited_candidate_receipts @@ -285,12 +277,7 @@ impl HandleNetworkMessage for TestState { .expect("Pregenerated") .clone(); let persisted_validation_data = self.pvd.clone(); - let mut statements = self.statements.get(&payload.candidate_hash).unwrap().clone(); - if self.node_group.contains(&ValidatorIndex(index as u32)) && - statements.iter().any(|s| s.unchecked_validator_index().0 == index as u32) - { - statements.retain(|s| s.unchecked_validator_index().0 == index as u32) - } + let statements = self.statements.get(&payload.candidate_hash).unwrap().clone(); let res = AttestedCandidateResponse { candidate_receipt, persisted_validation_data, @@ -372,7 +359,6 @@ impl HandleNetworkMessage for TestState { .position(|v| v == &authority_id) .expect("Should exist"); - gum::info!(target: LOG_TARGET, index = ?index, "Received BackedCandidateManifest"); let ack = BackedCandidateAcknowledgement { candidate_hash: manifest.candidate_hash, statement_knowledge: StatementFilter { @@ -402,19 +388,11 @@ impl HandleNetworkMessage for TestState { None }, NetworkMessage::MessageFromNode( - authority_id, + _authority_id, Versioned::V3(ValidationProtocol::StatementDistribution( StatementDistributionMessage::BackedCandidateKnown(ack), )), ) => { - let index = self - .test_authorities - .validator_authority_id - .iter() - .position(|v| v == &authority_id) - .expect("Should exist"); - gum::info!(target: LOG_TARGET, index = ?index, "Received BackedCandidateKnown"); - self.manifests_tracker .get(&ack.candidate_hash) .expect("Pregenerated") From 011a7e79f933ec852fafb1e65380f4c00eb9081c Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Wed, 15 May 2024 17:39:25 +0200 Subject: [PATCH 18/39] Remove unused vars --- polkadot/node/subsystem-bench/src/lib/statement/test_state.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 5a600b4ce28b..fc5c9be14be6 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -54,8 +54,6 @@ use std::{ }, }; -const LOG_TARGET: &str = "subsystem-bench::test-state"; - #[derive(Clone)] pub struct TestState { // Full test config From 1512ff853660af71f586a10c545b2a9d108c67d6 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 09:30:49 +0200 Subject: [PATCH 19/39] Fix manifest missing --- polkadot/node/subsystem-bench/src/lib/statement/mod.rs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 0901fdebf2fa..fcfb88e804ba 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -391,11 +391,7 @@ pub async fn benchmark_statement_distribution( parent_head_data_hash: state.pvd.parent_head.hash(), statement_knowledge: StatementFilter { seconded_in_group: BitVec::from_iter( - groups - .get(GroupIndex(group_index as u32)) - .unwrap() - .iter() - .map(|v| connected_validators.contains(&(v.0 as usize))), + groups.get(GroupIndex(group_index as u32)).unwrap().iter().map(|_| true), ), validated_in_group: BitVec::from_iter( groups.get(GroupIndex(group_index as u32)).unwrap().iter().map(|_| false), @@ -420,7 +416,6 @@ pub async fn benchmark_statement_distribution( futures::select! { msg = to_subsystems.next() => { if let Some(msg) = msg { - gum::info!(msg = ?msg); env.send_message(msg).await; } }, @@ -431,8 +426,7 @@ pub async fn benchmark_statement_distribution( .filter(|v| v.load(Ordering::SeqCst)) .collect::>() .len(); - - gum::info!(target: LOG_TARGET, ?total_message_count, ?manifest_count); + gum::debug!(target: LOG_TARGET, "{}/{} manifest exchanges", manifest_count, total_message_count); if manifest_count == total_message_count { break; } else { From cc683e36f75e0e1bc393843e5195edbc4aa7f434 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 09:37:50 +0200 Subject: [PATCH 20/39] Move validator pairs to test authorities --- .../node/subsystem-bench/src/lib/configuration.rs | 10 +++++++++- .../node/subsystem-bench/src/lib/statement/mod.rs | 7 ++++++- .../src/lib/statement/test_state.rs | 14 ++------------ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/configuration.rs b/polkadot/node/subsystem-bench/src/lib/configuration.rs index 1e0efb72a7df..58a379a1758b 100644 --- a/polkadot/node/subsystem-bench/src/lib/configuration.rs +++ b/polkadot/node/subsystem-bench/src/lib/configuration.rs @@ -18,12 +18,13 @@ use crate::keyring::Keyring; use itertools::Itertools; -use polkadot_primitives::{AssignmentId, AuthorityDiscoveryId, ValidatorId}; +use polkadot_primitives::{AssignmentId, AuthorityDiscoveryId, ValidatorId, ValidatorPair}; use rand::thread_rng; use rand_distr::{Distribution, Normal, Uniform}; use sc_network_types::PeerId; use serde::{Deserialize, Serialize}; use sp_consensus_babe::AuthorityId; +use sp_core::Pair; use std::collections::HashMap; /// Peer networking latency configuration. @@ -208,6 +209,11 @@ impl TestConfiguration { .map(|(peer_id, authority_id)| (*peer_id, authority_id.clone())) .collect(); + let validator_pairs = key_seeds + .iter() + .map(|seed| ValidatorPair::from_string_with_seed(seed, None).unwrap().0) + .collect(); + TestAuthorities { keyring, validator_public, @@ -217,6 +223,7 @@ impl TestConfiguration { validator_assignment_id, key_seeds, peer_id_to_authority, + validator_pairs, } } } @@ -246,6 +253,7 @@ pub struct TestAuthorities { pub key_seeds: Vec, pub peer_ids: Vec, pub peer_id_to_authority: HashMap, + pub validator_pairs: Vec, } /// Sample latency (in milliseconds) from a normal distribution with parameters diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index fcfb88e804ba..c55ff2ce4991 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -106,7 +106,12 @@ fn build_overseer( let (tx, rx) = mpsc::unbounded(); let mock_candidate_backing = MockCandidateBacking::new( tx, - state.validator_pairs.get(NODE_UNDER_TEST as usize).unwrap().clone(), + state + .test_authorities + .validator_pairs + .get(NODE_UNDER_TEST as usize) + .unwrap() + .clone(), state.pvd.clone(), state.node_group.clone(), ); diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index fc5c9be14be6..6a0986927bec 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -71,8 +71,6 @@ pub struct TestState { // Relay chain block headers pub block_headers: HashMap, // TODO - pub validator_pairs: Vec, - // TODO pub statements_tracker: HashMap>>, pub manifests_tracker: HashMap>, pub session_info: SessionInfo, @@ -98,7 +96,6 @@ impl TestState { commited_candidate_receipts: Default::default(), pvd: dummy_pvd(dummy_head_data(), 0), block_headers: Default::default(), - validator_pairs: Default::default(), statements_tracker: Default::default(), manifests_tracker: Default::default(), session_info, @@ -107,8 +104,6 @@ impl TestState { }; state.block_headers = state.block_infos.iter().map(generate_block_header).collect(); - state.validator_pairs = - state.test_authorities.key_seeds.iter().map(generate_pair).collect(); // For each unique pov we create a candidate receipt. let pov_sizes = Vec::from(config.pov_sizes()); // For n_cores @@ -158,7 +153,7 @@ impl TestState { CompactStatement::Seconded(candidate.hash()), block_info.hash, v, - state.validator_pairs.get(v.0 as usize).unwrap(), + state.test_authorities.validator_pairs.get(v.0 as usize).unwrap(), ) }) .collect_vec(); @@ -253,11 +248,6 @@ fn generate_receipt_templates( .collect() } -#[allow(clippy::ptr_arg)] -fn generate_pair(seed: &String) -> ValidatorPair { - ValidatorPair::from_string_with_seed(seed, None).unwrap().0 -} - impl HandleNetworkMessage for TestState { fn handle( &self, @@ -321,7 +311,7 @@ impl HandleNetworkMessage for TestState { let statement = CompactStatement::Valid(candidate_hash); let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; let payload = statement.signing_payload(&context); - let pair = self.validator_pairs.get(index).unwrap(); + let pair = self.test_authorities.validator_pairs.get(index).unwrap(); let signature = pair.sign(&payload[..]); let statement = SignedStatement::new( statement, From ea1fd8375b2ae783c47a2addfcf521f47dc7058e Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 16:17:32 +0200 Subject: [PATCH 21/39] Make HandleNetworkMessage async --- .../node/subsystem-bench/src/lib/approval/mod.rs | 3 ++- .../node/subsystem-bench/src/lib/mock/av_store.rs | 3 ++- polkadot/node/subsystem-bench/src/lib/network.rs | 12 +++++++----- .../subsystem-bench/src/lib/statement/test_state.rs | 3 ++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs index 026cf404a7f2..fd3f4b5f4350 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs @@ -465,8 +465,9 @@ impl ApprovalTestState { } } +#[async_trait::async_trait] impl HandleNetworkMessage for ApprovalTestState { - fn handle( + async fn handle( &self, _message: crate::network::NetworkMessage, _node_sender: &mut futures::channel::mpsc::UnboundedSender, diff --git a/polkadot/node/subsystem-bench/src/lib/mock/av_store.rs b/polkadot/node/subsystem-bench/src/lib/mock/av_store.rs index fba33523be85..a035bf018977 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/av_store.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/av_store.rs @@ -49,8 +49,9 @@ pub struct NetworkAvailabilityState { } // Implement access to the state. +#[async_trait::async_trait] impl HandleNetworkMessage for NetworkAvailabilityState { - fn handle( + async fn handle( &self, message: NetworkMessage, _node_sender: &mut futures::channel::mpsc::UnboundedSender, diff --git a/polkadot/node/subsystem-bench/src/lib/network.rs b/polkadot/node/subsystem-bench/src/lib/network.rs index e4e018cf045c..17b0aa609bdd 100644 --- a/polkadot/node/subsystem-bench/src/lib/network.rs +++ b/polkadot/node/subsystem-bench/src/lib/network.rs @@ -499,29 +499,31 @@ impl EmulatedPeer { } /// Interceptor pattern for handling messages. +#[async_trait::async_trait] pub trait HandleNetworkMessage { /// Returns `None` if the message was handled, or the `message` /// otherwise. /// /// `node_sender` allows sending of messages to the node in response /// to the handled message. - fn handle( + async fn handle( &self, message: NetworkMessage, node_sender: &mut UnboundedSender, ) -> Option; } +#[async_trait::async_trait] impl HandleNetworkMessage for Arc where - T: HandleNetworkMessage, + T: HandleNetworkMessage + Sync + Send, { - fn handle( + async fn handle( &self, message: NetworkMessage, node_sender: &mut UnboundedSender, ) -> Option { - self.as_ref().handle(message, node_sender) + T::handle(self, message, node_sender).await } } @@ -554,7 +556,7 @@ async fn emulated_peer_loop( for handler in handlers.iter() { // The check below guarantees that message is always `Some`: we are still // inside the loop. - message = handler.handle(message.unwrap(), &mut to_network_interface); + message = handler.handle(message.unwrap(), &mut to_network_interface).await; if message.is_none() { break } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 6a0986927bec..e1f0549d7fa9 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -248,8 +248,9 @@ fn generate_receipt_templates( .collect() } +#[async_trait::async_trait] impl HandleNetworkMessage for TestState { - fn handle( + async fn handle( &self, message: NetworkMessage, node_sender: &mut futures::channel::mpsc::UnboundedSender, From e29320f46554895e66fca6e2c7fc8bce08362545 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 16:18:20 +0200 Subject: [PATCH 22/39] Request a candidate from own backing group --- .../src/lib/mock/network_bridge.rs | 8 ++- .../subsystem-bench/src/lib/statement/mod.rs | 29 ++++---- .../src/lib/statement/test_state.rs | 68 +++++++++++++++---- 3 files changed, 74 insertions(+), 31 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs index 276f987116be..10508f456a48 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/network_bridge.rs @@ -36,8 +36,10 @@ use polkadot_node_subsystem_types::{ use sc_network::{request_responses::ProtocolConfig, RequestFailure}; const LOG_TARGET: &str = "subsystem-bench::network-bridge"; -const CHUNK_REQ_PROTOCOL_NAME_V1: &str = - "/ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff/req_chunk/1"; +const ALLOWED_PROTOCOLS: &[&str] = &[ + "/ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff/req_chunk/1", + "/ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff/req_attested_candidate/2", +]; /// A mock of the network bridge tx subsystem. pub struct MockNetworkBridgeTx { @@ -214,7 +216,7 @@ impl MockNetworkBridgeRx { }, NetworkMessage::RequestFromPeer(request) => { if let Some(protocol) = self.chunk_request_sender.as_mut() { - assert_eq!(&*protocol.name, CHUNK_REQ_PROTOCOL_NAME_V1); + assert!(ALLOWED_PROTOCOLS.contains(&&*protocol.name)); if let Some(inbound_queue) = protocol.inbound_queue.as_ref() { inbound_queue .send(request) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index c55ff2ce4991..8b5db18089cf 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -26,7 +26,7 @@ use crate::{ runtime_api::{MockRuntimeApi, MockRuntimeApiCoreState}, AlwaysSupportsParachains, }, - network::new_network, + network::{new_network, NetworkEmulatorHandle, NetworkInterface, NetworkInterfaceReceiver}, usage::BenchmarkUsage, NODE_UNDER_TEST, PEER_IN_NODE_GROUP, }; @@ -80,7 +80,9 @@ pub fn make_keystore() -> KeystorePtr { fn build_overseer( state: &TestState, - network_bridge: (MockNetworkBridgeTx, MockNetworkBridgeRx), + network: NetworkEmulatorHandle, + network_interface: NetworkInterface, + network_receiver: NetworkInterfaceReceiver, dependencies: &TestEnvironmentDependencies, ) -> ( Overseer, AlwaysSupportsParachains>, @@ -113,7 +115,7 @@ fn build_overseer( .unwrap() .clone(), state.pvd.clone(), - state.node_group.clone(), + state.own_backing_group.clone(), ); let (statement_req_receiver, statement_req_cfg) = IncomingRequest::get_config_receiver::< Block, @@ -131,19 +133,26 @@ fn build_overseer( Metrics::try_register(&dependencies.registry).unwrap(), rand::rngs::StdRng::from_entropy(), ); + let network_bridge_tx = MockNetworkBridgeTx::new( + network, + network_interface.subsystem_sender(), + state.test_authorities.clone(), + ); + let network_bridge_rx = MockNetworkBridgeRx::new(network_receiver, Some(candidate_req_cfg)); + let dummy = dummy_builder!(spawn_task_handle, overseer_metrics) .replace_runtime_api(|_| mock_runtime_api) .replace_chain_api(|_| mock_chain_api) .replace_prospective_parachains(|_| mock_prospective_parachains) .replace_candidate_backing(|_| mock_candidate_backing) .replace_statement_distribution(|_| subsystem) - .replace_network_bridge_tx(|_| network_bridge.0) - .replace_network_bridge_rx(|_| network_bridge.1); + .replace_network_bridge_tx(|_| network_bridge_tx) + .replace_network_bridge_rx(|_| network_bridge_rx); let (overseer, raw_handle) = dummy.build_with_connector(overseer_connector).expect("Should not fail"); let overseer_handle = OverseerHandle::new(raw_handle); - (overseer, overseer_handle, vec![statement_req_cfg, candidate_req_cfg], rx) + (overseer, overseer_handle, vec![statement_req_cfg], rx) } pub fn prepare_test( @@ -157,14 +166,8 @@ pub fn prepare_test( &state.test_authorities, vec![Arc::new(state.clone())], ); - let network_bridge_tx = MockNetworkBridgeTx::new( - network.clone(), - network_interface.subsystem_sender(), - state.test_authorities.clone(), - ); - let network_bridge_rx = MockNetworkBridgeRx::new(network_receiver, None); let (overseer, overseer_handle, cfg, to_subsystems) = - build_overseer(state, (network_bridge_tx, network_bridge_rx), &dependencies); + build_overseer(state, network.clone(), network_interface, network_receiver, &dependencies); ( TestEnvironment::new( diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index e1f0549d7fa9..a826b43948ef 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -18,13 +18,17 @@ use crate::{ configuration::{TestAuthorities, TestConfiguration}, mock::runtime_api::session_info_for_peers, network::{HandleNetworkMessage, NetworkMessage}, - NODE_UNDER_TEST, PEER_IN_NODE_GROUP, + NODE_UNDER_TEST, }; use bitvec::vec::BitVec; +use futures::channel::oneshot; use itertools::Itertools; -use parity_scale_codec::Encode; +use parity_scale_codec::{Decode, Encode}; use polkadot_node_network_protocol::{ - request_response::{v2::AttestedCandidateResponse, Requests}, + request_response::{ + v2::{AttestedCandidateRequest, AttestedCandidateResponse}, + Requests, + }, v3::{ BackedCandidateAcknowledgement, StatementDistributionMessage, StatementFilter, ValidationProtocol, @@ -44,7 +48,7 @@ use polkadot_primitives::{ use polkadot_primitives_test_helpers::{ dummy_committed_candidate_receipt, dummy_hash, dummy_head_data, dummy_pvd, }; -use sc_network::ProtocolName; +use sc_network::{config::IncomingRequest, ProtocolName}; use sp_core::{Pair, H256}; use std::{ collections::HashMap, @@ -75,14 +79,14 @@ pub struct TestState { pub manifests_tracker: HashMap>, pub session_info: SessionInfo, pub statements: HashMap>>, - pub node_group: Vec, + pub own_backing_group: Vec, } impl TestState { pub fn new(config: &TestConfiguration) -> Self { let test_authorities = config.generate_authorities(); let session_info = session_info_for_peers(config, &test_authorities); - let node_group = session_info + let own_backing_group = session_info .validator_groups .iter() .find(|g| g.contains(&ValidatorIndex(NODE_UNDER_TEST))) @@ -99,7 +103,7 @@ impl TestState { statements_tracker: Default::default(), manifests_tracker: Default::default(), session_info, - node_group, + own_backing_group, statements: Default::default(), }; @@ -348,16 +352,50 @@ impl HandleNetworkMessage for TestState { .position(|v| v == &authority_id) .expect("Should exist"); + let backing_group = + self.session_info.validator_groups.get(manifest.group_index).unwrap(); + let group_size = backing_group.len(); + let is_own_backing_group = backing_group.contains(&ValidatorIndex(NODE_UNDER_TEST)); + let mut seconded_in_group = + BitVec::from_iter((0..group_size).map(|_| !is_own_backing_group)); + let mut validated_in_group = BitVec::from_iter((0..group_size).map(|_| false)); + + if is_own_backing_group { + let (pending_response, response_receiver) = oneshot::channel(); + let peer_id = self.test_authorities.peer_ids.get(index).unwrap().to_owned(); + node_sender + .start_send(NetworkMessage::RequestFromPeer(IncomingRequest { + peer: peer_id, + payload: AttestedCandidateRequest { + candidate_hash: manifest.candidate_hash, + mask: StatementFilter::blank(self.own_backing_group.len()), + } + .encode(), + pending_response, + })) + .unwrap(); + + let response = response_receiver.await.unwrap(); + let response = + AttestedCandidateResponse::decode(&mut response.result.unwrap().as_ref()) + .unwrap(); + + for statement in response.statements { + let validator_index = statement.unchecked_validator_index(); + let position_in_group = + backing_group.iter().position(|v| *v == validator_index).unwrap(); + match statement.unchecked_payload() { + CompactStatement::Seconded(_) => + seconded_in_group.set(position_in_group, true), + CompactStatement::Valid(_) => + validated_in_group.set(position_in_group, true), + } + } + } + let ack = BackedCandidateAcknowledgement { candidate_hash: manifest.candidate_hash, - statement_knowledge: StatementFilter { - seconded_in_group: BitVec::from_iter( - (0..self.node_group.len()).map(|v| v == PEER_IN_NODE_GROUP as usize), - ), - validated_in_group: BitVec::from_iter( - (0..self.node_group.len()).map(|v| v == NODE_UNDER_TEST as usize), - ), - }, + statement_knowledge: StatementFilter { seconded_in_group, validated_in_group }, }; node_sender .start_send(NetworkMessage::MessageFromPeer( From b8ac6396dab3e093e9f5499449e46ecbccf7248b Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 16:22:17 +0200 Subject: [PATCH 23/39] Update var names --- .../src/lib/mock/candidate_backing.rs | 18 ++++++++++-------- .../subsystem-bench/src/lib/statement/mod.rs | 6 +++--- .../src/lib/statement/test_state.rs | 6 +++--- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs index ee0c8ff4d066..5feb269a1eec 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs @@ -62,7 +62,7 @@ impl MockCandidateBacking { #[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] impl MockCandidateBacking { async fn run(mut self, mut ctx: Context) { - let mut tracker: HashMap = Default::default(); + let mut statements_tracker: HashMap = Default::default(); loop { let msg = ctx.recv().await.expect("Overseer never fails us"); @@ -84,15 +84,16 @@ impl MockCandidateBacking { match statement.payload() { StatementWithPVD::Seconded(receipt, _pvd) => { let candidate_hash = receipt.hash(); - tracker + statements_tracker .entry(candidate_hash) .and_modify(|v| { *v += 1; }) .or_insert(1); - let received = *tracker.get(&candidate_hash).unwrap(); - if received == 1 && is_from_node_group { + let statements_received_count = + *statements_tracker.get(&candidate_hash).unwrap(); + if statements_received_count == 1 && is_from_node_group { let statement = Statement::Valid(candidate_hash); let context = SigningContext { parent_hash: relay_parent, @@ -117,7 +118,7 @@ impl MockCandidateBacking { let _ = self.to_subsystems.send(message).await; } - if received == 2 { + if statements_received_count == 2 { let message = AllMessages::StatementDistribution( polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(candidate_hash), ); @@ -125,15 +126,16 @@ impl MockCandidateBacking { } }, StatementWithPVD::Valid(candidate_hash) => { - tracker + statements_tracker .entry(*candidate_hash) .and_modify(|v| { *v += 1; }) .or_insert(1); - let received = *tracker.get(candidate_hash).unwrap(); - if received == 2 { + let statements_received_count = + *statements_tracker.get(candidate_hash).unwrap(); + if statements_received_count == 2 { let message = AllMessages::StatementDistribution( polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(*candidate_hash), ); diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 8b5db18089cf..3473a8fdb148 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -378,11 +378,11 @@ pub async fn benchmark_statement_distribution( .chain(two_hop_x_initiators) .chain(two_hop_y_initiators) { - let sent = message_tracker.get_mut(group_index).unwrap(); - if *sent { + let messages_sent_count = message_tracker.get_mut(group_index).unwrap(); + if *messages_sent_count { continue } - *sent = true; + *messages_sent_count = true; let candidate_hash = state .candidate_receipts diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index a826b43948ef..b37611cb5137 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -293,7 +293,7 @@ impl HandleNetworkMessage for TestState { .expect("Should exist"); let candidate_hash = *statement.unchecked_payload().candidate_hash(); - let sent = self + let statements_sent_count = self .statements_tracker .get(&candidate_hash) .unwrap() @@ -301,10 +301,10 @@ impl HandleNetworkMessage for TestState { .unwrap() .as_ref(); - if sent.load(Ordering::SeqCst) { + if statements_sent_count.load(Ordering::SeqCst) { return None } else { - sent.store(true, Ordering::SeqCst); + statements_sent_count.store(true, Ordering::SeqCst); } let group_statements = self.statements.get(&candidate_hash).unwrap(); From 45a86ceef77c30b66c4a0e3bb47ae00b0e045827 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 16:51:19 +0200 Subject: [PATCH 24/39] Use context to send messages --- ...statement-distribution-regression-bench.rs | 3 +- .../src/cli/subsystem-bench.rs | 4 +- .../src/lib/mock/candidate_backing.rs | 28 +++++------ .../subsystem-bench/src/lib/statement/mod.rs | 48 ++++++------------- 4 files changed, 28 insertions(+), 55 deletions(-) diff --git a/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs b/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs index 29990f0c3a30..e6936fe18746 100644 --- a/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs +++ b/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs @@ -43,12 +43,11 @@ fn main() -> Result<(), String> { .map(|n| { print!("\r[{}{}]", "#".repeat(n), "_".repeat(BENCH_COUNT - n)); std::io::stdout().flush().unwrap(); - let (mut env, _cfgs, to_subsystems) = prepare_test(&state, false); + let (mut env, _cfgs) = prepare_test(&state, false); env.runtime().block_on(benchmark_statement_distribution( "statement-distribution", &mut env, &state, - to_subsystems, )) }) .collect(); diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index 632f9b72a2ff..e081ce2999ff 100644 --- a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -175,13 +175,11 @@ impl BenchCli { }, TestObjective::StatementDistribution => { let state = statement::TestState::new(&test_config); - let (mut env, _protocol_config, to_subsystems) = - statement::prepare_test(&state, true); + let (mut env, _protocol_config) = statement::prepare_test(&state, true); env.runtime().block_on(statement::benchmark_statement_distribution( &benchmark_name, &mut env, &state, - to_subsystems, )) }, }; diff --git a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs index 5feb269a1eec..f6e05632dcbc 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs @@ -17,8 +17,7 @@ //! A generic candidate backing subsystem mockup suitable to be used in benchmarks. use crate::NODE_UNDER_TEST; -use futures::{channel::mpsc::UnboundedSender, FutureExt, SinkExt}; -use overseer::AllMessages; +use futures::FutureExt; use polkadot_node_primitives::{SignedFullStatementWithPVD, Statement, StatementWithPVD}; use polkadot_node_subsystem::{ messages::CandidateBackingMessage, overseer, SpawnedSubsystem, SubsystemError, @@ -33,7 +32,6 @@ use std::collections::HashMap; const LOG_TARGET: &str = "subsystem-bench::candidate-backing-mock"; pub struct MockCandidateBacking { - to_subsystems: UnboundedSender, pair: ValidatorPair, pvd: PersistedValidationData, node_group: Vec, @@ -41,12 +39,11 @@ pub struct MockCandidateBacking { impl MockCandidateBacking { pub fn new( - to_subsystems: UnboundedSender, pair: ValidatorPair, pvd: PersistedValidationData, node_group: Vec, ) -> Self { - Self { to_subsystems, pair, pvd, node_group } + Self { pair, pvd, node_group } } } @@ -61,7 +58,7 @@ impl MockCandidateBacking { #[overseer::contextbounds(CandidateBacking, prefix = self::overseer)] impl MockCandidateBacking { - async fn run(mut self, mut ctx: Context) { + async fn run(self, mut ctx: Context) { let mut statements_tracker: HashMap = Default::default(); loop { @@ -102,7 +99,7 @@ impl MockCandidateBacking { let payload = statement.to_compact().signing_payload(&context); let signature = self.pair.sign(&payload[..]); - let message = AllMessages::StatementDistribution( + let message = polkadot_node_subsystem::messages::StatementDistributionMessage::Share( relay_parent, SignedFullStatementWithPVD::new( @@ -112,17 +109,16 @@ impl MockCandidateBacking { &context, &self.pair.public(), ) - .unwrap(), - ) + .unwrap() ); - let _ = self.to_subsystems.send(message).await; + ctx.send_message(message).await; } if statements_received_count == 2 { - let message = AllMessages::StatementDistribution( - polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(candidate_hash), + let message = + polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(candidate_hash ); - let _ = self.to_subsystems.send(message).await; + ctx.send_message(message).await; } }, StatementWithPVD::Valid(candidate_hash) => { @@ -136,10 +132,10 @@ impl MockCandidateBacking { let statements_received_count = *statements_tracker.get(candidate_hash).unwrap(); if statements_received_count == 2 { - let message = AllMessages::StatementDistribution( - polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(*candidate_hash), + let message = + polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(*candidate_hash ); - let _ = self.to_subsystems.send(message).await; + ctx.send_message(message).await; } }, } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 3473a8fdb148..709e6df597df 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -32,7 +32,6 @@ use crate::{ }; use bitvec::vec::BitVec; use colored::Colorize; -use futures::{channel::mpsc, FutureExt, StreamExt}; use itertools::Itertools; use polkadot_node_metrics::metrics::Metrics; use polkadot_node_network_protocol::{ @@ -88,7 +87,6 @@ fn build_overseer( Overseer, AlwaysSupportsParachains>, OverseerHandle, Vec, - mpsc::UnboundedReceiver, ) { let overseer_connector = OverseerConnector::with_event_capacity(64000); let overseer_metrics = OverseerMetrics::try_register(&dependencies.registry).unwrap(); @@ -105,9 +103,7 @@ fn build_overseer( let chain_api_state = ChainApiState { block_headers: state.block_headers.clone() }; let mock_chain_api = MockChainApi::new(chain_api_state); let mock_prospective_parachains = MockProspectiveParachains::new(); - let (tx, rx) = mpsc::unbounded(); let mock_candidate_backing = MockCandidateBacking::new( - tx, state .test_authorities .validator_pairs @@ -152,13 +148,13 @@ fn build_overseer( dummy.build_with_connector(overseer_connector).expect("Should not fail"); let overseer_handle = OverseerHandle::new(raw_handle); - (overseer, overseer_handle, vec![statement_req_cfg], rx) + (overseer, overseer_handle, vec![statement_req_cfg]) } pub fn prepare_test( state: &TestState, with_prometheus_endpoint: bool, -) -> (TestEnvironment, Vec, mpsc::UnboundedReceiver) { +) -> (TestEnvironment, Vec) { let dependencies = TestEnvironmentDependencies::default(); let (network, network_interface, network_receiver) = new_network( &state.config, @@ -166,7 +162,7 @@ pub fn prepare_test( &state.test_authorities, vec![Arc::new(state.clone())], ); - let (overseer, overseer_handle, cfg, to_subsystems) = + let (overseer, overseer_handle, cfg) = build_overseer(state, network.clone(), network_interface, network_receiver, &dependencies); ( @@ -180,7 +176,6 @@ pub fn prepare_test( with_prometheus_endpoint, ), cfg, - to_subsystems, ) } @@ -232,7 +227,6 @@ pub async fn benchmark_statement_distribution( benchmark_name: &str, env: &mut TestEnvironment, state: &TestState, - mut to_subsystems: mpsc::UnboundedReceiver, ) -> BenchmarkUsage { state.reset_trackers(); @@ -419,29 +413,19 @@ pub async fn benchmark_statement_distribution( total_message_count += message_tracker.iter().filter(|&&v| v).collect_vec().len(); - let mut timeout = message_check_delay(); loop { - futures::select! { - msg = to_subsystems.next() => { - if let Some(msg) = msg { - env.send_message(msg).await; - } - }, - _ = timeout => { - let manifest_count = state - .manifests_tracker - .values() - .filter(|v| v.load(Ordering::SeqCst)) - .collect::>() - .len(); - gum::debug!(target: LOG_TARGET, "{}/{} manifest exchanges", manifest_count, total_message_count); - if manifest_count == total_message_count { - break; - } else { - timeout = message_check_delay(); - } - } + let manifest_count = state + .manifests_tracker + .values() + .filter(|v| v.load(Ordering::SeqCst)) + .collect::>() + .len(); + gum::debug!(target: LOG_TARGET, "{}/{} manifest exchanges", manifest_count, total_message_count); + + if manifest_count == total_message_count { + break; } + tokio::time::sleep(Duration::from_millis(50)).await; } } @@ -455,7 +439,3 @@ pub async fn benchmark_statement_distribution( env.stop().await; env.collect_resource_usage(benchmark_name, &["statement-distribution"]) } - -fn message_check_delay() -> futures::prelude::future::Fuse { - futures_timer::Delay::new(Duration::from_millis(50)).fuse() -} From 6f225a9ced4947cf1f6022072496186f2ba44ec9 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 16:59:12 +0200 Subject: [PATCH 25/39] Add minimum_backing_votes to config --- .../subsystem-bench/src/lib/configuration.rs | 7 +++++++ .../src/lib/mock/candidate_backing.rs | 18 +++++++++++++----- .../src/lib/mock/runtime_api.rs | 2 +- .../subsystem-bench/src/lib/statement/mod.rs | 1 + 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/configuration.rs b/polkadot/node/subsystem-bench/src/lib/configuration.rs index 58a379a1758b..7c42e7e61aab 100644 --- a/polkadot/node/subsystem-bench/src/lib/configuration.rs +++ b/polkadot/node/subsystem-bench/src/lib/configuration.rs @@ -90,6 +90,9 @@ fn default_n_delay_tranches() -> usize { fn default_no_show_slots() -> usize { 3 } +fn default_minimum_backing_votes() -> u32 { + 2 +} /// The test input parameters #[derive(Clone, Debug, Serialize, Deserialize)] @@ -138,6 +141,9 @@ pub struct TestConfiguration { pub connectivity: usize, /// Number of blocks to run the test for pub num_blocks: usize, + /// Number of minimum backing votes + #[serde(default = "default_minimum_backing_votes")] + pub minimum_backing_votes: u32, } impl Default for TestConfiguration { @@ -159,6 +165,7 @@ impl Default for TestConfiguration { latency: default_peer_latency(), connectivity: default_connectivity(), num_blocks: Default::default(), + minimum_backing_votes: default_minimum_backing_votes(), } } } diff --git a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs index f6e05632dcbc..b11315467d2b 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs @@ -16,7 +16,7 @@ //! A generic candidate backing subsystem mockup suitable to be used in benchmarks. -use crate::NODE_UNDER_TEST; +use crate::{configuration::TestConfiguration, NODE_UNDER_TEST}; use futures::FutureExt; use polkadot_node_primitives::{SignedFullStatementWithPVD, Statement, StatementWithPVD}; use polkadot_node_subsystem::{ @@ -32,6 +32,7 @@ use std::collections::HashMap; const LOG_TARGET: &str = "subsystem-bench::candidate-backing-mock"; pub struct MockCandidateBacking { + config: TestConfiguration, pair: ValidatorPair, pvd: PersistedValidationData, node_group: Vec, @@ -39,11 +40,12 @@ pub struct MockCandidateBacking { impl MockCandidateBacking { pub fn new( + config: TestConfiguration, pair: ValidatorPair, pvd: PersistedValidationData, node_group: Vec, ) -> Self { - Self { pair, pvd, node_group } + Self { config, pair, pvd, node_group } } } @@ -90,7 +92,9 @@ impl MockCandidateBacking { let statements_received_count = *statements_tracker.get(&candidate_hash).unwrap(); - if statements_received_count == 1 && is_from_node_group { + if statements_received_count == + (self.config.minimum_backing_votes - 1) && is_from_node_group + { let statement = Statement::Valid(candidate_hash); let context = SigningContext { parent_hash: relay_parent, @@ -114,7 +118,9 @@ impl MockCandidateBacking { ctx.send_message(message).await; } - if statements_received_count == 2 { + if statements_received_count == + self.config.minimum_backing_votes + { let message = polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(candidate_hash ); @@ -131,7 +137,9 @@ impl MockCandidateBacking { let statements_received_count = *statements_tracker.get(candidate_hash).unwrap(); - if statements_received_count == 2 { + if statements_received_count == + self.config.minimum_backing_votes + { let message = polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(*candidate_hash ); diff --git a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs index 77b759a5575e..d7ff075d0609 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs @@ -268,7 +268,7 @@ impl MockRuntimeApi { _parent, RuntimeApiRequest::MinimumBackingVotes(_session_index, tx), ) => { - tx.send(Ok(2)).unwrap(); + tx.send(Ok(self.config.minimum_backing_votes)).unwrap(); }, RuntimeApiMessage::Request( _parent, diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 709e6df597df..615a1cc0338a 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -104,6 +104,7 @@ fn build_overseer( let mock_chain_api = MockChainApi::new(chain_api_state); let mock_prospective_parachains = MockProspectiveParachains::new(); let mock_candidate_backing = MockCandidateBacking::new( + state.config.clone(), state .test_authorities .validator_pairs From 2b5944f9080f672dca6d2c8a4bf3af629c5abae0 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 17:07:43 +0200 Subject: [PATCH 26/39] Remove todos --- .../node/subsystem-bench/src/cli/subsystem-bench.rs | 2 +- .../subsystem-bench/src/lib/statement/test_state.rs | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs index e081ce2999ff..1e921500a4d2 100644 --- a/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs +++ b/polkadot/node/subsystem-bench/src/cli/subsystem-bench.rs @@ -40,7 +40,7 @@ pub enum TestObjective { DataAvailabilityWrite, /// Benchmark the approval-voting and approval-distribution subsystems. ApprovalVoting(approval::ApprovalsOptions), - // TODO + // Benchmark the statement-distribution subsystem StatementDistribution, } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index b37611cb5137..26e3e5c43ee3 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -70,16 +70,20 @@ pub struct TestState { pub candidate_receipts: HashMap>, // Map from generated commited candidate receipts pub commited_candidate_receipts: HashMap>, - // TODO + // PersistedValidationData, we use one for all candidates pub pvd: PersistedValidationData, // Relay chain block headers pub block_headers: HashMap, - // TODO - pub statements_tracker: HashMap>>, - pub manifests_tracker: HashMap>, + // Session info pub session_info: SessionInfo, + // Pregenerated statements pub statements: HashMap>>, + // Indices in the backing group where the node under test is pub own_backing_group: Vec, + // Tracks how many statements we received for a candidates + pub statements_tracker: HashMap>>, + // Tracks if manifest exchange happened + pub manifests_tracker: HashMap>, } impl TestState { From a78eeb7536f4d814b8ca267e4506891a44c0f2a3 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 17:14:02 +0200 Subject: [PATCH 27/39] Remove redundant vars --- polkadot/node/subsystem-bench/src/lib/lib.rs | 3 --- .../node/subsystem-bench/src/lib/network.rs | 11 ++--------- .../subsystem-bench/src/lib/statement/mod.rs | 18 ++++++++++++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/lib.rs b/polkadot/node/subsystem-bench/src/lib/lib.rs index 5d7872531c3c..e18227af8be3 100644 --- a/polkadot/node/subsystem-bench/src/lib/lib.rs +++ b/polkadot/node/subsystem-bench/src/lib/lib.rs @@ -16,9 +16,6 @@ // The validator index that represents the node that is under test. pub const NODE_UNDER_TEST: u32 = 0; -// The validator index that represents the peer in same group as the node under test -// Always connected -pub const PEER_IN_NODE_GROUP: u32 = 1; pub mod approval; pub mod availability; diff --git a/polkadot/node/subsystem-bench/src/lib/network.rs b/polkadot/node/subsystem-bench/src/lib/network.rs index 17b0aa609bdd..595e389662aa 100644 --- a/polkadot/node/subsystem-bench/src/lib/network.rs +++ b/polkadot/node/subsystem-bench/src/lib/network.rs @@ -36,7 +36,7 @@ use crate::{ configuration::{random_latency, TestAuthorities, TestConfiguration}, environment::TestEnvironmentDependencies, - NODE_UNDER_TEST, PEER_IN_NODE_GROUP, + NODE_UNDER_TEST, }; use colored::Colorize; use futures::{ @@ -849,15 +849,8 @@ pub fn new_network( peers_indices.partial_shuffle(&mut thread_rng(), connected_count); // Node under test is always mark as disconnected. - let mut skip = 1; peers[NODE_UNDER_TEST as usize].disconnect(); - if to_disconnect.contains(&(PEER_IN_NODE_GROUP as usize)) { - skip -= 1; - } - for peer in to_disconnect.iter().skip(skip) { - if *peer == PEER_IN_NODE_GROUP as usize { - continue - } + for peer in to_disconnect.iter().skip(1) { peers[*peer].disconnect(); } diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 615a1cc0338a..36db71a3aa18 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -28,7 +28,7 @@ use crate::{ }, network::{new_network, NetworkEmulatorHandle, NetworkInterface, NetworkInterfaceReceiver}, usage::BenchmarkUsage, - NODE_UNDER_TEST, PEER_IN_NODE_GROUP, + NODE_UNDER_TEST, }; use bitvec::vec::BitVec; use colored::Colorize; @@ -239,6 +239,13 @@ pub async fn benchmark_statement_distribution( .filter_map(|(i, id)| if env.network().is_peer_connected(id) { Some(i) } else { None }) .collect_vec(); + let seconding_validator_in_own_backing_group = state + .own_backing_group + .iter() + .find(|v| connected_validators.contains(&(v.0 as usize))) + .unwrap() + .to_owned(); + let config = env.config().clone(); let groups = state.session_info.validator_groups.clone(); let node_group_index = groups @@ -275,15 +282,18 @@ pub async fn benchmark_statement_distribution( env.send_message(update).await; } - let seconding_peer_id = - *state.test_authorities.peer_ids.get(PEER_IN_NODE_GROUP as usize).unwrap(); + let seconding_peer_id = *state + .test_authorities + .peer_ids + .get(seconding_validator_in_own_backing_group.0 as usize) + .unwrap(); let candidate = state.candidate_receipts.get(&block_info.hash).unwrap().first().unwrap(); let candidate_hash = candidate.hash(); let statement = state .statements .get(&candidate_hash) .unwrap() - .get(PEER_IN_NODE_GROUP as usize) + .get(seconding_validator_in_own_backing_group.0 as usize) .unwrap() .clone(); let message = AllMessages::StatementDistribution( From ba526b6d228dd722bc42c2e260a72f8b1117e87c Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 17:19:37 +0200 Subject: [PATCH 28/39] Remove redundant Debug --- polkadot/node/subsystem-bench/src/lib/network.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/network.rs b/polkadot/node/subsystem-bench/src/lib/network.rs index 595e389662aa..a2c7076898dd 100644 --- a/polkadot/node/subsystem-bench/src/lib/network.rs +++ b/polkadot/node/subsystem-bench/src/lib/network.rs @@ -149,7 +149,6 @@ impl RateLimit { /// A wrapper for both gossip and request/response protocols along with the destination /// peer(`AuthorityDiscoveryId``). -#[derive(Debug)] pub enum NetworkMessage { /// A gossip message from peer to node. MessageFromPeer(PeerId, VersionedValidationProtocol), @@ -432,7 +431,7 @@ impl NetworkInterface { } /// A handle for controlling an emulated peer. -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct EmulatedPeerHandle { /// Send messages to be processed by the peer. messages_tx: UnboundedSender, @@ -696,7 +695,7 @@ impl PeerEmulatorStats { } /// The state of a peer on the emulated network. -#[derive(Clone, Debug)] +#[derive(Clone)] enum Peer { Connected(EmulatedPeerHandle), Disconnected(EmulatedPeerHandle), From 50cd03a1ce4eea27d9d1b6f95fa6854a22d4cb73 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 17:44:22 +0200 Subject: [PATCH 29/39] Polish --- .../src/lib/mock/candidate_backing.rs | 18 +++--- .../subsystem-bench/src/lib/statement/mod.rs | 57 ++++++++----------- .../src/lib/statement/test_state.rs | 18 +++--- 3 files changed, 41 insertions(+), 52 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs index b11315467d2b..3047fc91b8b5 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs @@ -35,7 +35,7 @@ pub struct MockCandidateBacking { config: TestConfiguration, pair: ValidatorPair, pvd: PersistedValidationData, - node_group: Vec, + own_backing_group: Vec, } impl MockCandidateBacking { @@ -43,9 +43,9 @@ impl MockCandidateBacking { config: TestConfiguration, pair: ValidatorPair, pvd: PersistedValidationData, - node_group: Vec, + own_backing_group: Vec, ) -> Self { - Self { config, pair, pvd, node_group } + Self { config, pair, pvd, own_backing_group } } } @@ -74,12 +74,10 @@ impl MockCandidateBacking { gum::trace!(target: LOG_TARGET, msg=?msg, "recv message"); match msg { - // If a statement is not from a node group - ignore it. - // For the first seconded send Share - // For the seconde statement send Backed CandidateBackingMessage::Statement(relay_parent, statement) => { let validator_id = statement.validator_index(); - let is_from_node_group = self.node_group.contains(&validator_id); + let is_own_backing_group = + self.own_backing_group.contains(&validator_id); match statement.payload() { StatementWithPVD::Seconded(receipt, _pvd) => { let candidate_hash = receipt.hash(); @@ -93,7 +91,8 @@ impl MockCandidateBacking { let statements_received_count = *statements_tracker.get(&candidate_hash).unwrap(); if statements_received_count == - (self.config.minimum_backing_votes - 1) && is_from_node_group + (self.config.minimum_backing_votes - 1) && + is_own_backing_group { let statement = Statement::Valid(candidate_hash); let context = SigningContext { @@ -102,14 +101,13 @@ impl MockCandidateBacking { }; let payload = statement.to_compact().signing_payload(&context); - let signature = self.pair.sign(&payload[..]); let message = polkadot_node_subsystem::messages::StatementDistributionMessage::Share( relay_parent, SignedFullStatementWithPVD::new( statement.supply_pvd(self.pvd.clone()), ValidatorIndex(NODE_UNDER_TEST), - signature, + self.pair.sign(&payload[..]), &context, &self.pair.public(), ) diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 36db71a3aa18..3a2b05ec17e2 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -145,8 +145,7 @@ fn build_overseer( .replace_statement_distribution(|_| subsystem) .replace_network_bridge_tx(|_| network_bridge_tx) .replace_network_bridge_rx(|_| network_bridge_rx); - let (overseer, raw_handle) = - dummy.build_with_connector(overseer_connector).expect("Should not fail"); + let (overseer, raw_handle) = dummy.build_with_connector(overseer_connector).unwrap(); let overseer_handle = OverseerHandle::new(raw_handle); (overseer, overseer_handle, vec![statement_req_cfg]) @@ -238,7 +237,6 @@ pub async fn benchmark_statement_distribution( .enumerate() .filter_map(|(i, id)| if env.network().is_peer_connected(id) { Some(i) } else { None }) .collect_vec(); - let seconding_validator_in_own_backing_group = state .own_backing_group .iter() @@ -248,38 +246,35 @@ pub async fn benchmark_statement_distribution( let config = env.config().clone(); let groups = state.session_info.validator_groups.clone(); - let node_group_index = groups + let own_backing_group_index = groups .iter() .position(|group| group.iter().any(|v| v.0 == NODE_UNDER_TEST)) - .expect("Pregenerated"); + .unwrap(); env.metrics().set_n_validators(config.n_validators); env.metrics().set_n_cores(config.n_cores); - // First create the initialization messages that make sure that then node under - // tests receives notifications about the topology used and the connected peers. let topology = generate_topology(&state.test_authorities); - let mut initialization_messages = - env.network().generate_statement_distribution_peer_connected(); - initialization_messages - .extend(generate_new_session_topology(&topology, ValidatorIndex(NODE_UNDER_TEST))); - for message in initialization_messages { + let peer_connected_messages = env.network().generate_statement_distribution_peer_connected(); + let new_session_topology_messages = + generate_new_session_topology(&topology, ValidatorIndex(NODE_UNDER_TEST)); + for message in peer_connected_messages.into_iter().chain(new_session_topology_messages) { env.send_message(message).await; } let test_start = Instant::now(); - let mut total_message_count = 0; + let mut total_messages_count = 0; for block_info in state.block_infos.iter() { let block_num = block_info.number as usize; - gum::info!(target: LOG_TARGET, "Current block {}/{} {}", block_num, config.num_blocks, block_info.hash); + gum::info!(target: LOG_TARGET, "Current block {}/{} {:?}", block_num, config.num_blocks, block_info.hash); env.metrics().set_current_block(block_num); env.import_block(block_info.clone()).await; - for update in env + for peer_view_change in env .network() .generate_statement_distribution_peer_view_change(view![block_info.hash]) { - env.send_message(update).await; + env.send_message(peer_view_change).await; } let seconding_peer_id = *state @@ -306,27 +301,25 @@ pub async fn benchmark_statement_distribution( )), ); env.send_message(message).await; - // Just sent for the node group - let mut message_tracker = (0..groups.len()).map(|i| i == node_group_index).collect_vec(); + // One was just sent for the own backing group + let mut messages_tracker = + (0..groups.len()).map(|i| i == own_backing_group_index).collect_vec(); let neighbors = topology.compute_grid_neighbors_for(ValidatorIndex(NODE_UNDER_TEST)).unwrap(); - let connected_neighbors_x = neighbors .validator_indices_x .iter() .filter(|&v| connected_validators.contains(&(v.0 as usize))) .cloned() .collect_vec(); - let connected_neighbors_y = neighbors .validator_indices_y .iter() .filter(|&v| connected_validators.contains(&(v.0 as usize))) .cloned() .collect_vec(); - - let one_hop_initiators = connected_neighbors_x + let one_hop_peers_and_groups = connected_neighbors_x .iter() .chain(connected_neighbors_y.iter()) .map(|validator_index| { @@ -337,7 +330,7 @@ pub async fn benchmark_statement_distribution( (peer_id, group_index) }) .collect_vec(); - let two_hop_x_initiators = connected_neighbors_x + let two_hop_x_peers_and_groups = connected_neighbors_x .iter() .flat_map(|validator_index| { let peer_id = @@ -357,7 +350,7 @@ pub async fn benchmark_statement_distribution( .collect_vec() }) .collect_vec(); - let two_hop_y_initiators = connected_neighbors_y + let two_hop_y_peers_and_groups = connected_neighbors_y .iter() .flat_map(|validator_index| { let peer_id = @@ -378,12 +371,12 @@ pub async fn benchmark_statement_distribution( }) .collect_vec(); - for (seconding_peer_id, group_index) in one_hop_initiators + for (seconding_peer_id, group_index) in one_hop_peers_and_groups .into_iter() - .chain(two_hop_x_initiators) - .chain(two_hop_y_initiators) + .chain(two_hop_x_peers_and_groups) + .chain(two_hop_y_peers_and_groups) { - let messages_sent_count = message_tracker.get_mut(group_index).unwrap(); + let messages_sent_count = messages_tracker.get_mut(group_index).unwrap(); if *messages_sent_count { continue } @@ -422,18 +415,18 @@ pub async fn benchmark_statement_distribution( env.send_message(message).await; } - total_message_count += message_tracker.iter().filter(|&&v| v).collect_vec().len(); + total_messages_count += messages_tracker.iter().filter(|&&v| v).collect_vec().len(); loop { - let manifest_count = state + let manifests_count = state .manifests_tracker .values() .filter(|v| v.load(Ordering::SeqCst)) .collect::>() .len(); - gum::debug!(target: LOG_TARGET, "{}/{} manifest exchanges", manifest_count, total_message_count); + gum::debug!(target: LOG_TARGET, "{}/{} manifest exchanges", manifests_count, total_messages_count); - if manifest_count == total_message_count { + if manifests_count == total_messages_count { break; } tokio::time::sleep(Duration::from_millis(50)).await; diff --git a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs index 26e3e5c43ee3..b8ea64c7e331 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/test_state.rs @@ -136,7 +136,7 @@ impl TestState { ); state.statements_tracker.entry(receipt.hash()).or_default().extend( (0..config.n_validators) - .map(|index| Arc::new(AtomicBool::new(index <= 1))) + .map(|_| Arc::new(AtomicBool::new(false))) .collect_vec(), ); state.manifests_tracker.insert(receipt.hash(), Arc::new(AtomicBool::new(false))); @@ -271,7 +271,7 @@ impl HandleNetworkMessage for TestState { .values() .flatten() .find(|v| v.hash() == payload.candidate_hash) - .expect("Pregenerated") + .unwrap() .clone(); let persisted_validation_data = self.pvd.clone(); let statements = self.statements.get(&payload.candidate_hash).unwrap().clone(); @@ -294,7 +294,7 @@ impl HandleNetworkMessage for TestState { .validator_authority_id .iter() .position(|v| v == &authority_id) - .expect("Should exist"); + .unwrap(); let candidate_hash = *statement.unchecked_payload().candidate_hash(); let statements_sent_count = self @@ -304,7 +304,6 @@ impl HandleNetworkMessage for TestState { .get(index) .unwrap() .as_ref(); - if statements_sent_count.load(Ordering::SeqCst) { return None } else { @@ -335,7 +334,7 @@ impl HandleNetworkMessage for TestState { node_sender .start_send(NetworkMessage::MessageFromPeer( - *self.test_authorities.peer_ids.get(index).expect("Must exist"), + *self.test_authorities.peer_ids.get(index).unwrap(), Versioned::V3(ValidationProtocol::StatementDistribution( StatementDistributionMessage::Statement(relay_parent, statement), )), @@ -354,8 +353,7 @@ impl HandleNetworkMessage for TestState { .validator_authority_id .iter() .position(|v| v == &authority_id) - .expect("Should exist"); - + .unwrap(); let backing_group = self.session_info.validator_groups.get(manifest.group_index).unwrap(); let group_size = backing_group.len(); @@ -403,7 +401,7 @@ impl HandleNetworkMessage for TestState { }; node_sender .start_send(NetworkMessage::MessageFromPeer( - *self.test_authorities.peer_ids.get(index).expect("Must exist"), + *self.test_authorities.peer_ids.get(index).unwrap(), Versioned::V3(ValidationProtocol::StatementDistribution( StatementDistributionMessage::BackedCandidateKnown(ack), )), @@ -412,7 +410,7 @@ impl HandleNetworkMessage for TestState { self.manifests_tracker .get(&manifest.candidate_hash) - .expect("Pregenerated") + .unwrap() .as_ref() .store(true, Ordering::SeqCst); @@ -426,7 +424,7 @@ impl HandleNetworkMessage for TestState { ) => { self.manifests_tracker .get(&ack.candidate_hash) - .expect("Pregenerated") + .unwrap() .as_ref() .store(true, Ordering::SeqCst); From 0ffe859b892e8a603b4ea7b8acc7921daaad54e0 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Thu, 16 May 2024 18:14:43 +0200 Subject: [PATCH 30/39] Fyx GetHypotheticalMembership --- .../subsystem-bench/src/lib/mock/prospective_parachains.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs index 943855a3431d..8a865af21a07 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/prospective_parachains.rs @@ -54,12 +54,12 @@ impl MockProspectiveParachains { ProspectiveParachainsMessage::GetMinimumRelayParents(_relay_parent, tx) => { tx.send(vec![]).unwrap(); }, - ProspectiveParachainsMessage::GetHypotheticalFrontier(req, tx) => { + ProspectiveParachainsMessage::GetHypotheticalMembership(req, tx) => { tx.send( req.candidates .iter() .cloned() - .map(|candidate| (candidate, vec![(Hash::repeat_byte(0), vec![0])])) + .map(|candidate| (candidate, vec![Hash::repeat_byte(0)])) .collect(), ) .unwrap(); From 24f26b0a2134be459050c75389a430aadd6c3e03 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Fri, 17 May 2024 10:36:29 +0200 Subject: [PATCH 31/39] Fix clippy --- polkadot/node/network/statement-distribution/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/polkadot/node/network/statement-distribution/src/lib.rs b/polkadot/node/network/statement-distribution/src/lib.rs index 4ca199c3378b..462f0629c92a 100644 --- a/polkadot/node/network/statement-distribution/src/lib.rs +++ b/polkadot/node/network/statement-distribution/src/lib.rs @@ -22,6 +22,10 @@ #![deny(unused_crate_dependencies)] #![warn(missing_docs)] +// clippy consider polkadot_subsystem_bench as unused_crate_dependencies +#[cfg(bench)] +use polkadot_subsystem_bench as _; + use error::{log_error, FatalResult}; use std::time::Duration; From e21238694bb633f34eb585dffe841ea23a1422a3 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Fri, 17 May 2024 14:48:22 +0200 Subject: [PATCH 32/39] Remove clippy rule --- polkadot/node/network/statement-distribution/src/lib.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/polkadot/node/network/statement-distribution/src/lib.rs b/polkadot/node/network/statement-distribution/src/lib.rs index 462f0629c92a..4d56c795f13b 100644 --- a/polkadot/node/network/statement-distribution/src/lib.rs +++ b/polkadot/node/network/statement-distribution/src/lib.rs @@ -19,13 +19,8 @@ //! This is responsible for distributing signed statements about candidate //! validity among validators. -#![deny(unused_crate_dependencies)] #![warn(missing_docs)] -// clippy consider polkadot_subsystem_bench as unused_crate_dependencies -#[cfg(bench)] -use polkadot_subsystem_bench as _; - use error::{log_error, FatalResult}; use std::time::Duration; From c5119d5d280dad686bbbba4f638836710b14d853 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Fri, 17 May 2024 15:03:26 +0200 Subject: [PATCH 33/39] Update baselines --- .../benches/statement-distribution-regression-bench.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs b/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs index e6936fe18746..a849bc20b6dc 100644 --- a/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs +++ b/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs @@ -64,10 +64,10 @@ fn main() -> Result<(), String> { // We expect no variance for received and sent // but use 0.001 because we operate with floats messages.extend(average_usage.check_network_usage(&[ - ("Received from peers", 106.0680, 0.001), - ("Sent to peers", 374.5640, 0.001), + ("Received from peers", 108.8000, 0.001), + ("Sent to peers", 123.8180, 0.001), ])); - messages.extend(average_usage.check_cpu_usage(&[("statement-distribution", 0.0320, 0.1)])); + messages.extend(average_usage.check_cpu_usage(&[("statement-distribution", 0.0390, 0.1)])); if messages.is_empty() { Ok(()) From 74d477f12d470789e0d446d822078f5fb7c5a09f Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Mon, 27 May 2024 12:07:19 +0200 Subject: [PATCH 34/39] Use generic peer connected generation --- .../subsystem-bench/src/lib/approval/mod.rs | 4 ++- .../node/subsystem-bench/src/lib/network.rs | 36 +++++-------------- .../subsystem-bench/src/lib/statement/mod.rs | 4 ++- 3 files changed, 14 insertions(+), 30 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs index 4a2c0304b940..4a479b6af29e 100644 --- a/polkadot/node/subsystem-bench/src/lib/approval/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/approval/mod.rs @@ -917,7 +917,9 @@ pub async fn bench_approvals_run( // First create the initialization messages that make sure that then node under // tests receives notifications about the topology used and the connected peers. - let mut initialization_messages = env.network().generate_approval_distribution_peer_connected(); + let mut initialization_messages = env.network().generate_peer_connected(|e| { + AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate(e)) + }); initialization_messages.extend(generate_new_session_topology( &state.test_authorities, ValidatorIndex(NODE_UNDER_TEST), diff --git a/polkadot/node/subsystem-bench/src/lib/network.rs b/polkadot/node/subsystem-bench/src/lib/network.rs index a2c7076898dd..9686f456b9e6 100644 --- a/polkadot/node/subsystem-bench/src/lib/network.rs +++ b/polkadot/node/subsystem-bench/src/lib/network.rs @@ -51,14 +51,14 @@ use futures::{ }; use itertools::Itertools; use net_protocol::{ - peer_set::{ProtocolVersion, ValidationVersion}, + peer_set::ValidationVersion, request_response::{Recipient, Requests, ResponseSender}, ObservedRole, VersionedValidationProtocol, View, }; use parity_scale_codec::Encode; use polkadot_node_network_protocol::{self as net_protocol, Versioned}; use polkadot_node_subsystem::messages::StatementDistributionMessage; -use polkadot_node_subsystem_types::messages::{ApprovalDistributionMessage, NetworkBridgeEvent}; +use polkadot_node_subsystem_types::messages::NetworkBridgeEvent; use polkadot_node_subsystem_util::metrics::prometheus::{ self, CounterVec, Opts, PrometheusError, Registry, }; @@ -759,41 +759,21 @@ impl NetworkEmulatorHandle { }) .collect_vec() } + /// Generates peer_connected messages for all peers in `test_authorities` - pub fn generate_statement_distribution_peer_connected(&self) -> Vec { + pub fn generate_peer_connected(&self, mapper: F) -> Vec + where + F: Fn(NetworkBridgeEvent) -> AllMessages, + { self.peers .iter() .filter(|peer| peer.is_connected()) .map(|peer| { - let network = NetworkBridgeEvent::PeerConnected( + mapper(NetworkBridgeEvent::PeerConnected( peer.handle().peer_id, ObservedRole::Authority, ValidationVersion::V3.into(), Some(vec![peer.authority_id()].into_iter().collect()), - ); - - AllMessages::StatementDistribution( - StatementDistributionMessage::NetworkBridgeUpdate(network), - ) - }) - .collect_vec() - } - - /// Generates peer_connected messages for all peers in `test_authorities` - pub fn generate_approval_distribution_peer_connected(&self) -> Vec { - self.peers - .iter() - .filter(|peer| peer.is_connected()) - .map(|peer| { - let network = NetworkBridgeEvent::PeerConnected( - peer.handle().peer_id, - ObservedRole::Full, - ProtocolVersion::from(ValidationVersion::V3), - None, - ); - - AllMessages::ApprovalDistribution(ApprovalDistributionMessage::NetworkBridgeUpdate( - network, )) }) .collect_vec() diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 3a2b05ec17e2..3cbb753c52ea 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -255,7 +255,9 @@ pub async fn benchmark_statement_distribution( env.metrics().set_n_cores(config.n_cores); let topology = generate_topology(&state.test_authorities); - let peer_connected_messages = env.network().generate_statement_distribution_peer_connected(); + let peer_connected_messages = env.network().generate_peer_connected(|e| { + AllMessages::StatementDistribution(StatementDistributionMessage::NetworkBridgeUpdate(e)) + }); let new_session_topology_messages = generate_new_session_topology(&topology, ValidatorIndex(NODE_UNDER_TEST)); for message in peer_connected_messages.into_iter().chain(new_session_topology_messages) { From 0405d9dd45662fba6a9b5a6f34755ef9a304841e Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Mon, 27 May 2024 12:13:01 +0200 Subject: [PATCH 35/39] Use MockCandidateBackingState --- .../src/lib/mock/candidate_backing.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs index 3047fc91b8b5..1c05920c0c98 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs @@ -31,13 +31,17 @@ use std::collections::HashMap; const LOG_TARGET: &str = "subsystem-bench::candidate-backing-mock"; -pub struct MockCandidateBacking { - config: TestConfiguration, +struct MockCandidateBackingState { pair: ValidatorPair, pvd: PersistedValidationData, own_backing_group: Vec, } +pub struct MockCandidateBacking { + config: TestConfiguration, + state: MockCandidateBackingState, +} + impl MockCandidateBacking { pub fn new( config: TestConfiguration, @@ -45,7 +49,7 @@ impl MockCandidateBacking { pvd: PersistedValidationData, own_backing_group: Vec, ) -> Self { - Self { config, pair, pvd, own_backing_group } + Self { config, state: MockCandidateBackingState { pair, pvd, own_backing_group } } } } @@ -77,7 +81,7 @@ impl MockCandidateBacking { CandidateBackingMessage::Statement(relay_parent, statement) => { let validator_id = statement.validator_index(); let is_own_backing_group = - self.own_backing_group.contains(&validator_id); + self.state.own_backing_group.contains(&validator_id); match statement.payload() { StatementWithPVD::Seconded(receipt, _pvd) => { let candidate_hash = receipt.hash(); @@ -105,11 +109,11 @@ impl MockCandidateBacking { polkadot_node_subsystem::messages::StatementDistributionMessage::Share( relay_parent, SignedFullStatementWithPVD::new( - statement.supply_pvd(self.pvd.clone()), + statement.supply_pvd(self.state.pvd.clone()), ValidatorIndex(NODE_UNDER_TEST), - self.pair.sign(&payload[..]), + self.state.pair.sign(&payload[..]), &context, - &self.pair.public(), + &self.state.pair.public(), ) .unwrap() ); From 8775d70e1c1c9fe818dd48837cffc46eedfe32f0 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Mon, 27 May 2024 12:17:00 +0200 Subject: [PATCH 36/39] Use AsyncBackingParams from Kusama --- polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs index d7ff075d0609..0c86153e94d5 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs @@ -250,8 +250,8 @@ impl MockRuntimeApi { RuntimeApiRequest::AsyncBackingParams(sender), ) => { let _ = sender.send(Ok(AsyncBackingParams { - max_candidate_depth: 1, - allowed_ancestry_len: 1, + max_candidate_depth: 3, + allowed_ancestry_len: 2, })); }, RuntimeApiMessage::Request(_parent, RuntimeApiRequest::Version(tx)) => { From c7abc3490554d603b2acc98a8b4820a6636a6970 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Mon, 27 May 2024 13:20:57 +0200 Subject: [PATCH 37/39] Extract handle_statement --- .../src/lib/mock/candidate_backing.rs | 150 ++++++++++-------- 1 file changed, 80 insertions(+), 70 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs index 1c05920c0c98..51494016e185 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/candidate_backing.rs @@ -24,7 +24,7 @@ use polkadot_node_subsystem::{ }; use polkadot_node_subsystem_types::OverseerSignal; use polkadot_primitives::{ - CandidateHash, PersistedValidationData, SigningContext, ValidatorIndex, ValidatorPair, + CandidateHash, Hash, PersistedValidationData, SigningContext, ValidatorIndex, ValidatorPair, }; use sp_core::Pair; use std::collections::HashMap; @@ -51,6 +51,78 @@ impl MockCandidateBacking { ) -> Self { Self { config, state: MockCandidateBackingState { pair, pvd, own_backing_group } } } + + fn handle_statement( + &self, + relay_parent: Hash, + statement: SignedFullStatementWithPVD, + statements_tracker: &mut HashMap, + ) -> Vec { + let mut messages = vec![]; + let validator_id = statement.validator_index(); + let is_own_backing_group = self.state.own_backing_group.contains(&validator_id); + + match statement.payload() { + StatementWithPVD::Seconded(receipt, _pvd) => { + let candidate_hash = receipt.hash(); + statements_tracker + .entry(candidate_hash) + .and_modify(|v| { + *v += 1; + }) + .or_insert(1); + + let statements_received_count = *statements_tracker.get(&candidate_hash).unwrap(); + if statements_received_count == (self.config.minimum_backing_votes - 1) && + is_own_backing_group + { + let statement = Statement::Valid(candidate_hash); + let context = SigningContext { parent_hash: relay_parent, session_index: 0 }; + let payload = statement.to_compact().signing_payload(&context); + let message = + polkadot_node_subsystem::messages::StatementDistributionMessage::Share( + relay_parent, + SignedFullStatementWithPVD::new( + statement.supply_pvd(self.state.pvd.clone()), + ValidatorIndex(NODE_UNDER_TEST), + self.state.pair.sign(&payload[..]), + &context, + &self.state.pair.public(), + ) + .unwrap(), + ); + messages.push(message); + } + + if statements_received_count == self.config.minimum_backing_votes { + let message = + polkadot_node_subsystem::messages::StatementDistributionMessage::Backed( + candidate_hash, + ); + messages.push(message); + } + }, + StatementWithPVD::Valid(candidate_hash) => { + statements_tracker + .entry(*candidate_hash) + .and_modify(|v| { + *v += 1; + }) + .or_insert(1); + + let statements_received_count = *statements_tracker.get(candidate_hash).unwrap(); + if statements_received_count == self.config.minimum_backing_votes { + let message = + polkadot_node_subsystem::messages::StatementDistributionMessage::Backed( + *candidate_hash, + ); + messages.push(message); + } + }, + } + + messages + } } #[overseer::subsystem(CandidateBacking, error=SubsystemError, prefix=self::overseer)] @@ -79,75 +151,13 @@ impl MockCandidateBacking { match msg { CandidateBackingMessage::Statement(relay_parent, statement) => { - let validator_id = statement.validator_index(); - let is_own_backing_group = - self.state.own_backing_group.contains(&validator_id); - match statement.payload() { - StatementWithPVD::Seconded(receipt, _pvd) => { - let candidate_hash = receipt.hash(); - statements_tracker - .entry(candidate_hash) - .and_modify(|v| { - *v += 1; - }) - .or_insert(1); - - let statements_received_count = - *statements_tracker.get(&candidate_hash).unwrap(); - if statements_received_count == - (self.config.minimum_backing_votes - 1) && - is_own_backing_group - { - let statement = Statement::Valid(candidate_hash); - let context = SigningContext { - parent_hash: relay_parent, - session_index: 0, - }; - let payload = - statement.to_compact().signing_payload(&context); - let message = - polkadot_node_subsystem::messages::StatementDistributionMessage::Share( - relay_parent, - SignedFullStatementWithPVD::new( - statement.supply_pvd(self.state.pvd.clone()), - ValidatorIndex(NODE_UNDER_TEST), - self.state.pair.sign(&payload[..]), - &context, - &self.state.pair.public(), - ) - .unwrap() - ); - ctx.send_message(message).await; - } - - if statements_received_count == - self.config.minimum_backing_votes - { - let message = - polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(candidate_hash - ); - ctx.send_message(message).await; - } - }, - StatementWithPVD::Valid(candidate_hash) => { - statements_tracker - .entry(*candidate_hash) - .and_modify(|v| { - *v += 1; - }) - .or_insert(1); - - let statements_received_count = - *statements_tracker.get(candidate_hash).unwrap(); - if statements_received_count == - self.config.minimum_backing_votes - { - let message = - polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(*candidate_hash - ); - ctx.send_message(message).await; - } - }, + let messages = self.handle_statement( + relay_parent, + statement, + &mut statements_tracker, + ); + for message in messages { + ctx.send_message(message).await; } }, _ => { From bd285d549262c5be024b780e1aa2acf247468af9 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Mon, 27 May 2024 14:05:53 +0200 Subject: [PATCH 38/39] Send more than 1 message --- .../subsystem-bench/src/lib/configuration.rs | 14 ++++++++++++++ .../src/lib/mock/runtime_api.rs | 4 ++-- .../subsystem-bench/src/lib/statement/mod.rs | 19 +++++++++++-------- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/polkadot/node/subsystem-bench/src/lib/configuration.rs b/polkadot/node/subsystem-bench/src/lib/configuration.rs index 7c42e7e61aab..f614a5e552a8 100644 --- a/polkadot/node/subsystem-bench/src/lib/configuration.rs +++ b/polkadot/node/subsystem-bench/src/lib/configuration.rs @@ -93,6 +93,12 @@ fn default_no_show_slots() -> usize { fn default_minimum_backing_votes() -> u32 { 2 } +fn default_max_candidate_depth() -> u32 { + 3 +} +fn default_allowed_ancestry_len() -> u32 { + 2 +} /// The test input parameters #[derive(Clone, Debug, Serialize, Deserialize)] @@ -144,6 +150,12 @@ pub struct TestConfiguration { /// Number of minimum backing votes #[serde(default = "default_minimum_backing_votes")] pub minimum_backing_votes: u32, + /// Async Backing max_candidate_depth + #[serde(default = "default_max_candidate_depth")] + pub max_candidate_depth: u32, + /// Async Backing allowed_ancestry_len + #[serde(default = "default_allowed_ancestry_len")] + pub allowed_ancestry_len: u32, } impl Default for TestConfiguration { @@ -166,6 +178,8 @@ impl Default for TestConfiguration { connectivity: default_connectivity(), num_blocks: Default::default(), minimum_backing_votes: default_minimum_backing_votes(), + max_candidate_depth: default_max_candidate_depth(), + allowed_ancestry_len: default_allowed_ancestry_len(), } } } diff --git a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs index 0c86153e94d5..9788a1123ec0 100644 --- a/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs +++ b/polkadot/node/subsystem-bench/src/lib/mock/runtime_api.rs @@ -250,8 +250,8 @@ impl MockRuntimeApi { RuntimeApiRequest::AsyncBackingParams(sender), ) => { let _ = sender.send(Ok(AsyncBackingParams { - max_candidate_depth: 3, - allowed_ancestry_len: 2, + max_candidate_depth: self.config.max_candidate_depth, + allowed_ancestry_len: self.config.allowed_ancestry_len, })); }, RuntimeApiMessage::Request(_parent, RuntimeApiRequest::Version(tx)) => { diff --git a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs index 3cbb753c52ea..508dd9179f7b 100644 --- a/polkadot/node/subsystem-bench/src/lib/statement/mod.rs +++ b/polkadot/node/subsystem-bench/src/lib/statement/mod.rs @@ -265,7 +265,7 @@ pub async fn benchmark_statement_distribution( } let test_start = Instant::now(); - let mut total_messages_count = 0; + let mut candidates_advertised = 0; for block_info in state.block_infos.iter() { let block_num = block_info.number as usize; gum::info!(target: LOG_TARGET, "Current block {}/{} {:?}", block_num, config.num_blocks, block_info.hash); @@ -303,9 +303,12 @@ pub async fn benchmark_statement_distribution( )), ); env.send_message(message).await; + + let max_messages_per_candidate = state.config.max_candidate_depth + 1; // One was just sent for the own backing group - let mut messages_tracker = - (0..groups.len()).map(|i| i == own_backing_group_index).collect_vec(); + let mut messages_tracker = (0..groups.len()) + .map(|i| if i == own_backing_group_index { max_messages_per_candidate } else { 0 }) + .collect_vec(); let neighbors = topology.compute_grid_neighbors_for(ValidatorIndex(NODE_UNDER_TEST)).unwrap(); @@ -379,10 +382,10 @@ pub async fn benchmark_statement_distribution( .chain(two_hop_y_peers_and_groups) { let messages_sent_count = messages_tracker.get_mut(group_index).unwrap(); - if *messages_sent_count { + if *messages_sent_count == max_messages_per_candidate { continue } - *messages_sent_count = true; + *messages_sent_count += 1; let candidate_hash = state .candidate_receipts @@ -417,7 +420,7 @@ pub async fn benchmark_statement_distribution( env.send_message(message).await; } - total_messages_count += messages_tracker.iter().filter(|&&v| v).collect_vec().len(); + candidates_advertised += messages_tracker.iter().filter(|&&v| v > 0).collect_vec().len(); loop { let manifests_count = state @@ -426,9 +429,9 @@ pub async fn benchmark_statement_distribution( .filter(|v| v.load(Ordering::SeqCst)) .collect::>() .len(); - gum::debug!(target: LOG_TARGET, "{}/{} manifest exchanges", manifests_count, total_messages_count); + gum::debug!(target: LOG_TARGET, "{}/{} manifest exchanges", manifests_count, candidates_advertised); - if manifests_count == total_messages_count { + if manifests_count == candidates_advertised { break; } tokio::time::sleep(Duration::from_millis(50)).await; From a80d2f18abc659d8c7935b33c4a2718c7aa0cc60 Mon Sep 17 00:00:00 2001 From: Andrei Eres Date: Mon, 27 May 2024 14:39:17 +0200 Subject: [PATCH 39/39] Update baseline --- .../benches/statement-distribution-regression-bench.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs b/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs index a849bc20b6dc..abcb1e6783fb 100644 --- a/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs +++ b/polkadot/node/network/statement-distribution/benches/statement-distribution-regression-bench.rs @@ -64,8 +64,8 @@ fn main() -> Result<(), String> { // We expect no variance for received and sent // but use 0.001 because we operate with floats messages.extend(average_usage.check_network_usage(&[ - ("Received from peers", 108.8000, 0.001), - ("Sent to peers", 123.8180, 0.001), + ("Received from peers", 106.4000, 0.001), + ("Sent to peers", 127.9100, 0.001), ])); messages.extend(average_usage.check_cpu_usage(&[("statement-distribution", 0.0390, 0.1)]));