referrerpolicy=no-referrer-when-downgrade

polkadot_subsystem_bench/mock/
candidate_backing.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17//! A generic candidate backing subsystem mockup suitable to be used in benchmarks.
18
19use crate::{configuration::TestConfiguration, NODE_UNDER_TEST};
20use futures::FutureExt;
21use polkadot_node_primitives::{SignedFullStatementWithPVD, Statement, StatementWithPVD};
22use polkadot_node_subsystem::{
23	messages::CandidateBackingMessage, overseer, SpawnedSubsystem, SubsystemError,
24};
25use polkadot_node_subsystem_types::OverseerSignal;
26use polkadot_primitives::{
27	CandidateHash, Hash, PersistedValidationData, SigningContext, ValidatorIndex, ValidatorPair,
28};
29use sp_core::Pair;
30use std::collections::HashMap;
31
32const LOG_TARGET: &str = "subsystem-bench::candidate-backing-mock";
33
34struct MockCandidateBackingState {
35	pair: ValidatorPair,
36	pvd: PersistedValidationData,
37	own_backing_group: Vec<ValidatorIndex>,
38}
39
40pub struct MockCandidateBacking {
41	config: TestConfiguration,
42	state: MockCandidateBackingState,
43}
44
45impl MockCandidateBacking {
46	pub fn new(
47		config: TestConfiguration,
48		pair: ValidatorPair,
49		pvd: PersistedValidationData,
50		own_backing_group: Vec<ValidatorIndex>,
51	) -> Self {
52		Self { config, state: MockCandidateBackingState { pair, pvd, own_backing_group } }
53	}
54
55	fn handle_statement(
56		&self,
57		relay_parent: Hash,
58		statement: SignedFullStatementWithPVD,
59		statements_tracker: &mut HashMap<CandidateHash, u32>,
60	) -> Vec<polkadot_node_subsystem::messages::StatementDistributionMessage> {
61		let mut messages = vec![];
62		let validator_id = statement.validator_index();
63		let is_own_backing_group = self.state.own_backing_group.contains(&validator_id);
64
65		match statement.payload() {
66			StatementWithPVD::Seconded(receipt, _pvd) => {
67				let candidate_hash = receipt.hash();
68				statements_tracker
69					.entry(candidate_hash)
70					.and_modify(|v| {
71						*v += 1;
72					})
73					.or_insert(1);
74
75				let statements_received_count = *statements_tracker.get(&candidate_hash).unwrap();
76				if statements_received_count == (self.config.minimum_backing_votes - 1) &&
77					is_own_backing_group
78				{
79					let statement = Statement::Valid(candidate_hash);
80					let context = SigningContext { parent_hash: relay_parent, session_index: 0 };
81					let payload = statement.to_compact().signing_payload(&context);
82					let message =
83						polkadot_node_subsystem::messages::StatementDistributionMessage::Share(
84							relay_parent,
85							SignedFullStatementWithPVD::new(
86								statement.supply_pvd(self.state.pvd.clone()),
87								ValidatorIndex(NODE_UNDER_TEST),
88								self.state.pair.sign(&payload[..]),
89								&context,
90								&self.state.pair.public(),
91							)
92							.unwrap(),
93						);
94					messages.push(message);
95				}
96
97				if statements_received_count == self.config.minimum_backing_votes {
98					let message =
99						polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(
100							candidate_hash,
101						);
102					messages.push(message);
103				}
104			},
105			StatementWithPVD::Valid(candidate_hash) => {
106				statements_tracker
107					.entry(*candidate_hash)
108					.and_modify(|v| {
109						*v += 1;
110					})
111					.or_insert(1);
112
113				let statements_received_count = *statements_tracker.get(candidate_hash).unwrap();
114				if statements_received_count == self.config.minimum_backing_votes {
115					let message =
116						polkadot_node_subsystem::messages::StatementDistributionMessage::Backed(
117							*candidate_hash,
118						);
119					messages.push(message);
120				}
121			},
122		}
123
124		messages
125	}
126}
127
128#[overseer::subsystem(CandidateBacking, error=SubsystemError, prefix=self::overseer)]
129impl<Context> MockCandidateBacking {
130	fn start(self, ctx: Context) -> SpawnedSubsystem {
131		let future = self.run(ctx).map(|_| Ok(())).boxed();
132
133		SpawnedSubsystem { name: "test-environment", future }
134	}
135}
136
137#[overseer::contextbounds(CandidateBacking, prefix = self::overseer)]
138impl MockCandidateBacking {
139	async fn run<Context>(self, mut ctx: Context) {
140		let mut statements_tracker: HashMap<CandidateHash, u32> = Default::default();
141
142		loop {
143			let msg = ctx.recv().await.expect("Overseer never fails us");
144			match msg {
145				orchestra::FromOrchestra::Signal(signal) =>
146					if signal == OverseerSignal::Conclude {
147						return
148					},
149				orchestra::FromOrchestra::Communication { msg } => {
150					gum::trace!(target: LOG_TARGET, msg=?msg, "recv message");
151
152					match msg {
153						CandidateBackingMessage::Statement(relay_parent, statement) => {
154							let messages = self.handle_statement(
155								relay_parent,
156								statement,
157								&mut statements_tracker,
158							);
159							for message in messages {
160								ctx.send_message(message).await;
161							}
162						},
163						_ => {
164							unimplemented!("Unexpected candidate-backing message")
165						},
166					}
167				},
168			}
169		}
170	}
171}