polkadot_subsystem_bench/mock/
candidate_backing.rs1use 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}