1extern crate alloc;
18
19use alloc::collections::btree_map::BTreeMap;
20use codec::{Decode, Encode};
21use cumulus_primitives_core::{
22 relay_chain, AbridgedHostConfiguration, AbridgedHrmpChannel, ParaId,
23};
24use polkadot_primitives::{Header, UpgradeGoAhead};
25use sp_consensus_babe::{
26 digests::{CompatibleDigestItem, PreDigest, PrimaryPreDigest},
27 AuthorityId, AuthorityPair, BabeAuthorityWeight,
28};
29use sp_core::{
30 sr25519::vrf::{VrfPreOutput, VrfProof, VrfSignature},
31 Pair, H256,
32};
33use sp_runtime::{
34 traits::{HashingFor, Header as HeaderT},
35 Digest, DigestItem,
36};
37use sp_trie::PrefixedMemoryDB;
38
39#[derive(Clone)]
41pub struct RelayStateSproofBuilder {
42 pub para_id: ParaId,
51
52 pub host_config: AbridgedHostConfiguration,
53 pub dmq_mqc_head: Option<relay_chain::Hash>,
54 pub upgrade_go_ahead: Option<UpgradeGoAhead>,
55 pub relay_dispatch_queue_remaining_capacity: Option<(u32, u32)>,
56 pub hrmp_ingress_channel_index: Option<Vec<ParaId>>,
57 pub hrmp_egress_channel_index: Option<Vec<ParaId>>,
58 pub hrmp_channels: BTreeMap<relay_chain::HrmpChannelId, AbridgedHrmpChannel>,
59 pub current_slot: relay_chain::Slot,
60 pub current_epoch: u64,
61 pub randomness: relay_chain::Hash,
62 pub additional_key_values: Vec<(Vec<u8>, Vec<u8>)>,
63 pub included_para_head: Option<relay_chain::HeadData>,
64 pub num_authorities: u64,
65}
66
67impl Default for RelayStateSproofBuilder {
68 fn default() -> Self {
69 RelayStateSproofBuilder {
70 para_id: ParaId::from(200),
71 host_config: cumulus_primitives_core::AbridgedHostConfiguration {
72 max_code_size: 2 * 1024 * 1024,
73 max_head_data_size: 1024 * 1024,
74 max_upward_queue_count: 8,
75 max_upward_queue_size: 1024,
76 max_upward_message_size: 256,
77 max_upward_message_num_per_candidate: 5,
78 hrmp_max_message_num_per_candidate: 5,
79 validation_upgrade_cooldown: 6,
80 validation_upgrade_delay: 6,
81 async_backing_params: relay_chain::AsyncBackingParams {
82 allowed_ancestry_len: 0,
83 max_candidate_depth: 0,
84 },
85 },
86 dmq_mqc_head: None,
87 upgrade_go_ahead: None,
88 relay_dispatch_queue_remaining_capacity: None,
89 hrmp_ingress_channel_index: None,
90 hrmp_egress_channel_index: None,
91 hrmp_channels: BTreeMap::new(),
92 current_slot: 0.into(),
93 current_epoch: 0u64,
94 randomness: relay_chain::Hash::default(),
95 additional_key_values: vec![],
96 included_para_head: None,
97 num_authorities: 1,
98 }
99 }
100}
101
102impl RelayStateSproofBuilder {
103 pub fn upsert_inbound_channel(&mut self, sender: ParaId) -> &mut AbridgedHrmpChannel {
110 let in_index = self.hrmp_ingress_channel_index.get_or_insert_with(Vec::new);
111 if let Err(idx) = in_index.binary_search(&sender) {
112 in_index.insert(idx, sender);
113 }
114
115 self.upsert_channel(relay_chain::HrmpChannelId { sender, recipient: self.para_id })
116 }
117
118 pub fn upsert_outbound_channel(&mut self, recipient: ParaId) -> &mut AbridgedHrmpChannel {
125 let in_index = self.hrmp_egress_channel_index.get_or_insert_with(Vec::new);
126 if let Err(idx) = in_index.binary_search(&recipient) {
127 in_index.insert(idx, recipient);
128 }
129
130 self.upsert_channel(relay_chain::HrmpChannelId { sender: self.para_id, recipient })
131 }
132
133 fn upsert_channel(&mut self, id: relay_chain::HrmpChannelId) -> &mut AbridgedHrmpChannel {
136 self.hrmp_channels.entry(id).or_insert_with(|| AbridgedHrmpChannel {
137 max_capacity: 0,
138 max_total_size: 0,
139 max_message_size: 0,
140 msg_count: 0,
141 total_size: 0,
142 mqc_head: None,
143 })
144 }
145
146 pub fn into_state_root_proof_and_descendants(
150 self,
151 relay_parent_offset: u64,
152 ) -> (polkadot_primitives::Hash, sp_state_machine::StorageProof, Vec<Header>) {
153 let authorities = generate_authority_pairs(self.num_authorities);
154 let (state_root, proof) = self.into_state_root_and_proof();
155 let descendants =
156 build_relay_parent_descendants(relay_parent_offset + 1, state_root.into(), authorities);
157 (state_root, proof, descendants)
158 }
159
160 pub fn into_state_root_and_proof(
161 mut self,
162 ) -> (polkadot_primitives::Hash, sp_state_machine::StorageProof) {
163 if self.num_authorities > 0 {
165 let authorities = generate_authority_pairs(self.num_authorities);
166 let auth_pair = convert_to_authority_weight_pair(&authorities);
167
168 self.additional_key_values.push((
170 relay_chain::well_known_keys::AUTHORITIES.to_vec(),
171 auth_pair.clone().encode(),
172 ));
173 self.additional_key_values.push((
174 relay_chain::well_known_keys::NEXT_AUTHORITIES.to_vec(),
175 auth_pair.encode(),
176 ));
177 }
178
179 let (db, root) =
180 PrefixedMemoryDB::<HashingFor<polkadot_primitives::Block>>::default_with_root();
181 let state_version = Default::default(); let mut backend = sp_state_machine::TrieBackendBuilder::new(db, root).build();
183
184 let mut relevant_keys = Vec::new();
185 {
186 use codec::Encode as _;
187
188 let mut insert = |key: Vec<u8>, value: Vec<u8>| {
189 relevant_keys.push(key.clone());
190 backend.insert(vec![(None, vec![(key, Some(value))])], state_version);
191 };
192
193 insert(relay_chain::well_known_keys::ACTIVE_CONFIG.to_vec(), self.host_config.encode());
194 if let Some(dmq_mqc_head) = self.dmq_mqc_head {
195 insert(
196 relay_chain::well_known_keys::dmq_mqc_head(self.para_id),
197 dmq_mqc_head.encode(),
198 );
199 }
200 if let Some(para_head) = self.included_para_head {
201 insert(relay_chain::well_known_keys::para_head(self.para_id), para_head.encode());
202 }
203 if let Some(relay_dispatch_queue_remaining_capacity) =
204 self.relay_dispatch_queue_remaining_capacity
205 {
206 insert(
207 relay_chain::well_known_keys::relay_dispatch_queue_remaining_capacity(
208 self.para_id,
209 )
210 .key,
211 relay_dispatch_queue_remaining_capacity.encode(),
212 );
213 }
214 if let Some(upgrade_go_ahead) = self.upgrade_go_ahead {
215 insert(
216 relay_chain::well_known_keys::upgrade_go_ahead_signal(self.para_id),
217 upgrade_go_ahead.encode(),
218 );
219 }
220 if let Some(hrmp_ingress_channel_index) = self.hrmp_ingress_channel_index {
221 let mut sorted = hrmp_ingress_channel_index.clone();
222 sorted.sort();
223 assert_eq!(sorted, hrmp_ingress_channel_index);
224
225 insert(
226 relay_chain::well_known_keys::hrmp_ingress_channel_index(self.para_id),
227 hrmp_ingress_channel_index.encode(),
228 );
229 }
230 if let Some(hrmp_egress_channel_index) = self.hrmp_egress_channel_index {
231 let mut sorted = hrmp_egress_channel_index.clone();
232 sorted.sort();
233 assert_eq!(sorted, hrmp_egress_channel_index);
234
235 insert(
236 relay_chain::well_known_keys::hrmp_egress_channel_index(self.para_id),
237 hrmp_egress_channel_index.encode(),
238 );
239 }
240 for (channel, metadata) in self.hrmp_channels {
241 insert(relay_chain::well_known_keys::hrmp_channels(channel), metadata.encode());
242 }
243 insert(relay_chain::well_known_keys::EPOCH_INDEX.to_vec(), self.current_epoch.encode());
244 insert(
245 relay_chain::well_known_keys::ONE_EPOCH_AGO_RANDOMNESS.to_vec(),
246 self.randomness.encode(),
247 );
248 insert(relay_chain::well_known_keys::CURRENT_SLOT.to_vec(), self.current_slot.encode());
249
250 for (key, value) in self.additional_key_values {
251 insert(key, value);
252 }
253 }
254
255 let root = *backend.root();
256 let proof = sp_state_machine::prove_read(backend, relevant_keys).expect("prove read");
257 (root, proof)
258 }
259}
260
261pub fn generate_authority_pairs(num_authorities: u64) -> Vec<AuthorityPair> {
263 (0..num_authorities).map(|i| AuthorityPair::from_seed(&[i as u8; 32])).collect()
264}
265
266fn convert_to_authority_weight_pair(
268 authorities: &[AuthorityPair],
269) -> Vec<(AuthorityId, BabeAuthorityWeight)> {
270 authorities
271 .iter()
272 .map(|auth| (auth.public().into(), Default::default()))
273 .collect()
274}
275
276fn add_babe_pre_digest(header: &mut Header, authority_index: u32, block_number: u64) {
278 fn generate_testing_vrf() -> VrfSignature {
280 let vrf_proof_bytes = [0u8; 64];
281 let proof: VrfProof = VrfProof::decode(&mut vrf_proof_bytes.as_slice()).unwrap();
282 let vrf_pre_out_bytes = [0u8; 32];
283 let pre_output: VrfPreOutput =
284 VrfPreOutput::decode(&mut vrf_pre_out_bytes.as_slice()).unwrap();
285 VrfSignature { pre_output, proof }
286 }
287
288 let pre_digest = PrimaryPreDigest {
289 authority_index,
290 slot: block_number.into(),
291 vrf_signature: generate_testing_vrf(),
292 };
293
294 header
295 .digest_mut()
296 .push(DigestItem::babe_pre_digest(PreDigest::Primary(pre_digest)));
297}
298
299pub fn build_relay_parent_descendants(
301 num_headers: u64,
302 state_root: H256,
303 authorities: Vec<AuthorityPair>,
304) -> Vec<Header> {
305 let mut headers = Vec::with_capacity(num_headers as usize);
306
307 let mut previous_hash = None;
308
309 for block_number in 0..num_headers as u32 {
310 let mut header = Header {
311 number: block_number,
312 parent_hash: previous_hash.unwrap_or_default(),
313 state_root,
314 extrinsics_root: H256::default(),
315 digest: Digest::default(),
316 };
317 let authority_index = block_number % (authorities.len() as u32);
318
319 add_babe_pre_digest(&mut header, authority_index, block_number as u64);
321
322 let signature = authorities[authority_index as usize].sign(header.hash().as_bytes());
324 header.digest_mut().push(DigestItem::babe_seal(signature.into()));
325
326 previous_hash = Some(header.hash());
327 headers.push(header);
328 }
329
330 headers
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336 use codec::Decode;
337 use proptest::prelude::*;
338 use sp_consensus_babe::{
339 digests::{PreDigest, PrimaryPreDigest},
340 AuthorityId, AuthorityPair, BabeAuthorityWeight,
341 };
342 use sp_core::{crypto::Pair, sr25519::Signature, H256};
343 use sp_runtime::{
344 generic::{Digest, Header},
345 DigestItem,
346 };
347
348 #[test]
350 fn test_generate_authority_pairs_count() {
351 assert_eq!(generate_authority_pairs(0).len(), 0);
353
354 assert_eq!(generate_authority_pairs(5).len(), 5);
356
357 assert_eq!(generate_authority_pairs(100).len(), 100);
359
360 let pairs = generate_authority_pairs(10);
362 let public_keys: std::collections::HashSet<_> =
363 pairs.iter().map(|pair| pair.public()).collect();
364
365 assert_eq!(pairs.len(), public_keys.len());
366 }
367
368 #[test]
370 fn test_convert_to_authority_weight_pair() {
371 let num_authorities = 3;
372 let authorities = generate_authority_pairs(num_authorities);
373 let converted_pairs = convert_to_authority_weight_pair(&authorities);
374
375 assert_eq!(converted_pairs.len(), num_authorities as usize);
377
378 for (i, (authority_id, weight)) in converted_pairs.iter().enumerate() {
379 let expected_id: AuthorityId = authorities[i].public().into();
381 assert_eq!(*authority_id, expected_id);
382
383 assert_eq!(*weight, BabeAuthorityWeight::default());
385 }
386 }
387
388 #[test]
390 fn test_add_babe_pre_digest() {
391 let mut header = Header {
392 number: 0,
393 parent_hash: H256::default(),
394 state_root: H256::default(),
395 extrinsics_root: H256::default(),
396 digest: Digest::default(),
397 };
398 let authority_index = 42;
399 let block_number = 100;
400
401 add_babe_pre_digest(&mut header, authority_index, block_number);
402
403 assert_eq!(header.digest().logs.len(), 1);
405
406 let digest_item = &header.digest().logs[0];
408
409 let pre_digest_data = match digest_item {
410 DigestItem::PreRuntime(id, data) if id == &sp_consensus_babe::BABE_ENGINE_ID => {
411 PreDigest::decode(&mut &data[..]).unwrap()
412 },
413 _ => panic!("Expected a BABE pre-digest"),
414 };
415
416 match pre_digest_data {
417 PreDigest::Primary(PrimaryPreDigest {
418 authority_index: auth_idx,
419 slot,
420 vrf_signature: _,
421 }) => {
422 assert_eq!(auth_idx, authority_index);
423 assert_eq!(slot, relay_chain::Slot::from(block_number));
424 },
425 _ => panic!("Expected a Primary PreDigest"),
426 }
427 }
428
429 proptest! {
430 #[test]
432 fn prop_test_build_relay_parent_descendants(
433 num_headers in 1..20u64, seed_bytes: [u8; 32],
435 num_authorities in 1..5u64,
436 ) {
437 let state_root = H256::from(seed_bytes);
438 let authorities = generate_authority_pairs(num_authorities);
439
440 if authorities.is_empty() {
442 return Ok(());
443 }
444
445 let headers = build_relay_parent_descendants(num_headers, state_root, authorities.clone());
446
447 prop_assert_eq!(headers.len(), num_headers as usize);
449
450 let mut previous_hash: Option<H256> = None;
451
452 for (i, header) in headers.iter().enumerate() {
453 let block_number = i as u32;
454 let expected_authority_index = block_number % (num_authorities as u32);
455 let authority_pair = &authorities[expected_authority_index as usize];
456
457 prop_assert_eq!(header.number, block_number);
459 prop_assert_eq!(header.parent_hash, previous_hash.unwrap_or_default());
460 prop_assert_eq!(header.state_root, state_root);
461
462 prop_assert_eq!(header.digest().logs.len(), 2);
464
465 let pre_digest_item = &header.digest().logs[0];
466 let seal_item = &header.digest().logs[1];
467
468 let pre_digest_data = match pre_digest_item {
470 DigestItem::PreRuntime(id, data) if id == &sp_consensus_babe::BABE_ENGINE_ID => {
471 PreDigest::decode(&mut &data[..]).unwrap()
472 }
473 _ => panic!("Expected a BABE pre-digest"),
474 };
475
476 if let PreDigest::Primary(PrimaryPreDigest { authority_index, slot, .. }) = pre_digest_data {
477 prop_assert_eq!(authority_index, expected_authority_index);
478 prop_assert_eq!(slot, relay_chain::Slot::from(block_number as u64));
479 } else {
480 panic!("Pre-Digest should be Primary");
481 }
482
483 let signature = match seal_item {
485 DigestItem::Seal(id, data) if id == &sp_consensus_babe::BABE_ENGINE_ID => {
486 let raw_sig = Signature::decode(&mut &data[..]).expect("Valid signature");
487 sp_consensus_babe::AuthoritySignature::from(raw_sig)
488 }
489 _ => panic!("Expected a BABE seal"),
490 };
491
492 let mut header_without_seal = header.clone();
495 header_without_seal.digest_mut().pop(); let header_hash_for_verification = header_without_seal.hash();
497 prop_assert!(AuthorityPair::verify(&signature, header_hash_for_verification.as_bytes(), &authority_pair.public()));
498
499 let header_hash = header.hash();
500
501 previous_hash = Some(header_hash);
502 }
503 }
504 }
505
506 #[test]
509 fn test_authorities_included_in_proof() {
510 let mut builder = RelayStateSproofBuilder::default();
511 builder.num_authorities = 3;
512
513 let (state_root, proof) = builder.into_state_root_and_proof();
514
515 let authorities_key = relay_chain::well_known_keys::AUTHORITIES;
517 let next_authorities_key = relay_chain::well_known_keys::NEXT_AUTHORITIES;
518
519 use sp_runtime::traits::HashingFor;
522 use sp_state_machine::{Backend, TrieBackendBuilder};
523 let db = proof.into_memory_db::<HashingFor<polkadot_primitives::Block>>();
524 let backend = TrieBackendBuilder::new(db, state_root).build();
525
526 let authorities_data = backend.storage(authorities_key).unwrap().unwrap();
528 let authorities: Vec<(AuthorityId, BabeAuthorityWeight)> =
529 codec::Decode::decode(&mut &authorities_data[..]).unwrap();
530 assert_eq!(authorities.len(), 3);
531
532 let next_authorities_data = backend.storage(next_authorities_key).unwrap().unwrap();
534 let next_authorities: Vec<(AuthorityId, BabeAuthorityWeight)> =
535 codec::Decode::decode(&mut &next_authorities_data[..]).unwrap();
536 assert_eq!(next_authorities.len(), 3);
537
538 assert_eq!(authorities, next_authorities);
540 }
541
542 #[test]
544 fn test_into_state_root_proof_and_descendants_generates_correct_number_of_headers() {
545 let mut builder = RelayStateSproofBuilder::default();
546 builder.num_authorities = 2;
547
548 let test_cases = vec![0, 1, 5, 10];
550
551 for relay_parent_offset in test_cases {
552 let builder_clone = builder.clone();
553 let (state_root, _proof, descendants) =
554 builder_clone.into_state_root_proof_and_descendants(relay_parent_offset);
555
556 let expected_num_headers = relay_parent_offset + 1;
558 assert_eq!(
559 descendants.len(),
560 expected_num_headers as usize,
561 "Failed for relay_parent_offset {}: expected {} headers, got {}",
562 relay_parent_offset,
563 expected_num_headers,
564 descendants.len()
565 );
566
567 for (i, header) in descendants.iter().enumerate() {
569 assert_eq!(header.number, i as u32);
570 assert_eq!(header.state_root, state_root.into());
571 }
572
573 for header in &descendants {
575 assert_eq!(
576 header.digest().logs.len(),
577 2,
578 "Each header should have pre-digest and seal"
579 );
580 }
581 }
582 }
583}