1#![forbid(unused_crate_dependencies)]
18#![forbid(unused_extern_crates)]
19
20use codec::{Decode, Encode};
26use polkadot_primitives::{
27 AppVerify, CandidateCommitments, CandidateDescriptorV2, CandidateHash, CandidateReceiptV2,
28 CollatorId, CollatorSignature, CommittedCandidateReceiptV2, CoreIndex, Hash, HashT, HeadData,
29 Id, Id as ParaId, MutateDescriptorV2, PersistedValidationData, SessionIndex, ValidationCode,
30 ValidationCodeHash, ValidatorId,
31};
32pub use rand;
33use scale_info::TypeInfo;
34use sp_application_crypto::{sr25519, ByteArray};
35use sp_keyring::Sr25519Keyring;
36use sp_runtime::{generic::Digest, traits::BlakeTwo256};
37
38const MAX_POV_SIZE: u32 = 1_000_000;
39
40#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
42pub struct CandidateDescriptor<H = Hash> {
43 pub para_id: Id,
45 pub relay_parent: H,
47 pub collator: CollatorId,
49 pub persisted_validation_data_hash: Hash,
53 pub pov_hash: Hash,
55 pub erasure_root: Hash,
57 pub signature: CollatorSignature,
60 pub para_head: Hash,
62 pub validation_code_hash: ValidationCodeHash,
64}
65
66impl<H: AsRef<[u8]>> CandidateDescriptor<H> {
67 pub fn check_collator_signature(&self) -> Result<(), ()> {
69 check_collator_signature(
70 &self.relay_parent,
71 &self.para_id,
72 &self.persisted_validation_data_hash,
73 &self.pov_hash,
74 &self.validation_code_hash,
75 &self.collator,
76 &self.signature,
77 )
78 }
79}
80
81#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
83pub struct CandidateReceipt<H = Hash> {
84 pub descriptor: CandidateDescriptor<H>,
86 pub commitments_hash: Hash,
88}
89
90impl<H> CandidateReceipt<H> {
91 pub fn descriptor(&self) -> &CandidateDescriptor<H> {
93 &self.descriptor
94 }
95
96 pub fn hash(&self) -> CandidateHash
98 where
99 H: Encode,
100 {
101 CandidateHash(BlakeTwo256::hash_of(self))
102 }
103}
104
105impl<H: Copy + AsRef<[u8]>> From<CandidateReceiptV2<H>> for CandidateReceipt<H> {
106 fn from(value: CandidateReceiptV2<H>) -> Self {
107 Self { descriptor: value.descriptor.into(), commitments_hash: value.commitments_hash }
108 }
109}
110
111impl<H: Copy + AsRef<[u8]> + From<Hash>> From<CandidateReceipt<H>> for CandidateReceiptV2<H> {
112 fn from(value: CandidateReceipt<H>) -> Self {
113 Self { descriptor: value.descriptor.into(), commitments_hash: value.commitments_hash }
114 }
115}
116
117#[derive(PartialEq, Eq, Clone, Encode, Decode, TypeInfo)]
119pub struct CommittedCandidateReceipt<H = Hash> {
120 pub descriptor: CandidateDescriptor<H>,
122 pub commitments: CandidateCommitments,
124}
125
126impl<H> CommittedCandidateReceipt<H> {
127 pub fn descriptor(&self) -> &CandidateDescriptor<H> {
129 &self.descriptor
130 }
131}
132
133impl<H: Clone> CommittedCandidateReceipt<H> {
134 pub fn to_plain(&self) -> CandidateReceipt<H> {
136 CandidateReceipt {
137 descriptor: self.descriptor.clone(),
138 commitments_hash: self.commitments.hash(),
139 }
140 }
141
142 pub fn hash(&self) -> CandidateHash
147 where
148 H: Encode,
149 {
150 self.to_plain().hash()
151 }
152
153 pub fn corresponds_to(&self, receipt: &CandidateReceipt<H>) -> bool
155 where
156 H: PartialEq,
157 {
158 receipt.descriptor == self.descriptor && receipt.commitments_hash == self.commitments.hash()
159 }
160}
161
162impl PartialOrd for CommittedCandidateReceipt {
163 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
164 Some(self.cmp(other))
165 }
166}
167
168impl Ord for CommittedCandidateReceipt {
169 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
170 self.descriptor()
173 .para_id
174 .cmp(&other.descriptor().para_id)
175 .then_with(|| self.commitments.head_data.cmp(&other.commitments.head_data))
176 }
177}
178
179impl<H: Copy + AsRef<[u8]>> From<CommittedCandidateReceiptV2<H>> for CommittedCandidateReceipt<H> {
180 fn from(value: CommittedCandidateReceiptV2<H>) -> Self {
181 Self { descriptor: value.descriptor.into(), commitments: value.commitments }
182 }
183}
184
185impl<H: Copy + AsRef<[u8]>> From<CandidateDescriptorV2<H>> for CandidateDescriptor<H> {
186 fn from(value: CandidateDescriptorV2<H>) -> Self {
187 Self {
188 para_id: value.para_id(),
189 relay_parent: value.relay_parent(),
190 collator: value.rebuild_collator_field_for_tests(),
191 persisted_validation_data_hash: value.persisted_validation_data_hash(),
192 pov_hash: value.pov_hash(),
193 erasure_root: value.erasure_root(),
194 signature: value.rebuild_signature_field_for_tests(),
195 para_head: value.para_head(),
196 validation_code_hash: value.validation_code_hash(),
197 }
198 }
199}
200
201fn clone_into_array<A, T>(slice: &[T]) -> A
202where
203 A: Default + AsMut<[T]>,
204 T: Clone,
205{
206 let mut a = A::default();
207 <A as AsMut<[T]>>::as_mut(&mut a).clone_from_slice(slice);
208 a
209}
210
211impl<H: Copy + AsRef<[u8]> + From<Hash>> From<CandidateDescriptor<H>> for CandidateDescriptorV2<H> {
212 fn from(value: CandidateDescriptor<H>) -> Self {
213 let collator = value.collator.as_slice();
214 let signature = value.signature.into_inner().0;
215
216 CandidateDescriptorV2::new_from_raw(
217 value.para_id,
218 value.relay_parent,
219 collator[0],
220 u16::from_ne_bytes(clone_into_array(&collator[1..3])),
221 SessionIndex::from_ne_bytes(clone_into_array(&collator[3..7])),
222 collator[7],
223 clone_into_array(&collator[8..]),
224 value.persisted_validation_data_hash,
225 value.pov_hash,
226 value.erasure_root,
227 H::from(Hash::from_slice(&signature[0..32])),
228 clone_into_array(&signature[32..64]),
229 value.para_head,
230 value.validation_code_hash,
231 )
232 }
233}
234
235impl<H: Copy + AsRef<[u8]> + From<Hash>> From<CommittedCandidateReceipt<H>>
236 for CommittedCandidateReceiptV2<H>
237{
238 fn from(value: CommittedCandidateReceipt<H>) -> Self {
239 Self { descriptor: value.descriptor.into(), commitments: value.commitments }
240 }
241}
242
243pub fn collator_signature_payload<H: AsRef<[u8]>>(
245 relay_parent: &H,
246 para_id: &Id,
247 persisted_validation_data_hash: &Hash,
248 pov_hash: &Hash,
249 validation_code_hash: &ValidationCodeHash,
250) -> [u8; 132] {
251 let mut payload = [0u8; 132];
253
254 payload[0..32].copy_from_slice(relay_parent.as_ref());
255 u32::from(*para_id).using_encoded(|s| payload[32..32 + s.len()].copy_from_slice(s));
256 payload[36..68].copy_from_slice(persisted_validation_data_hash.as_ref());
257 payload[68..100].copy_from_slice(pov_hash.as_ref());
258 payload[100..132].copy_from_slice(validation_code_hash.as_ref());
259
260 payload
261}
262
263pub(crate) fn check_collator_signature<H: AsRef<[u8]>>(
264 relay_parent: &H,
265 para_id: &Id,
266 persisted_validation_data_hash: &Hash,
267 pov_hash: &Hash,
268 validation_code_hash: &ValidationCodeHash,
269 collator: &CollatorId,
270 signature: &CollatorSignature,
271) -> Result<(), ()> {
272 let payload = collator_signature_payload(
273 relay_parent,
274 para_id,
275 persisted_validation_data_hash,
276 pov_hash,
277 validation_code_hash,
278 );
279
280 if signature.verify(&payload[..], collator) {
281 Ok(())
282 } else {
283 Err(())
284 }
285}
286
287pub fn dummy_candidate_receipt<H: AsRef<[u8]>>(relay_parent: H) -> CandidateReceipt<H> {
289 CandidateReceipt::<H> {
290 commitments_hash: dummy_candidate_commitments(dummy_head_data()).hash(),
291 descriptor: dummy_candidate_descriptor(relay_parent),
292 }
293}
294
295pub fn dummy_candidate_receipt_v2<H: AsRef<[u8]> + Copy + Default>(
297 relay_parent: H,
298) -> CandidateReceiptV2<H> {
299 CandidateReceiptV2::<H> {
300 commitments_hash: dummy_candidate_commitments(dummy_head_data()).hash(),
301 descriptor: dummy_candidate_descriptor_v2(relay_parent),
302 }
303}
304
305pub fn dummy_committed_candidate_receipt<H: AsRef<[u8]>>(
307 relay_parent: H,
308) -> CommittedCandidateReceipt<H> {
309 CommittedCandidateReceipt::<H> {
310 descriptor: dummy_candidate_descriptor::<H>(relay_parent),
311 commitments: dummy_candidate_commitments(dummy_head_data()),
312 }
313}
314
315pub fn dummy_committed_candidate_receipt_v2<H: AsRef<[u8]> + Copy + Default>(
317 relay_parent: H,
318) -> CommittedCandidateReceiptV2<H> {
319 CommittedCandidateReceiptV2 {
320 descriptor: dummy_candidate_descriptor_v2::<H>(relay_parent),
321 commitments: dummy_candidate_commitments(dummy_head_data()),
322 }
323}
324
325pub fn dummy_committed_candidate_receipt_v3<H: AsRef<[u8]> + Copy + Default>(
327 relay_parent: H,
328 scheduling_parent: H,
329) -> CommittedCandidateReceiptV2<H> {
330 CommittedCandidateReceiptV2 {
331 descriptor: dummy_candidate_descriptor_v3::<H>(relay_parent, scheduling_parent),
332 commitments: dummy_candidate_commitments(dummy_head_data()),
333 }
334}
335
336pub fn dummy_candidate_receipt_bad_sig(
339 relay_parent: Hash,
340 commitments: impl Into<Option<Hash>>,
341) -> CandidateReceipt<Hash> {
342 let commitments_hash = if let Some(commitments) = commitments.into() {
343 commitments
344 } else {
345 dummy_candidate_commitments(dummy_head_data()).hash()
346 };
347 CandidateReceipt::<Hash> {
348 commitments_hash,
349 descriptor: dummy_candidate_descriptor_bad_sig(relay_parent),
350 }
351}
352
353pub fn dummy_candidate_receipt_v2_bad_sig(
356 relay_parent: Hash,
357 commitments: impl Into<Option<Hash>>,
358) -> CandidateReceiptV2<Hash> {
359 let commitments_hash = if let Some(commitments) = commitments.into() {
360 commitments
361 } else {
362 dummy_candidate_commitments(dummy_head_data()).hash()
363 };
364 CandidateReceiptV2::<Hash> {
365 commitments_hash,
366 descriptor: dummy_candidate_descriptor_bad_sig(relay_parent).into(),
367 }
368}
369
370pub fn dummy_candidate_commitments(head_data: impl Into<Option<HeadData>>) -> CandidateCommitments {
372 CandidateCommitments {
373 head_data: head_data.into().unwrap_or(dummy_head_data()),
374 upward_messages: vec![].try_into().expect("empty vec fits within bounds"),
375 new_validation_code: None,
376 horizontal_messages: vec![].try_into().expect("empty vec fits within bounds"),
377 processed_downward_messages: 0,
378 hrmp_watermark: 0_u32,
379 }
380}
381
382pub fn dummy_hash() -> Hash {
384 Hash::zero()
385}
386
387pub fn dummy_digest() -> Digest {
389 Digest::default()
390}
391
392pub fn dummy_candidate_descriptor_bad_sig(relay_parent: Hash) -> CandidateDescriptor<Hash> {
394 let zeros = Hash::zero();
395 CandidateDescriptor::<Hash> {
396 para_id: 0.into(),
397 relay_parent,
398 collator: dummy_collator(),
399 persisted_validation_data_hash: zeros,
400 pov_hash: zeros,
401 erasure_root: zeros,
402 signature: dummy_collator_signature(),
403 para_head: zeros,
404 validation_code_hash: dummy_validation_code().hash(),
405 }
406}
407
408pub fn dummy_candidate_descriptor<H: AsRef<[u8]>>(relay_parent: H) -> CandidateDescriptor<H> {
410 let collator = sp_keyring::Sr25519Keyring::Ferdie;
411 let invalid = Hash::zero();
412 let descriptor = make_valid_candidate_descriptor(
413 1.into(),
414 relay_parent,
415 invalid,
416 invalid,
417 invalid,
418 invalid,
419 invalid,
420 collator,
421 );
422 descriptor
423}
424
425pub fn dummy_candidate_descriptor_v2<H: AsRef<[u8]> + Copy + Default>(
427 relay_parent: H,
428) -> CandidateDescriptorV2<H> {
429 let invalid = Hash::zero();
430 let descriptor = make_valid_candidate_descriptor_v2(
431 1.into(),
432 relay_parent,
433 CoreIndex(1),
434 1,
435 invalid,
436 invalid,
437 invalid,
438 invalid,
439 invalid,
440 );
441 descriptor
442}
443
444pub fn dummy_candidate_descriptor_v3<H: AsRef<[u8]> + Copy + Default>(
446 relay_parent: H,
447 scheduling_parent: H,
448) -> CandidateDescriptorV2<H> {
449 let invalid = Hash::zero();
450 let descriptor = make_valid_candidate_descriptor_v3(
451 1.into(),
452 relay_parent,
453 CoreIndex(1),
454 1,
455 1,
456 invalid,
457 invalid,
458 invalid,
459 invalid,
460 invalid,
461 scheduling_parent,
462 );
463 descriptor
464}
465
466pub fn dummy_validation_code() -> ValidationCode {
468 ValidationCode(vec![1, 2, 3, 4, 5, 6, 7, 8, 9])
469}
470
471pub fn dummy_head_data() -> HeadData {
473 HeadData(vec![])
474}
475
476pub fn dummy_validator() -> ValidatorId {
478 ValidatorId::from(sr25519::Public::default())
479}
480
481pub fn dummy_collator() -> CollatorId {
487 let mut bytes = [0u8; 32];
488 bytes[8] = 1;
489 CollatorId::from(sr25519::Public::from_raw(bytes))
490}
491
492pub fn dummy_collator_signature() -> CollatorSignature {
495 CollatorSignature::from_slice(&mut (0..64).into_iter().collect::<Vec<_>>().as_slice())
496 .expect("64 bytes; qed")
497}
498
499pub fn zero_collator_signature() -> CollatorSignature {
501 CollatorSignature::from(sr25519::Signature::default())
502}
503
504pub fn dummy_pvd(parent_head: HeadData, relay_parent_number: u32) -> PersistedValidationData {
506 PersistedValidationData {
507 parent_head,
508 relay_parent_number,
509 max_pov_size: MAX_POV_SIZE,
510 relay_parent_storage_root: dummy_hash(),
511 }
512}
513
514pub fn dummy_signature() -> polkadot_primitives::ValidatorSignature {
516 sp_core::crypto::UncheckedFrom::unchecked_from([1u8; 64])
517}
518
519pub fn make_candidate(
521 relay_parent_hash: Hash,
522 relay_parent_number: u32,
523 para_id: ParaId,
524 parent_head: HeadData,
525 head_data: HeadData,
526 validation_code_hash: ValidationCodeHash,
527) -> (CommittedCandidateReceiptV2, PersistedValidationData) {
528 let pvd = dummy_pvd(parent_head, relay_parent_number);
529 let commitments = CandidateCommitments {
530 head_data,
531 horizontal_messages: Default::default(),
532 upward_messages: Default::default(),
533 new_validation_code: None,
534 processed_downward_messages: 0,
535 hrmp_watermark: relay_parent_number,
536 };
537
538 let mut candidate =
539 dummy_candidate_receipt_bad_sig(relay_parent_hash, Some(Default::default()));
540 candidate.commitments_hash = commitments.hash();
541 candidate.descriptor.para_id = para_id;
542 candidate.descriptor.persisted_validation_data_hash = pvd.hash();
543 candidate.descriptor.validation_code_hash = validation_code_hash;
544 let candidate =
545 CommittedCandidateReceiptV2 { descriptor: candidate.descriptor.into(), commitments };
546
547 (candidate, pvd)
548}
549
550pub fn make_candidate_v2(
552 relay_parent_hash: Hash,
553 relay_parent_number: u32,
554 para_id: ParaId,
555 parent_head: HeadData,
556 head_data: HeadData,
557 validation_code_hash: ValidationCodeHash,
558) -> (CommittedCandidateReceiptV2, PersistedValidationData) {
559 let pvd = dummy_pvd(parent_head, relay_parent_number);
560 let commitments = CandidateCommitments {
561 head_data,
562 horizontal_messages: Default::default(),
563 upward_messages: Default::default(),
564 new_validation_code: None,
565 processed_downward_messages: 0,
566 hrmp_watermark: relay_parent_number,
567 };
568
569 let mut descriptor = dummy_candidate_descriptor_v2(relay_parent_hash);
570 descriptor.set_para_id(para_id);
571 descriptor.set_persisted_validation_data_hash(pvd.hash());
572 descriptor.set_validation_code_hash(validation_code_hash);
573 let candidate = CommittedCandidateReceiptV2 { descriptor, commitments };
574
575 (candidate, pvd)
576}
577
578pub fn make_candidate_v3(
580 relay_parent_hash: Hash,
581 relay_parent_number: u32,
582 scheduling_parent: Hash,
583 para_id: ParaId,
584 parent_head: HeadData,
585 head_data: HeadData,
586 validation_code_hash: ValidationCodeHash,
587) -> (CommittedCandidateReceiptV2, PersistedValidationData) {
588 let pvd = dummy_pvd(parent_head, relay_parent_number);
589 let commitments = CandidateCommitments {
590 head_data: head_data.clone(),
591 horizontal_messages: Default::default(),
592 upward_messages: Default::default(),
593 new_validation_code: None,
594 processed_downward_messages: 0,
595 hrmp_watermark: relay_parent_number,
596 };
597
598 let descriptor = CandidateDescriptorV2::new_v3(
599 para_id,
600 relay_parent_hash,
601 CoreIndex(0),
602 1, 1, pvd.hash(),
605 Hash::repeat_byte(1), Hash::repeat_byte(1), head_data.hash(),
608 validation_code_hash,
609 scheduling_parent,
610 );
611 let candidate = CommittedCandidateReceiptV2 { descriptor, commitments };
612
613 (candidate, pvd)
614}
615
616pub fn make_valid_candidate_descriptor<H: AsRef<[u8]>>(
619 para_id: ParaId,
620 relay_parent: H,
621 persisted_validation_data_hash: Hash,
622 pov_hash: Hash,
623 validation_code_hash: impl Into<ValidationCodeHash>,
624 para_head: Hash,
625 erasure_root: Hash,
626 collator: Sr25519Keyring,
627) -> CandidateDescriptor<H> {
628 let validation_code_hash = validation_code_hash.into();
629 let payload = collator_signature_payload::<H>(
630 &relay_parent,
631 ¶_id,
632 &persisted_validation_data_hash,
633 &pov_hash,
634 &validation_code_hash,
635 );
636
637 let signature = collator.sign(&payload).into();
638 let descriptor = CandidateDescriptor {
639 para_id,
640 relay_parent,
641 collator: collator.public().into(),
642 persisted_validation_data_hash,
643 pov_hash,
644 erasure_root,
645 signature,
646 para_head,
647 validation_code_hash,
648 };
649
650 assert!(descriptor.check_collator_signature().is_ok());
651 descriptor
652}
653
654pub fn make_valid_candidate_descriptor_v2<H: AsRef<[u8]> + Copy + Default>(
656 para_id: ParaId,
657 relay_parent: H,
658 core_index: CoreIndex,
659 session_index: SessionIndex,
660 persisted_validation_data_hash: Hash,
661 pov_hash: Hash,
662 validation_code_hash: impl Into<ValidationCodeHash>,
663 para_head: Hash,
664 erasure_root: Hash,
665) -> CandidateDescriptorV2<H> {
666 let validation_code_hash = validation_code_hash.into();
667
668 let descriptor = CandidateDescriptorV2::new(
669 para_id,
670 relay_parent,
671 core_index,
672 session_index,
673 persisted_validation_data_hash,
674 pov_hash,
675 erasure_root,
676 para_head,
677 validation_code_hash,
678 );
679
680 descriptor
681}
682
683pub fn make_valid_candidate_descriptor_v3<H: AsRef<[u8]> + Copy + Default>(
688 para_id: ParaId,
689 relay_parent: H,
690 core_index: CoreIndex,
691 session_index: SessionIndex,
692 scheduling_session_index: SessionIndex,
693 persisted_validation_data_hash: Hash,
694 pov_hash: Hash,
695 validation_code_hash: impl Into<ValidationCodeHash>,
696 para_head: Hash,
697 erasure_root: Hash,
698 scheduling_parent: H,
699) -> CandidateDescriptorV2<H> {
700 let validation_code_hash = validation_code_hash.into();
701
702 CandidateDescriptorV2::new_v3(
703 para_id,
704 relay_parent,
705 core_index,
706 session_index,
707 scheduling_session_index,
708 persisted_validation_data_hash,
709 pov_hash,
710 erasure_root,
711 para_head,
712 validation_code_hash,
713 scheduling_parent,
714 )
715}
716
717pub fn resign_candidate_descriptor_with_collator<H: AsRef<[u8]>>(
719 descriptor: &mut CandidateDescriptor<H>,
720 collator: Sr25519Keyring,
721) {
722 descriptor.collator = collator.public().into();
723 let payload = collator_signature_payload::<H>(
724 &descriptor.relay_parent,
725 &descriptor.para_id,
726 &descriptor.persisted_validation_data_hash,
727 &descriptor.pov_hash,
728 &descriptor.validation_code_hash,
729 );
730 let signature = collator.sign(&payload).into();
731 descriptor.signature = signature;
732}
733
734pub fn validator_pubkeys(val_ids: &[Sr25519Keyring]) -> Vec<ValidatorId> {
736 val_ids.iter().map(|v| v.public().into()).collect()
737}
738
739pub struct TestCandidateBuilder {
741 pub para_id: ParaId,
742 pub pov_hash: Hash,
743 pub relay_parent: Hash,
744 pub commitments_hash: Hash,
745 pub core_index: CoreIndex,
746 pub scheduling_parent: Hash,
747 pub para_head: Hash,
748}
749
750impl std::default::Default for TestCandidateBuilder {
751 fn default() -> Self {
752 let zeros = Hash::zero();
753 Self {
754 para_id: 0.into(),
755 pov_hash: zeros,
756 relay_parent: zeros,
757 commitments_hash: zeros,
758 core_index: CoreIndex(0),
759 scheduling_parent: zeros,
760 para_head: zeros,
761 }
762 }
763}
764
765impl TestCandidateBuilder {
766 pub fn build(self) -> CandidateReceiptV2 {
768 let mut descriptor = dummy_candidate_descriptor_v2(self.relay_parent);
769 descriptor.set_para_id(self.para_id);
770 descriptor.set_pov_hash(self.pov_hash);
771 descriptor.set_core_index(self.core_index);
772 CandidateReceiptV2 { descriptor, commitments_hash: self.commitments_hash }
773 }
774
775 pub fn build_v3(self) -> CandidateReceiptV2 {
777 let mut descriptor =
778 dummy_candidate_descriptor_v3(self.relay_parent, self.scheduling_parent);
779 descriptor.set_para_id(self.para_id);
780 descriptor.set_pov_hash(self.pov_hash);
781 descriptor.set_core_index(self.core_index);
782 descriptor.set_para_head(self.para_head);
783 CandidateReceiptV2 { descriptor, commitments_hash: self.commitments_hash }
784 }
785}
786
787pub struct AlwaysZeroRng;
790
791impl Default for AlwaysZeroRng {
792 fn default() -> Self {
793 Self {}
794 }
795}
796impl rand::RngCore for AlwaysZeroRng {
797 fn next_u32(&mut self) -> u32 {
798 0_u32
799 }
800
801 fn next_u64(&mut self) -> u64 {
802 0_u64
803 }
804
805 fn fill_bytes(&mut self, dest: &mut [u8]) {
806 for element in dest.iter_mut() {
807 *element = 0_u8;
808 }
809 }
810
811 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
812 self.fill_bytes(dest);
813 Ok(())
814 }
815}
816
817#[cfg(test)]
818mod candidate_receipt_tests {
819
820 use super::*;
821 use bitvec::prelude::*;
822 use polkadot_primitives::{
823 transpose_claim_queue, v9::CandidateUMPSignals, BackedCandidate,
824 CandidateDescriptorVersion, ClaimQueueOffset, CommittedCandidateReceiptError, CoreSelector,
825 UMPSignal, UMP_SEPARATOR,
826 };
827 use std::collections::BTreeMap;
828
829 #[test]
830 fn collator_signature_payload_is_valid() {
831 let h = Hash::default();
833 assert_eq!(h.as_ref().len(), 32);
834
835 let _payload = collator_signature_payload(
836 &Hash::repeat_byte(1),
837 &5u32.into(),
838 &Hash::repeat_byte(2),
839 &Hash::repeat_byte(3),
840 &Hash::repeat_byte(4).into(),
841 );
842 }
843
844 #[test]
845 fn is_binary_compatibile() {
846 let old_ccr = dummy_committed_candidate_receipt(Hash::default());
847 let new_ccr = dummy_committed_candidate_receipt_v2(Hash::default());
848
849 assert_eq!(old_ccr.encoded_size(), new_ccr.encoded_size());
850
851 let encoded_old = old_ccr.encode();
852
853 let new_ccr: CommittedCandidateReceiptV2 =
855 Decode::decode(&mut encoded_old.as_slice()).unwrap();
856
857 assert_eq!(old_ccr.hash(), new_ccr.hash());
859 }
860
861 #[test]
862 fn test_from_v1_descriptor() {
863 let mut old_ccr = dummy_committed_candidate_receipt(Hash::default()).to_plain();
864 old_ccr.descriptor.collator = dummy_collator();
865 old_ccr.descriptor.signature = dummy_collator_signature();
866
867 let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::default()).to_plain();
868
869 new_ccr.descriptor = old_ccr.descriptor.clone().into();
871
872 assert_eq!(old_ccr.hash(), new_ccr.hash());
874
875 assert_eq!(new_ccr.descriptor.version_old_rules(), CandidateDescriptorVersion::V1);
876 assert_eq!(old_ccr.descriptor.collator, new_ccr.descriptor.collator().unwrap());
877 assert_eq!(old_ccr.descriptor.signature, new_ccr.descriptor.signature().unwrap());
878 }
879
880 #[test]
881 fn invalid_version_descriptor() {
882 let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::default());
883 assert_eq!(new_ccr.descriptor.version_old_rules(), CandidateDescriptorVersion::V2);
884 new_ccr.descriptor.set_version(100);
886
887 let new_ccr: CommittedCandidateReceiptV2 =
889 Decode::decode(&mut new_ccr.encode().as_slice()).unwrap();
890
891 assert_eq!(
892 new_ccr.descriptor.version_old_rules(),
893 CandidateDescriptorVersion::Unknown(100)
894 );
895 assert_eq!(
896 new_ccr.parse_ump_signals(&std::collections::BTreeMap::new()),
897 Err(CommittedCandidateReceiptError::UnknownVersion(100))
898 );
899 }
900
901 #[test]
902 fn test_version2_receipts_decoded_as_v1() {
903 let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::default());
904 new_ccr.descriptor.set_core_index(CoreIndex(123));
905 new_ccr.descriptor.set_para_id(ParaId::new(1000));
906
907 new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]);
909 new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]);
910
911 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
913
914 new_ccr
916 .commitments
917 .upward_messages
918 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
919
920 let encoded_ccr = new_ccr.encode();
921 let decoded_ccr: CommittedCandidateReceipt =
922 Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
923
924 assert_eq!(decoded_ccr.descriptor.relay_parent, new_ccr.descriptor.relay_parent());
925 assert_eq!(decoded_ccr.descriptor.para_id, new_ccr.descriptor.para_id());
926
927 assert_eq!(new_ccr.hash(), decoded_ccr.hash());
928
929 let encoded_ccr = new_ccr.encode();
931 let v2_ccr: CommittedCandidateReceiptV2 =
932 Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
933
934 assert_eq!(v2_ccr.descriptor.core_index(), Some(CoreIndex(123)));
935
936 let mut cq = BTreeMap::new();
937 cq.insert(
938 CoreIndex(123),
939 vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
940 );
941
942 assert!(new_ccr.parse_ump_signals(&transpose_claim_queue(cq)).is_ok());
943
944 assert_eq!(new_ccr.hash(), v2_ccr.hash());
945 }
946
947 #[test]
949 fn test_v1_descriptors_with_ump_signal() {
950 let mut ccr = dummy_committed_candidate_receipt(Hash::default());
951 ccr.descriptor.para_id = ParaId::new(1024);
952 ccr.descriptor.signature = dummy_collator_signature();
954 ccr.descriptor.collator = dummy_collator();
955
956 ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
957 ccr.commitments
958 .upward_messages
959 .force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode());
960
961 ccr.commitments
962 .upward_messages
963 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
964
965 let encoded_ccr: Vec<u8> = ccr.encode();
966
967 let v1_ccr: CommittedCandidateReceiptV2 =
968 Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
969
970 assert_eq!(v1_ccr.descriptor.version_old_rules(), CandidateDescriptorVersion::V1);
971 assert!(!v1_ccr.commitments.ump_signals().unwrap().is_empty());
972
973 let mut cq = BTreeMap::new();
974 cq.insert(CoreIndex(0), vec![v1_ccr.descriptor.para_id()].into());
975 cq.insert(CoreIndex(1), vec![v1_ccr.descriptor.para_id()].into());
976
977 assert_eq!(v1_ccr.descriptor.core_index(), None);
978
979 assert_eq!(
980 v1_ccr.parse_ump_signals(&transpose_claim_queue(cq)),
981 Err(CommittedCandidateReceiptError::UMPSignalWithV1Descriptor)
982 );
983 }
984
985 #[test]
986 fn test_core_select_is_optional() {
987 let mut old_ccr = dummy_committed_candidate_receipt(Hash::default());
989 old_ccr.descriptor.para_id = ParaId::new(1000);
990 let encoded_ccr: Vec<u8> = old_ccr.encode();
991
992 let new_ccr: CommittedCandidateReceiptV2 =
993 Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
994
995 let mut cq = BTreeMap::new();
996 cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
997
998 assert!(new_ccr.parse_ump_signals(&transpose_claim_queue(cq)).is_ok());
1001
1002 let mut cq = BTreeMap::new();
1003 cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
1004 cq.insert(CoreIndex(1), vec![new_ccr.descriptor.para_id()].into());
1005
1006 assert!(new_ccr.parse_ump_signals(&transpose_claim_queue(cq)).is_ok());
1009
1010 old_ccr.descriptor.signature = dummy_collator_signature();
1012 old_ccr.descriptor.collator = dummy_collator();
1013
1014 let old_ccr_hash = old_ccr.hash();
1015
1016 let encoded_ccr: Vec<u8> = old_ccr.encode();
1017
1018 let new_ccr: CommittedCandidateReceiptV2 =
1019 Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1020
1021 assert_eq!(new_ccr.descriptor.signature(), Some(old_ccr.descriptor.signature));
1022 assert_eq!(new_ccr.descriptor.collator(), Some(old_ccr.descriptor.collator));
1023
1024 assert_eq!(new_ccr.descriptor.core_index(), None);
1025 assert_eq!(new_ccr.descriptor.para_id(), ParaId::new(1000));
1026
1027 assert_eq!(old_ccr_hash, new_ccr.hash());
1028 }
1029
1030 #[test]
1031 fn test_ump_commitments() {
1037 let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::default());
1038 new_ccr.descriptor.set_core_index(CoreIndex(123));
1039 new_ccr.descriptor.set_para_id(ParaId::new(1000));
1040
1041 let mut cq = BTreeMap::new();
1042 cq.insert(
1043 CoreIndex(123),
1044 vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1045 );
1046 let cq = transpose_claim_queue(cq);
1047
1048 new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]);
1052 new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]);
1053
1054 assert_eq!(new_ccr.parse_ump_signals(&cq), Ok(CandidateUMPSignals::dummy(None, None)));
1055
1056 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1058
1059 assert_eq!(new_ccr.parse_ump_signals(&cq), Ok(CandidateUMPSignals::dummy(None, None)));
1060
1061 {
1063 let mut new_ccr = new_ccr.clone();
1064 new_ccr
1065 .commitments
1066 .upward_messages
1067 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1068
1069 assert_eq!(
1070 new_ccr.parse_ump_signals(&cq),
1071 Ok(CandidateUMPSignals::dummy(Some((CoreSelector(0), ClaimQueueOffset(1))), None))
1072 );
1073 }
1074
1075 {
1076 let mut new_ccr = new_ccr.clone();
1077
1078 new_ccr
1080 .commitments
1081 .upward_messages
1082 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1083
1084 assert_eq!(
1085 new_ccr.parse_ump_signals(&cq),
1086 Ok(CandidateUMPSignals::dummy(None, Some(vec![1, 2, 3].try_into().unwrap())))
1087 );
1088
1089 new_ccr
1092 .commitments
1093 .upward_messages
1094 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1095
1096 assert_eq!(
1097 new_ccr.parse_ump_signals(&cq),
1098 Ok(CandidateUMPSignals::dummy(
1099 Some((CoreSelector(0), ClaimQueueOffset(1))),
1100 Some(vec![1, 2, 3].try_into().unwrap())
1101 ))
1102 );
1103 }
1104
1105 new_ccr
1107 .commitments
1108 .upward_messages
1109 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1110 new_ccr
1111 .commitments
1112 .upward_messages
1113 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1114
1115 assert_eq!(
1116 new_ccr.parse_ump_signals(&cq),
1117 Ok(CandidateUMPSignals::dummy(
1118 Some((CoreSelector(0), ClaimQueueOffset(1))),
1119 Some(vec![1, 2, 3].try_into().unwrap())
1120 ))
1121 );
1122 }
1123
1124 #[test]
1125 fn test_invalid_ump_commitments() {
1126 let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::default());
1127 new_ccr.descriptor.set_core_index(CoreIndex(0));
1128 new_ccr.descriptor.set_para_id(ParaId::new(1000));
1129
1130 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1131
1132 let mut cq = BTreeMap::new();
1133 cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
1134 let cq = transpose_claim_queue(cq);
1135
1136 new_ccr
1138 .commitments
1139 .upward_messages
1140 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1141
1142 new_ccr.commitments.upward_messages.force_push(vec![0, 13, 200].encode());
1144
1145 assert_eq!(
1147 new_ccr.parse_ump_signals(&cq),
1148 Err(CommittedCandidateReceiptError::UmpSignalDecode)
1149 );
1150 assert_eq!(
1151 new_ccr.commitments.ump_signals(),
1152 Err(CommittedCandidateReceiptError::UmpSignalDecode)
1153 );
1154
1155 {
1157 new_ccr.commitments.upward_messages.clear();
1160 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1161 new_ccr
1162 .commitments
1163 .upward_messages
1164 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1165
1166 let mut cq = BTreeMap::new();
1167 cq.insert(
1168 CoreIndex(0),
1169 vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1170 );
1171 cq.insert(
1172 CoreIndex(100),
1173 vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1174 );
1175 let cq = transpose_claim_queue(cq);
1176
1177 assert_eq!(
1178 new_ccr.parse_ump_signals(&cq),
1179 Ok(CandidateUMPSignals::dummy(None, Some(vec![1, 2, 3].try_into().unwrap())))
1180 );
1181
1182 new_ccr.descriptor.set_core_index(CoreIndex(1));
1183 assert_eq!(
1184 new_ccr.parse_ump_signals(&cq),
1185 Err(CommittedCandidateReceiptError::InvalidCoreIndex)
1186 );
1187 new_ccr.descriptor.set_core_index(CoreIndex(0));
1188
1189 new_ccr
1190 .commitments
1191 .upward_messages
1192 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1193
1194 assert_eq!(
1196 new_ccr.parse_ump_signals(&transpose_claim_queue(Default::default())),
1197 Err(CommittedCandidateReceiptError::NoAssignment)
1198 );
1199
1200 new_ccr.descriptor.set_core_index(CoreIndex(1));
1202 assert_eq!(
1203 new_ccr.parse_ump_signals(&cq),
1204 Err(CommittedCandidateReceiptError::CoreIndexMismatch {
1205 descriptor: CoreIndex(1),
1206 commitments: CoreIndex(0),
1207 })
1208 );
1209 }
1210
1211 new_ccr.descriptor.set_core_index(CoreIndex(0));
1212
1213 new_ccr.commitments.upward_messages.clear();
1215 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1216 new_ccr
1217 .commitments
1218 .upward_messages
1219 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1220 new_ccr
1221 .commitments
1222 .upward_messages
1223 .force_push(UMPSignal::ApprovedPeer(vec![4, 5].try_into().unwrap()).encode());
1224
1225 assert_eq!(
1226 new_ccr.parse_ump_signals(&cq),
1227 Err(CommittedCandidateReceiptError::DuplicateUMPSignal)
1228 );
1229
1230 new_ccr.commitments.upward_messages.clear();
1232 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1233 new_ccr
1234 .commitments
1235 .upward_messages
1236 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1237 new_ccr
1238 .commitments
1239 .upward_messages
1240 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(0)).encode());
1241 new_ccr
1242 .commitments
1243 .upward_messages
1244 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1245
1246 assert_eq!(
1247 new_ccr.parse_ump_signals(&cq),
1248 Err(CommittedCandidateReceiptError::TooManyUMPSignals)
1249 );
1250 }
1251
1252 #[test]
1253 fn test_backed_candidate_injected_core_index() {
1254 let initial_validator_indices = bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1];
1255 let mut candidate = BackedCandidate::new(
1256 dummy_committed_candidate_receipt_v2(Hash::default()),
1257 vec![],
1258 initial_validator_indices.clone(),
1259 CoreIndex(10),
1260 );
1261
1262 candidate
1264 .set_validator_indices_and_core_index(initial_validator_indices.clone().into(), None);
1265 let (validator_indices, core_index) = candidate.validator_indices_and_core_index();
1266 assert_eq!(validator_indices, initial_validator_indices.as_bitslice());
1267 assert!(core_index.is_none());
1268
1269 candidate.set_validator_indices_and_core_index(
1272 bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1, 0, 1, 0, 1, 0].into(),
1273 None,
1274 );
1275
1276 let (validator_indices, core_index) = candidate.validator_indices_and_core_index();
1277 assert_eq!(validator_indices, bitvec![u8, bitvec::order::Lsb0; 0].as_bitslice());
1278 assert!(core_index.is_some());
1279
1280 let mut candidate = BackedCandidate::new(
1282 dummy_committed_candidate_receipt_v2(Hash::default()),
1283 vec![],
1284 bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1],
1285 CoreIndex(10),
1286 );
1287 let (validator_indices, core_index) = candidate.validator_indices_and_core_index();
1288 assert_eq!(validator_indices, bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1]);
1289 assert_eq!(core_index, Some(CoreIndex(10)));
1290
1291 let encoded_validator_indices = candidate.raw_validator_indices();
1292 candidate.set_validator_indices_and_core_index(validator_indices.into(), core_index);
1293 assert_eq!(candidate.raw_validator_indices(), encoded_validator_indices);
1294 }
1295}