1use crate::configuration::{TestAuthorities, TestConfiguration};
20use bitvec::prelude::BitVec;
21use futures::FutureExt;
22use itertools::Itertools;
23use polkadot_node_subsystem::{
24 messages::{RuntimeApiMessage, RuntimeApiRequest},
25 overseer, SpawnedSubsystem, SubsystemError,
26};
27use polkadot_node_subsystem_types::OverseerSignal;
28use polkadot_primitives::{
29 node_features, ApprovalVotingParams, AsyncBackingParams, CandidateEvent,
30 CandidateReceiptV2 as CandidateReceipt, CoreIndex, CoreState, GroupIndex, GroupRotationInfo,
31 Id as ParaId, IndexedVec, NodeFeatures, OccupiedCore, ScheduledCore, SessionIndex, SessionInfo,
32 ValidationCode, ValidatorIndex,
33};
34use sp_consensus_babe::Epoch as BabeEpoch;
35use sp_core::H256;
36use std::collections::{BTreeMap, HashMap, VecDeque};
37
38const LOG_TARGET: &str = "subsystem-bench::runtime-api-mock";
39
40#[derive(Clone)]
42pub struct RuntimeApiState {
43 authorities: TestAuthorities,
45 node_features: NodeFeatures,
47 candidate_hashes: HashMap<H256, Vec<CandidateReceipt>>,
49 candidate_events: HashMap<H256, Vec<CandidateEvent>>,
51 babe_epoch: Option<BabeEpoch>,
52 session_index: SessionIndex,
54 claim_queue: BTreeMap<CoreIndex, VecDeque<ParaId>>,
56}
57
58#[derive(Clone)]
59pub enum MockRuntimeApiCoreState {
60 Occupied,
61 Scheduled,
62 #[allow(dead_code)]
63 Free,
64}
65
66#[derive(Clone)]
68pub struct MockRuntimeApi {
69 state: RuntimeApiState,
70 config: TestConfiguration,
71 core_state: MockRuntimeApiCoreState,
72}
73
74impl MockRuntimeApi {
75 pub fn new(
76 config: TestConfiguration,
77 authorities: TestAuthorities,
78 candidate_hashes: HashMap<H256, Vec<CandidateReceipt>>,
79 candidate_events: HashMap<H256, Vec<CandidateEvent>>,
80 babe_epoch: Option<BabeEpoch>,
81 session_index: SessionIndex,
82 core_state: MockRuntimeApiCoreState,
83 ) -> MockRuntimeApi {
84 let node_features = default_node_features();
86 let validator_group_count =
87 session_info_for_peers(&config, &authorities).validator_groups.len();
88
89 let claim_queue = candidate_hashes
92 .iter()
93 .next()
94 .expect("Candidates are generated at test start")
95 .1
96 .iter()
97 .enumerate()
98 .map(|(index, candidate_receipt)| {
99 assert!(index < validator_group_count);
101 (CoreIndex(index as u32), vec![candidate_receipt.descriptor.para_id()].into())
102 })
103 .collect();
104
105 Self {
106 state: RuntimeApiState {
107 authorities,
108 candidate_hashes,
109 candidate_events,
110 babe_epoch,
111 session_index,
112 node_features,
113 claim_queue,
114 },
115 config,
116 core_state,
117 }
118 }
119
120 fn session_info(&self) -> SessionInfo {
121 session_info_for_peers(&self.config, &self.state.authorities)
122 }
123}
124
125pub fn session_info_for_peers(
127 configuration: &TestConfiguration,
128 authorities: &TestAuthorities,
129) -> SessionInfo {
130 let all_validators = (0..configuration.n_validators)
131 .map(|i| ValidatorIndex(i as _))
132 .collect::<Vec<_>>();
133
134 let validator_groups = all_validators
135 .chunks(configuration.max_validators_per_core)
136 .map(Vec::from)
137 .collect::<Vec<_>>();
138
139 SessionInfo {
140 validators: authorities.validator_public.iter().cloned().collect(),
141 discovery_keys: authorities.validator_authority_id.to_vec(),
142 assignment_keys: authorities.validator_assignment_id.to_vec(),
143 validator_groups: IndexedVec::<GroupIndex, Vec<ValidatorIndex>>::from(validator_groups),
144 n_cores: configuration.n_cores as u32,
145 needed_approvals: configuration.needed_approvals as u32,
146 zeroth_delay_tranche_width: configuration.zeroth_delay_tranche_width as u32,
147 relay_vrf_modulo_samples: configuration.relay_vrf_modulo_samples as u32,
148 n_delay_tranches: configuration.n_delay_tranches as u32,
149 no_show_slots: configuration.no_show_slots as u32,
150 active_validator_indices: (0..authorities.validator_authority_id.len())
151 .map(|index| ValidatorIndex(index as u32))
152 .collect_vec(),
153 dispute_period: 6,
154 random_seed: [0u8; 32],
155 }
156}
157
158#[overseer::subsystem(RuntimeApi, error=SubsystemError, prefix=self::overseer)]
159impl<Context> MockRuntimeApi {
160 fn start(self, ctx: Context) -> SpawnedSubsystem {
161 let future = self.run(ctx).map(|_| Ok(())).boxed();
162
163 SpawnedSubsystem { name: "test-environment", future }
164 }
165}
166
167#[overseer::contextbounds(RuntimeApi, prefix = self::overseer)]
168impl MockRuntimeApi {
169 async fn run<Context>(self, mut ctx: Context) {
170 let validator_group_count = self.session_info().validator_groups.len();
171
172 loop {
173 let msg = ctx.recv().await.expect("Overseer never fails us");
174
175 match msg {
176 orchestra::FromOrchestra::Signal(signal) =>
177 if signal == OverseerSignal::Conclude {
178 return
179 },
180 orchestra::FromOrchestra::Communication { msg } => {
181 gum::debug!(target: LOG_TARGET, msg=?msg, "recv message");
182
183 match msg {
184 RuntimeApiMessage::Request(
185 request,
186 RuntimeApiRequest::CandidateEvents(sender),
187 ) => {
188 let candidate_events = self.state.candidate_events.get(&request);
189 let _ = sender.send(Ok(candidate_events.cloned().unwrap_or_default()));
190 },
191 RuntimeApiMessage::Request(
192 _block_hash,
193 RuntimeApiRequest::SessionInfo(_session_index, sender),
194 ) => {
195 let _ = sender.send(Ok(Some(self.session_info())));
196 },
197 RuntimeApiMessage::Request(
198 _block_hash,
199 RuntimeApiRequest::NodeFeatures(_session_index, sender),
200 ) => {
201 let _ = sender.send(Ok(self.state.node_features.clone()));
202 },
203 RuntimeApiMessage::Request(
204 _block_hash,
205 RuntimeApiRequest::SessionExecutorParams(_session_index, sender),
206 ) => {
207 let _ = sender.send(Ok(Some(Default::default())));
208 },
209 RuntimeApiMessage::Request(
210 _block_hash,
211 RuntimeApiRequest::Validators(sender),
212 ) => {
213 let _ =
214 sender.send(Ok(self.state.authorities.validator_public.clone()));
215 },
216 RuntimeApiMessage::Request(
217 _block_hash,
218 RuntimeApiRequest::SessionIndexForChild(sender),
219 ) => {
220 let _ = sender.send(Ok(self.state.session_index));
222 },
223 RuntimeApiMessage::Request(
224 block_hash,
225 RuntimeApiRequest::AvailabilityCores(sender),
226 ) => {
227 let candidate_hashes = self
228 .state
229 .candidate_hashes
230 .get(&block_hash)
231 .expect("Relay chain block hashes are generated at test start");
232
233 let cores = candidate_hashes
235 .iter()
236 .enumerate()
237 .map(|(index, candidate_receipt)| {
238 assert!(index < validator_group_count);
240
241 use MockRuntimeApiCoreState::*;
242 match self.core_state {
243 Occupied => CoreState::Occupied(OccupiedCore {
244 next_up_on_available: None,
245 occupied_since: 0,
246 time_out_at: 0,
247 next_up_on_time_out: None,
248 availability: BitVec::default(),
249 group_responsible: GroupIndex(index as u32),
250 candidate_hash: candidate_receipt.hash(),
251 candidate_descriptor: candidate_receipt
252 .descriptor
253 .clone(),
254 }),
255 Scheduled => CoreState::Scheduled(ScheduledCore {
256 para_id: (index + 1).into(),
257 collator: None,
258 }),
259 Free => todo!(),
260 }
261 })
262 .collect::<Vec<_>>();
263
264 let _ = sender.send(Ok(cores));
265 },
266 RuntimeApiMessage::Request(
267 _request,
268 RuntimeApiRequest::CurrentBabeEpoch(sender),
269 ) => {
270 let _ = sender.send(Ok(self
271 .state
272 .babe_epoch
273 .clone()
274 .expect("Babe epoch unpopulated")));
275 },
276 RuntimeApiMessage::Request(
277 _block_hash,
278 RuntimeApiRequest::AsyncBackingParams(sender),
279 ) => {
280 let _ = sender.send(Ok(AsyncBackingParams {
281 max_candidate_depth: self.config.max_candidate_depth,
282 allowed_ancestry_len: self.config.allowed_ancestry_len,
283 }));
284 },
285 RuntimeApiMessage::Request(_parent, RuntimeApiRequest::Version(tx)) => {
286 tx.send(Ok(RuntimeApiRequest::DISABLED_VALIDATORS_RUNTIME_REQUIREMENT))
287 .unwrap();
288 },
289 RuntimeApiMessage::Request(
290 _parent,
291 RuntimeApiRequest::DisabledValidators(tx),
292 ) => {
293 tx.send(Ok(vec![])).unwrap();
294 },
295 RuntimeApiMessage::Request(
296 _parent,
297 RuntimeApiRequest::MinimumBackingVotes(_session_index, tx),
298 ) => {
299 tx.send(Ok(self.config.minimum_backing_votes)).unwrap();
300 },
301 RuntimeApiMessage::Request(
302 _parent,
303 RuntimeApiRequest::ValidatorGroups(tx),
304 ) => {
305 let groups = self.session_info().validator_groups.to_vec();
306 let group_rotation_info = GroupRotationInfo {
307 session_start_block: 1,
308 group_rotation_frequency: 12,
309 now: 1,
310 };
311 tx.send(Ok((groups, group_rotation_info))).unwrap();
312 },
313 RuntimeApiMessage::Request(
314 _parent,
315 RuntimeApiRequest::ValidationCodeByHash(hash, tx),
316 ) => {
317 gum::debug!(target: LOG_TARGET, "ValidationCodeByHash: {:?}", hash);
318 let validation_code = ValidationCode(Vec::new());
319 if let Err(err) = tx.send(Ok(Some(validation_code))) {
320 gum::error!(target: LOG_TARGET, ?err, "validation code wasn't received");
321 }
322 },
323 RuntimeApiMessage::Request(
324 _parent,
325 RuntimeApiRequest::ApprovalVotingParams(_, tx),
326 ) =>
327 if let Err(err) = tx.send(Ok(ApprovalVotingParams::default())) {
328 gum::error!(target: LOG_TARGET, ?err, "Voting params weren't received");
329 },
330 RuntimeApiMessage::Request(_parent, RuntimeApiRequest::ClaimQueue(tx)) => {
331 tx.send(Ok(self.state.claim_queue.clone())).unwrap();
332 },
333 RuntimeApiMessage::Request(
334 _parent,
335 RuntimeApiRequest::FetchOnChainVotes(tx),
336 ) => {
337 tx.send(Ok(None)).unwrap();
338 },
339 RuntimeApiMessage::Request(
340 _parent,
341 RuntimeApiRequest::UnappliedSlashes(tx),
342 ) => {
343 tx.send(Ok(vec![])).unwrap();
344 },
345 message => {
347 unimplemented!("Unexpected runtime-api message: {:?}", message)
348 },
349 }
350 },
351 }
352 }
353 }
354}
355
356pub fn default_node_features() -> NodeFeatures {
357 let mut node_features = NodeFeatures::new();
358 node_features.resize(node_features::FeatureIndex::FirstUnassigned as usize, false);
359 node_features.set(node_features::FeatureIndex::AvailabilityChunkMapping as u8 as usize, true);
360 node_features.set(node_features::FeatureIndex::ElasticScalingMVP as u8 as usize, true);
361 node_features.set(node_features::FeatureIndex::CandidateReceiptV2 as u8 as usize, true);
362
363 node_features
364}