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}
747
748impl std::default::Default for TestCandidateBuilder {
749 fn default() -> Self {
750 let zeros = Hash::zero();
751 Self {
752 para_id: 0.into(),
753 pov_hash: zeros,
754 relay_parent: zeros,
755 commitments_hash: zeros,
756 core_index: CoreIndex(0),
757 }
758 }
759}
760
761impl TestCandidateBuilder {
762 pub fn build(self) -> CandidateReceiptV2 {
764 let mut descriptor = dummy_candidate_descriptor_v2(self.relay_parent);
765 descriptor.set_para_id(self.para_id);
766 descriptor.set_pov_hash(self.pov_hash);
767 descriptor.set_core_index(self.core_index);
768 CandidateReceiptV2 { descriptor, commitments_hash: self.commitments_hash }
769 }
770}
771
772pub struct AlwaysZeroRng;
775
776impl Default for AlwaysZeroRng {
777 fn default() -> Self {
778 Self {}
779 }
780}
781impl rand::RngCore for AlwaysZeroRng {
782 fn next_u32(&mut self) -> u32 {
783 0_u32
784 }
785
786 fn next_u64(&mut self) -> u64 {
787 0_u64
788 }
789
790 fn fill_bytes(&mut self, dest: &mut [u8]) {
791 for element in dest.iter_mut() {
792 *element = 0_u8;
793 }
794 }
795
796 fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
797 self.fill_bytes(dest);
798 Ok(())
799 }
800}
801
802#[cfg(test)]
803mod candidate_receipt_tests {
804
805 use super::*;
806 use bitvec::prelude::*;
807 use polkadot_primitives::{
808 transpose_claim_queue, v9::CandidateUMPSignals, BackedCandidate,
809 CandidateDescriptorVersion, ClaimQueueOffset, CommittedCandidateReceiptError, CoreSelector,
810 UMPSignal, UMP_SEPARATOR,
811 };
812 use std::collections::BTreeMap;
813
814 #[test]
815 fn collator_signature_payload_is_valid() {
816 let h = Hash::default();
818 assert_eq!(h.as_ref().len(), 32);
819
820 let _payload = collator_signature_payload(
821 &Hash::repeat_byte(1),
822 &5u32.into(),
823 &Hash::repeat_byte(2),
824 &Hash::repeat_byte(3),
825 &Hash::repeat_byte(4).into(),
826 );
827 }
828
829 #[test]
830 fn is_binary_compatibile() {
831 let old_ccr = dummy_committed_candidate_receipt(Hash::default());
832 let new_ccr = dummy_committed_candidate_receipt_v2(Hash::default());
833
834 assert_eq!(old_ccr.encoded_size(), new_ccr.encoded_size());
835
836 let encoded_old = old_ccr.encode();
837
838 let new_ccr: CommittedCandidateReceiptV2 =
840 Decode::decode(&mut encoded_old.as_slice()).unwrap();
841
842 assert_eq!(old_ccr.hash(), new_ccr.hash());
844 }
845
846 #[test]
847 fn test_from_v1_descriptor() {
848 let mut old_ccr = dummy_committed_candidate_receipt(Hash::default()).to_plain();
849 old_ccr.descriptor.collator = dummy_collator();
850 old_ccr.descriptor.signature = dummy_collator_signature();
851
852 let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::default()).to_plain();
853
854 new_ccr.descriptor = old_ccr.descriptor.clone().into();
856
857 assert_eq!(old_ccr.hash(), new_ccr.hash());
859
860 assert_eq!(new_ccr.descriptor.version_old_rules(), CandidateDescriptorVersion::V1);
861 assert_eq!(old_ccr.descriptor.collator, new_ccr.descriptor.collator().unwrap());
862 assert_eq!(old_ccr.descriptor.signature, new_ccr.descriptor.signature().unwrap());
863 }
864
865 #[test]
866 fn invalid_version_descriptor() {
867 let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::default());
868 assert_eq!(new_ccr.descriptor.version_old_rules(), CandidateDescriptorVersion::V2);
869 new_ccr.descriptor.set_version(100);
871
872 let new_ccr: CommittedCandidateReceiptV2 =
874 Decode::decode(&mut new_ccr.encode().as_slice()).unwrap();
875
876 assert_eq!(new_ccr.descriptor.version_old_rules(), CandidateDescriptorVersion::Unknown);
877 assert_eq!(
878 new_ccr.parse_ump_signals(&std::collections::BTreeMap::new()),
879 Err(CommittedCandidateReceiptError::UnknownVersion(100))
880 );
881 }
882
883 #[test]
884 fn test_version2_receipts_decoded_as_v1() {
885 let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::default());
886 new_ccr.descriptor.set_core_index(CoreIndex(123));
887 new_ccr.descriptor.set_para_id(ParaId::new(1000));
888
889 new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]);
891 new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]);
892
893 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
895
896 new_ccr
898 .commitments
899 .upward_messages
900 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
901
902 let encoded_ccr = new_ccr.encode();
903 let decoded_ccr: CommittedCandidateReceipt =
904 Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
905
906 assert_eq!(decoded_ccr.descriptor.relay_parent, new_ccr.descriptor.relay_parent());
907 assert_eq!(decoded_ccr.descriptor.para_id, new_ccr.descriptor.para_id());
908
909 assert_eq!(new_ccr.hash(), decoded_ccr.hash());
910
911 let encoded_ccr = new_ccr.encode();
913 let v2_ccr: CommittedCandidateReceiptV2 =
914 Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
915
916 assert_eq!(v2_ccr.descriptor.core_index(), Some(CoreIndex(123)));
917
918 let mut cq = BTreeMap::new();
919 cq.insert(
920 CoreIndex(123),
921 vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
922 );
923
924 assert!(new_ccr.parse_ump_signals(&transpose_claim_queue(cq)).is_ok());
925
926 assert_eq!(new_ccr.hash(), v2_ccr.hash());
927 }
928
929 #[test]
931 fn test_v1_descriptors_with_ump_signal() {
932 let mut ccr = dummy_committed_candidate_receipt(Hash::default());
933 ccr.descriptor.para_id = ParaId::new(1024);
934 ccr.descriptor.signature = dummy_collator_signature();
936 ccr.descriptor.collator = dummy_collator();
937
938 ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
939 ccr.commitments
940 .upward_messages
941 .force_push(UMPSignal::SelectCore(CoreSelector(1), ClaimQueueOffset(1)).encode());
942
943 ccr.commitments
944 .upward_messages
945 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
946
947 let encoded_ccr: Vec<u8> = ccr.encode();
948
949 let v1_ccr: CommittedCandidateReceiptV2 =
950 Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
951
952 assert_eq!(v1_ccr.descriptor.version_old_rules(), CandidateDescriptorVersion::V1);
953 assert!(!v1_ccr.commitments.ump_signals().unwrap().is_empty());
954
955 let mut cq = BTreeMap::new();
956 cq.insert(CoreIndex(0), vec![v1_ccr.descriptor.para_id()].into());
957 cq.insert(CoreIndex(1), vec![v1_ccr.descriptor.para_id()].into());
958
959 assert_eq!(v1_ccr.descriptor.core_index(), None);
960
961 assert_eq!(
962 v1_ccr.parse_ump_signals(&transpose_claim_queue(cq)),
963 Err(CommittedCandidateReceiptError::UMPSignalWithV1Descriptor)
964 );
965 }
966
967 #[test]
968 fn test_core_select_is_optional() {
969 let mut old_ccr = dummy_committed_candidate_receipt(Hash::default());
971 old_ccr.descriptor.para_id = ParaId::new(1000);
972 let encoded_ccr: Vec<u8> = old_ccr.encode();
973
974 let new_ccr: CommittedCandidateReceiptV2 =
975 Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
976
977 let mut cq = BTreeMap::new();
978 cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
979
980 assert!(new_ccr.parse_ump_signals(&transpose_claim_queue(cq)).is_ok());
983
984 let mut cq = BTreeMap::new();
985 cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
986 cq.insert(CoreIndex(1), vec![new_ccr.descriptor.para_id()].into());
987
988 assert!(new_ccr.parse_ump_signals(&transpose_claim_queue(cq)).is_ok());
991
992 old_ccr.descriptor.signature = dummy_collator_signature();
994 old_ccr.descriptor.collator = dummy_collator();
995
996 let old_ccr_hash = old_ccr.hash();
997
998 let encoded_ccr: Vec<u8> = old_ccr.encode();
999
1000 let new_ccr: CommittedCandidateReceiptV2 =
1001 Decode::decode(&mut encoded_ccr.as_slice()).unwrap();
1002
1003 assert_eq!(new_ccr.descriptor.signature(), Some(old_ccr.descriptor.signature));
1004 assert_eq!(new_ccr.descriptor.collator(), Some(old_ccr.descriptor.collator));
1005
1006 assert_eq!(new_ccr.descriptor.core_index(), None);
1007 assert_eq!(new_ccr.descriptor.para_id(), ParaId::new(1000));
1008
1009 assert_eq!(old_ccr_hash, new_ccr.hash());
1010 }
1011
1012 #[test]
1013 fn test_ump_commitments() {
1019 let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::default());
1020 new_ccr.descriptor.set_core_index(CoreIndex(123));
1021 new_ccr.descriptor.set_para_id(ParaId::new(1000));
1022
1023 let mut cq = BTreeMap::new();
1024 cq.insert(
1025 CoreIndex(123),
1026 vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1027 );
1028 let cq = transpose_claim_queue(cq);
1029
1030 new_ccr.commitments.upward_messages.force_push(vec![0u8; 256]);
1034 new_ccr.commitments.upward_messages.force_push(vec![0xff; 256]);
1035
1036 assert_eq!(new_ccr.parse_ump_signals(&cq), Ok(CandidateUMPSignals::dummy(None, None)));
1037
1038 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1040
1041 assert_eq!(new_ccr.parse_ump_signals(&cq), Ok(CandidateUMPSignals::dummy(None, None)));
1042
1043 {
1045 let mut new_ccr = new_ccr.clone();
1046 new_ccr
1047 .commitments
1048 .upward_messages
1049 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1050
1051 assert_eq!(
1052 new_ccr.parse_ump_signals(&cq),
1053 Ok(CandidateUMPSignals::dummy(Some((CoreSelector(0), ClaimQueueOffset(1))), None))
1054 );
1055 }
1056
1057 {
1058 let mut new_ccr = new_ccr.clone();
1059
1060 new_ccr
1062 .commitments
1063 .upward_messages
1064 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1065
1066 assert_eq!(
1067 new_ccr.parse_ump_signals(&cq),
1068 Ok(CandidateUMPSignals::dummy(None, Some(vec![1, 2, 3].try_into().unwrap())))
1069 );
1070
1071 new_ccr
1074 .commitments
1075 .upward_messages
1076 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1077
1078 assert_eq!(
1079 new_ccr.parse_ump_signals(&cq),
1080 Ok(CandidateUMPSignals::dummy(
1081 Some((CoreSelector(0), ClaimQueueOffset(1))),
1082 Some(vec![1, 2, 3].try_into().unwrap())
1083 ))
1084 );
1085 }
1086
1087 new_ccr
1089 .commitments
1090 .upward_messages
1091 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1092 new_ccr
1093 .commitments
1094 .upward_messages
1095 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1096
1097 assert_eq!(
1098 new_ccr.parse_ump_signals(&cq),
1099 Ok(CandidateUMPSignals::dummy(
1100 Some((CoreSelector(0), ClaimQueueOffset(1))),
1101 Some(vec![1, 2, 3].try_into().unwrap())
1102 ))
1103 );
1104 }
1105
1106 #[test]
1107 fn test_invalid_ump_commitments() {
1108 let mut new_ccr = dummy_committed_candidate_receipt_v2(Hash::default());
1109 new_ccr.descriptor.set_core_index(CoreIndex(0));
1110 new_ccr.descriptor.set_para_id(ParaId::new(1000));
1111
1112 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1113
1114 let mut cq = BTreeMap::new();
1115 cq.insert(CoreIndex(0), vec![new_ccr.descriptor.para_id()].into());
1116 let cq = transpose_claim_queue(cq);
1117
1118 new_ccr
1120 .commitments
1121 .upward_messages
1122 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1123
1124 new_ccr.commitments.upward_messages.force_push(vec![0, 13, 200].encode());
1126
1127 assert_eq!(
1129 new_ccr.parse_ump_signals(&cq),
1130 Err(CommittedCandidateReceiptError::UmpSignalDecode)
1131 );
1132 assert_eq!(
1133 new_ccr.commitments.ump_signals(),
1134 Err(CommittedCandidateReceiptError::UmpSignalDecode)
1135 );
1136
1137 {
1139 new_ccr.commitments.upward_messages.clear();
1142 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1143 new_ccr
1144 .commitments
1145 .upward_messages
1146 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1147
1148 let mut cq = BTreeMap::new();
1149 cq.insert(
1150 CoreIndex(0),
1151 vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1152 );
1153 cq.insert(
1154 CoreIndex(100),
1155 vec![new_ccr.descriptor.para_id(), new_ccr.descriptor.para_id()].into(),
1156 );
1157 let cq = transpose_claim_queue(cq);
1158
1159 assert_eq!(
1160 new_ccr.parse_ump_signals(&cq),
1161 Ok(CandidateUMPSignals::dummy(None, Some(vec![1, 2, 3].try_into().unwrap())))
1162 );
1163
1164 new_ccr.descriptor.set_core_index(CoreIndex(1));
1165 assert_eq!(
1166 new_ccr.parse_ump_signals(&cq),
1167 Err(CommittedCandidateReceiptError::InvalidCoreIndex)
1168 );
1169 new_ccr.descriptor.set_core_index(CoreIndex(0));
1170
1171 new_ccr
1172 .commitments
1173 .upward_messages
1174 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(1)).encode());
1175
1176 assert_eq!(
1178 new_ccr.parse_ump_signals(&transpose_claim_queue(Default::default())),
1179 Err(CommittedCandidateReceiptError::NoAssignment)
1180 );
1181
1182 new_ccr.descriptor.set_core_index(CoreIndex(1));
1184 assert_eq!(
1185 new_ccr.parse_ump_signals(&cq),
1186 Err(CommittedCandidateReceiptError::CoreIndexMismatch {
1187 descriptor: CoreIndex(1),
1188 commitments: CoreIndex(0),
1189 })
1190 );
1191 }
1192
1193 new_ccr.descriptor.set_core_index(CoreIndex(0));
1194
1195 new_ccr.commitments.upward_messages.clear();
1197 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1198 new_ccr
1199 .commitments
1200 .upward_messages
1201 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1202 new_ccr
1203 .commitments
1204 .upward_messages
1205 .force_push(UMPSignal::ApprovedPeer(vec![4, 5].try_into().unwrap()).encode());
1206
1207 assert_eq!(
1208 new_ccr.parse_ump_signals(&cq),
1209 Err(CommittedCandidateReceiptError::DuplicateUMPSignal)
1210 );
1211
1212 new_ccr.commitments.upward_messages.clear();
1214 new_ccr.commitments.upward_messages.force_push(UMP_SEPARATOR);
1215 new_ccr
1216 .commitments
1217 .upward_messages
1218 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1219 new_ccr
1220 .commitments
1221 .upward_messages
1222 .force_push(UMPSignal::SelectCore(CoreSelector(0), ClaimQueueOffset(0)).encode());
1223 new_ccr
1224 .commitments
1225 .upward_messages
1226 .force_push(UMPSignal::ApprovedPeer(vec![1, 2, 3].try_into().unwrap()).encode());
1227
1228 assert_eq!(
1229 new_ccr.parse_ump_signals(&cq),
1230 Err(CommittedCandidateReceiptError::TooManyUMPSignals)
1231 );
1232 }
1233
1234 #[test]
1235 fn test_backed_candidate_injected_core_index() {
1236 let initial_validator_indices = bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1];
1237 let mut candidate = BackedCandidate::new(
1238 dummy_committed_candidate_receipt_v2(Hash::default()),
1239 vec![],
1240 initial_validator_indices.clone(),
1241 CoreIndex(10),
1242 );
1243
1244 candidate
1246 .set_validator_indices_and_core_index(initial_validator_indices.clone().into(), None);
1247 let (validator_indices, core_index) = candidate.validator_indices_and_core_index();
1248 assert_eq!(validator_indices, initial_validator_indices.as_bitslice());
1249 assert!(core_index.is_none());
1250
1251 candidate.set_validator_indices_and_core_index(
1254 bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1, 0, 1, 0, 1, 0].into(),
1255 None,
1256 );
1257
1258 let (validator_indices, core_index) = candidate.validator_indices_and_core_index();
1259 assert_eq!(validator_indices, bitvec![u8, bitvec::order::Lsb0; 0].as_bitslice());
1260 assert!(core_index.is_some());
1261
1262 let mut candidate = BackedCandidate::new(
1264 dummy_committed_candidate_receipt_v2(Hash::default()),
1265 vec![],
1266 bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1],
1267 CoreIndex(10),
1268 );
1269 let (validator_indices, core_index) = candidate.validator_indices_and_core_index();
1270 assert_eq!(validator_indices, bitvec![u8, bitvec::order::Lsb0; 0, 1, 0, 1]);
1271 assert_eq!(core_index, Some(CoreIndex(10)));
1272
1273 let encoded_validator_indices = candidate.raw_validator_indices();
1274 candidate.set_validator_indices_and_core_index(validator_indices.into(), core_index);
1275 assert_eq!(candidate.raw_validator_indices(), encoded_validator_indices);
1276 }
1277}