1use core::{
2 borrow::Borrow,
3 ops::{Add, AddAssign},
4};
5
6use crate::simple::{Hash, MaxDependencies, MaxExtrinsics, MaxImports, MaxWorkItems};
7
8use super::*;
9use simple::OpaqueBlsPublic;
10
11pub trait Wrap {
13 type Wrap<T>: Borrow<T>;
15}
16
17#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct NoWrap;
22
23impl Wrap for NoWrap {
24 type Wrap<T> = T;
25}
26
27#[derive(Copy, Clone, Encode, Decode, Debug, Eq, PartialEq, MaxEncodedLen)]
30pub struct OpaqueValKeyset {
31 pub bandersnatch: OpaqueBandersnatchPublic,
33 pub ed25519: OpaqueEd25519Public,
35 pub bls: OpaqueBlsPublic,
37 pub metadata: OpaqueValidatorMetadata,
39}
40
41impl Default for OpaqueValKeyset {
42 fn default() -> Self {
43 Self {
44 bandersnatch: OpaqueBandersnatchPublic::zero(),
45 ed25519: OpaqueEd25519Public::zero(),
46 bls: OpaqueBlsPublic::zero(),
47 metadata: OpaqueValidatorMetadata::zero(),
48 }
49 }
50}
51
52pub type OpaqueValKeysets = BoundedVec<OpaqueValKeyset, MaxValCount>;
54
55#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
58pub enum RootIdentifier {
59 Direct(SegmentTreeRoot),
61 Indirect(WorkPackageHash),
64}
65
66impl From<SegmentTreeRoot> for RootIdentifier {
67 fn from(root: SegmentTreeRoot) -> Self {
68 Self::Direct(root)
69 }
70}
71impl From<WorkPackageHash> for RootIdentifier {
72 fn from(hash: WorkPackageHash) -> Self {
73 Self::Indirect(hash)
74 }
75}
76impl TryFrom<RootIdentifier> for SegmentTreeRoot {
77 type Error = WorkPackageHash;
78 fn try_from(root: RootIdentifier) -> Result<Self, Self::Error> {
79 match root {
80 RootIdentifier::Direct(root) => Ok(root),
81 RootIdentifier::Indirect(hash) => Err(hash),
82 }
83 }
84}
85
86#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
88pub struct ImportSpec {
89 pub root: RootIdentifier,
91 pub index: u16,
93}
94
95impl Encode for ImportSpec {
96 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
97 let off = match &self.root {
98 RootIdentifier::Direct(root) => {
99 root.encode_to(dest);
100 0
101 },
102 RootIdentifier::Indirect(hash) => {
103 hash.encode_to(dest);
104 1 << 15
105 },
106 };
107 (self.index + off).encode_to(dest);
108 }
109}
110
111impl Decode for ImportSpec {
112 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
113 let h = Hash::decode(input)?;
114 let i = u16::decode(input)?;
115 let root = if i & (1 << 15) == 0 {
116 SegmentTreeRoot::from(h).into()
117 } else {
118 WorkPackageHash::from(h).into()
119 };
120 Ok(Self { root, index: i & !(1 << 15) })
121 }
122
123 fn encoded_fixed_size() -> Option<usize> {
124 Some(core::mem::size_of::<Hash>() + core::mem::size_of::<u16>())
125 }
126}
127
128impl MaxEncodedLen for ImportSpec {
129 fn max_encoded_len() -> usize {
130 Hash::max_encoded_len() + u16::max_encoded_len()
131 }
132}
133
134#[derive(Clone, Encode, Decode, MaxEncodedLen, Debug)]
136pub struct ExtrinsicSpec {
137 pub hash: ExtrinsicHash,
139 pub len: u32,
141}
142
143pub type WrappedWorkItems<W> = BoundedVec<<W as Wrap>::Wrap<WorkItem>, MaxWorkItems>;
146
147pub type WorkItems = WrappedWorkItems<NoWrap>;
150
151#[derive(Clone, Encode, Decode, MaxEncodedLen, Debug)]
154pub struct WorkItem {
155 pub service: ServiceId,
157 pub code_hash: CodeHash,
160 pub refine_gas_limit: UnsignedGas,
162 pub accumulate_gas_limit: UnsignedGas,
164 pub export_count: u16,
166 pub payload: WorkPayload,
168 pub import_segments: WorkItemImportsVec,
170 pub extrinsics: BoundedVec<ExtrinsicSpec, MaxExtrinsics>,
172}
173
174impl WorkItem {
175 pub fn extrinsic_size(&self) -> u32 {
179 self.extrinsics
180 .iter()
181 .map(|xt| xt.len)
182 .fold(0u32, |sum, len| sum.saturating_add(len))
183 }
184}
185
186pub type WorkItemImportsVec = BoundedVec<ImportSpec, MaxImports>;
188
189#[derive(Clone, Encode, Decode, Debug, Eq, PartialEq)]
191pub struct RefineContext {
192 pub anchor: HeaderHash,
195 pub anchor_slot: Slot,
197 pub state_root: StateRootHash,
199 pub beefy_root: MmrPeakHash,
201 pub lookup_anchor: HeaderHash,
205 pub lookup_anchor_slot: Slot,
209 pub lookup_anchor_state_root: StateRootHash,
212 pub prerequisites: VecSet<WorkPackageHash>,
215}
216
217impl MaxEncodedLen for RefineContext {
218 fn max_encoded_len() -> usize {
219 HeaderHash::max_encoded_len() + Slot::max_encoded_len() + StateRootHash::max_encoded_len() + MmrPeakHash::max_encoded_len() + HeaderHash::max_encoded_len() + Slot::max_encoded_len() + StateRootHash::max_encoded_len() + BoundedVec::<WorkPackageHash, MaxDependencies>::max_encoded_len() }
228}
229
230impl RefineContext {
231 #[doc(hidden)]
232 pub fn largest() -> Self {
233 Self {
234 anchor: Default::default(),
235 anchor_slot: Slot::MAX,
236 state_root: Default::default(),
237 beefy_root: Default::default(),
238 lookup_anchor: Default::default(),
239 lookup_anchor_slot: Slot::MAX,
240 lookup_anchor_state_root: Default::default(),
241 prerequisites: (0..max_dependencies()).map(|i| [i as u8; 32].into()).collect(),
242 }
243 }
244}
245
246#[derive(Clone, Debug)]
253pub struct WrappedWorkPackage<W: Wrap> {
254 pub authorization: Authorization,
256 pub auth_code_host: ServiceId,
258 pub authorizer: Authorizer,
260 pub context: W::Wrap<RefineContext>,
262 pub items: W::Wrap<WrappedWorkItems<W>>,
264}
265
266impl<W: Wrap> WrappedWorkPackage<W> {
267 pub fn extrinsic_count(&self) -> u32 {
269 self.items
270 .borrow()
271 .iter()
272 .map(|item| item.borrow().extrinsics.len() as u32)
273 .sum()
274 }
275
276 pub fn saturated_extrinsic_size(&self) -> u32 {
280 self.items
281 .borrow()
282 .iter()
283 .map(|item| item.borrow().extrinsic_size())
284 .fold(0u32, |sum, size| sum.saturating_add(size))
285 }
286
287 pub fn import_specs(&self) -> impl Iterator<Item = &ImportSpec> {
289 self.items.borrow().iter().flat_map(|item| item.borrow().import_segments.iter())
290 }
291
292 pub fn import_count(&self) -> u32 {
294 self.items
295 .borrow()
296 .iter()
297 .map(|item| item.borrow().import_segments.len() as u32)
298 .sum()
299 }
300
301 pub fn export_count(&self) -> u32 {
303 self.items.borrow().iter().map(|item| item.borrow().export_count as u32).sum()
304 }
305
306 pub fn dependency_count(&self) -> u32 {
308 let mut indirect = VecSet::new();
309 for spec in self.import_specs() {
310 if let RootIdentifier::Indirect(wph) = &spec.root {
311 indirect.insert(wph);
312 }
313 }
314 (self.context.borrow().prerequisites.len() + indirect.len()) as u32
315 }
316}
317
318impl<W: Wrap> Encode for WrappedWorkPackage<W>
319where
320 W::Wrap<RefineContext>: Encode,
321 W::Wrap<WrappedWorkItems<W>>: Encode,
322{
323 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
324 self.auth_code_host.encode_to(dest);
325 self.authorizer.code_hash.encode_to(dest);
326 self.context.encode_to(dest);
327 self.authorization.encode_to(dest);
328 self.authorizer.config.encode_to(dest);
329 self.items.encode_to(dest);
330 }
331}
332
333impl<W: Wrap> Decode for WrappedWorkPackage<W>
334where
335 W::Wrap<RefineContext>: Decode,
336 W::Wrap<WrappedWorkItems<W>>: Decode,
337{
338 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
339 let auth_code_host = ServiceId::decode(input)?;
340 let auth_code_hash = CodeHash::decode(input)?;
341 let context = W::Wrap::<RefineContext>::decode(input)?;
342 let authorization = Authorization::decode(input)?;
343 let auth_config = AuthConfig::decode(input)?;
344 let items = W::Wrap::<WrappedWorkItems<W>>::decode(input)?;
345 Ok(Self {
346 authorization,
347 auth_code_host,
348 authorizer: Authorizer { code_hash: auth_code_hash, config: auth_config },
349 context,
350 items,
351 })
352 }
353}
354
355impl<W: Wrap> MaxEncodedLen for WrappedWorkPackage<W>
356where
357 Self: Encode,
358{
359 fn max_encoded_len() -> usize {
372 let mut max = Authorization::max_encoded_len() +
376 ServiceId::max_encoded_len() + Authorizer::max_encoded_len() +
378 RefineContext::max_encoded_len() +
379 WorkItems::max_encoded_len();
380
381 let max_inputs = 2 + max_work_items();
385 max -= (max_inputs - 1) * (max_input() as usize);
386
387 max -= (max_work_items() - 1) * (max_imports() as usize) * ImportSpec::max_encoded_len();
391
392 max -=
396 (max_work_items() - 1) * (max_extrinsics() as usize) * ExtrinsicSpec::max_encoded_len();
397
398 max
399 }
400}
401
402pub type WorkPackage = WrappedWorkPackage<NoWrap>;
406
407#[derive(Clone, Encode, Decode, MaxEncodedLen, Debug)]
410pub struct Authorizer {
411 pub code_hash: CodeHash,
413 pub config: AuthConfig,
415}
416
417impl Authorizer {
418 pub fn any() -> Self {
419 Self { code_hash: CodeHash::zero(), config: Default::default() }
420 }
421
422 pub fn with_concat<R>(&self, f: impl Fn(&[u8]) -> R) -> R {
423 f(&[&self.code_hash.0[..], &self.config[..]].concat()[..])
424 }
425
426 pub fn hash(&self, hasher: impl Fn(&[u8]) -> Hash) -> AuthorizerHash {
427 self.with_concat(hasher).into()
428 }
429}
430
431#[derive(Clone, Encode, Decode, MaxEncodedLen, Debug, Eq, PartialEq)]
438#[doc(hidden)]
439pub enum WorkError {
440 OutOfGas = 1,
442 Panic = 2,
444 BadExports = 3,
446 OutputOversize = 4,
448 BadCode = 5,
452 CodeOversize = 6,
454}
455
456#[derive(Copy, Clone, Encode, Decode, MaxEncodedLen, Debug, Eq, PartialEq, Default)]
459#[doc(hidden)]
460pub struct RefineLoad {
461 #[codec(compact)]
463 pub gas_used: UnsignedGas,
464 #[codec(compact)]
466 pub imports: u16,
467 #[codec(compact)]
469 pub extrinsic_count: u16,
470 #[codec(compact)]
472 pub extrinsic_size: u32,
473 #[codec(compact)]
475 pub exports: u16,
476}
477
478impl Add for RefineLoad {
479 type Output = Self;
480 fn add(self, rhs: Self) -> Self {
481 Self {
482 gas_used: self.gas_used + rhs.gas_used,
483 imports: self.imports + rhs.imports,
484 extrinsic_count: self.extrinsic_count + rhs.extrinsic_count,
485 extrinsic_size: self.extrinsic_size + rhs.extrinsic_size,
486 exports: self.exports + rhs.exports,
487 }
488 }
489}
490
491impl AddAssign for RefineLoad {
492 fn add_assign(&mut self, rhs: Self) {
493 self.gas_used += rhs.gas_used;
494 self.imports += rhs.imports;
495 self.extrinsic_count += rhs.extrinsic_count;
496 self.extrinsic_size += rhs.extrinsic_size;
497 self.exports += rhs.exports;
498 }
499}
500
501#[derive(Clone, Encode, Decode, MaxEncodedLen, Debug, Eq, PartialEq)]
504#[doc(hidden)]
505pub struct WorkDigest {
506 pub service: ServiceId,
508 pub code_hash: CodeHash,
511 pub payload_hash: PayloadHash,
513 pub accumulate_gas: UnsignedGas,
515 #[codec(encoded_as = "CompactRefineResult")]
517 pub result: Result<WorkOutput, WorkError>,
518 pub refine_load: RefineLoad,
520}
521
522#[derive(Clone, Debug, Encode, Decode)]
525pub struct WorkItemRecord {
526 pub package: WorkPackageHash,
528 pub exports_root: SegmentTreeRoot,
531 pub authorizer_hash: AuthorizerHash,
534 pub payload: PayloadHash,
536 #[codec(compact)]
538 pub gas_limit: UnsignedGas,
539 #[codec(encoded_as = "CompactRefineResult")]
541 pub result: Result<WorkOutput, WorkError>,
542 pub auth_output: AuthTrace,
545}
546
547#[derive(Debug, Clone, Encode, Decode, Default)]
549pub struct TransferRecord {
550 pub source: ServiceId,
552 pub destination: ServiceId,
554 pub amount: Balance,
556 pub memo: Memo,
558 pub gas_limit: UnsignedGas,
561}
562
563#[derive(Debug, Encode, Decode)]
565pub enum AccumulateItem {
566 WorkItem(WorkItemRecord),
568 Transfer(TransferRecord),
570}
571
572impl From<WorkItemRecord> for AccumulateItem {
573 fn from(w: WorkItemRecord) -> Self {
574 AccumulateItem::WorkItem(w)
575 }
576}
577
578impl From<TransferRecord> for AccumulateItem {
579 fn from(t: TransferRecord) -> Self {
580 AccumulateItem::Transfer(t)
581 }
582}
583
584#[derive(Debug, Encode, Decode)]
586#[doc(hidden)]
587pub struct AccumulateParams {
588 #[codec(compact)]
590 pub slot: Slot,
591 #[codec(compact)]
593 pub service_id: ServiceId,
594 #[codec(compact)]
596 pub item_count: u32,
597}
598
599#[derive(Debug, Encode, Decode)]
601#[doc(hidden)]
602pub struct IsAuthorizedParams {
603 #[codec(compact)]
605 pub core: u16,
606}
607
608#[derive(Debug, Encode, Decode)]
610#[doc(hidden)]
611pub struct RefineParams {
612 #[codec(compact)]
614 pub core_index: CoreIndex,
615 #[codec(compact)]
617 pub item_index: u32, #[codec(compact)]
620 pub service_id: ServiceId,
621 pub payload: WorkPayload,
623 pub package_hash: WorkPackageHash,
625}
626
627#[derive(Debug, Encode)]
630#[doc(hidden)]
631pub struct RefineParamsRef<'a> {
632 #[codec(compact)]
634 pub core_index: CoreIndex,
635 #[codec(compact)]
637 pub item_index: u32,
638 #[codec(compact)]
640 pub service_id: ServiceId,
641 pub payload: &'a WorkPayload,
643 pub package_hash: &'a WorkPackageHash,
645}
646
647#[derive(Debug, Clone, Encode, Decode, MaxEncodedLen)]
651pub struct ServiceInfo {
652 pub code_hash: CodeHash,
654 pub balance: Balance,
656 pub threshold: Balance,
658 pub min_item_gas: UnsignedGas,
661 pub min_memo_gas: UnsignedGas,
664 pub bytes: u64,
666 pub items: u32,
668 pub deposit_offset: Balance,
670 pub creation_slot: Slot,
672 pub last_accumulation_slot: Slot,
674 pub parent_service: ServiceId,
676}
677
678impl ServiceInfo {
679 pub const CODE_HASH_OFFSET: usize = 0;
681 pub const BALANCE_OFFSET: usize = 32;
682 pub const THRESHOLD_OFFSET: usize = 40;
683 pub const MIN_ITEM_GAS_OFFSET: usize = 48;
684 pub const MIN_MEMO_GAS_OFFSET: usize = 56;
685 pub const BYTES_OFFSET: usize = 64;
686 pub const ITEMS_OFFSET: usize = 72;
687 pub const DEPOSIT_OFFSET_OFFSET: usize = 76;
688 pub const CREATION_SLOT_OFFSET: usize = 84;
689 pub const LAST_ACCUMULATION_SLOT_OFFSET: usize = 88;
690 pub const PARENT_SERVICE_OFFSET: usize = 92;
691 pub const ENCODED_LEN: usize = 96;
693}
694
695struct CompactRefineResult(Result<WorkOutput, WorkError>);
697struct CompactRefineResultRef<'a>(&'a Result<WorkOutput, WorkError>);
698
699impl From<CompactRefineResult> for Result<WorkOutput, WorkError> {
700 fn from(value: CompactRefineResult) -> Self {
701 value.0
702 }
703}
704
705impl<'a> From<&'a Result<WorkOutput, WorkError>> for CompactRefineResultRef<'a> {
706 fn from(value: &'a Result<WorkOutput, WorkError>) -> Self {
707 CompactRefineResultRef(value)
708 }
709}
710
711impl<'a> codec::EncodeAsRef<'a, Result<WorkOutput, WorkError>> for CompactRefineResult {
712 type RefType = CompactRefineResultRef<'a>;
713}
714
715impl Encode for CompactRefineResult {
716 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
717 CompactRefineResultRef(&self.0).encode_to(dest)
718 }
719}
720
721impl MaxEncodedLen for CompactRefineResult {
722 fn max_encoded_len() -> usize {
723 (1 + WorkOutput::max_encoded_len()).max(WorkError::max_encoded_len())
724 }
725}
726
727impl Encode for CompactRefineResultRef<'_> {
728 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
729 match &self.0 {
730 Ok(o) => {
731 dest.push_byte(0);
732 o.encode_to(dest)
733 },
734 Err(e) => e.encode_to(dest),
735 }
736 }
737}
738
739impl Decode for CompactRefineResult {
740 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
741 match input.read_byte()? {
742 0 => Ok(Self(Ok(WorkOutput::decode(input)?))),
743 e => Ok(Self(Err(WorkError::decode(&mut &[e][..])?))),
744 }
745 }
746}
747
748macro_rules! fetch_kind {
749 ($($(#[$attr:meta])* $variant:ident = $value:expr),* $(,)?) => {
750 #[derive(Copy, Clone, Debug)]
752 pub enum FetchKind {
753 $(
754 $(#[$attr])*
755 $variant = $value,
756 )*
757 }
758
759 impl TryFrom<u64> for FetchKind {
760 type Error = ();
761 fn try_from(value: u64) -> Result<Self, Self::Error> {
762 match value {
763 $(
764 $value => Ok(FetchKind::$variant),
765 )*
766 _ => Err(()),
767 }
768 }
769 }
770 };
771}
772
773fetch_kind! {
774 ProtocolParameters = 0,
776 Entropy = 1,
778 AuthTrace = 2,
780 AnyExtrinsic = 3,
782 OurExtrinsic = 4,
784 AnyImport = 5,
786 OurImport = 6,
788 WorkPackage = 7,
790 AuthConfig = 8,
792 AuthToken = 9,
794 RefineContext = 10,
796 ItemsSummary = 11,
798 AnyItemSummary = 12,
800 AnyPayload = 13,
802 AccumulateItems = 14,
804 AnyAccumulateItem = 15,
806}
807
808#[derive(Clone, Encode, Decode, Debug)]
810pub struct WorkItemSummary {
811 pub service: ServiceId,
812 pub code_hash: CodeHash,
813 pub refine_gas_limit: UnsignedGas,
814 pub accumulate_gas_limit: UnsignedGas,
815 pub export_count: u16,
816 pub import_count: u16,
817 pub extrinsics_count: u16,
818 pub payload_len: u32,
819}
820
821impl From<&WorkItem> for WorkItemSummary {
822 fn from(w: &WorkItem) -> Self {
823 WorkItemSummary {
824 service: w.service,
825 code_hash: w.code_hash,
826 refine_gas_limit: w.refine_gas_limit,
827 accumulate_gas_limit: w.accumulate_gas_limit,
828 export_count: w.export_count,
829 import_count: w.import_segments.len() as u16,
830 extrinsics_count: w.extrinsics.len() as u16,
831 payload_len: w.payload.len() as u32,
832 }
833 }
834}
835
836#[derive(Copy, Clone, Debug)]
838pub enum PageMode {
839 ReadOnly = 0,
840 ReadWrite = 1,
841}
842
843#[derive(Copy, Clone, Debug)]
845pub enum PageOperation {
846 Free,
847 Alloc(PageMode),
848 SetMode(PageMode),
849}
850
851impl From<PageOperation> for u64 {
852 fn from(value: PageOperation) -> Self {
853 match value {
854 PageOperation::Free => 0,
855 PageOperation::Alloc(mode) => 1 + mode as u64,
856 PageOperation::SetMode(mode) => 3 + mode as u64,
857 }
858 }
859}
860
861impl TryFrom<u64> for PageOperation {
862 type Error = ();
863 fn try_from(operation: u64) -> Result<Self, Self::Error> {
864 match operation {
865 0 => Ok(Self::Free),
866 1 => Ok(Self::Alloc(PageMode::ReadOnly)),
867 2 => Ok(Self::Alloc(PageMode::ReadWrite)),
868 3 => Ok(Self::SetMode(PageMode::ReadOnly)),
869 4 => Ok(Self::SetMode(PageMode::ReadWrite)),
870 _ => Err(()),
871 }
872 }
873}
874
875#[cfg(test)]
876mod tests {
877 use super::*;
878 use bounded_collections::{TryCollect, bounded_vec};
879 use codec::DecodeAll;
880
881 #[test]
882 fn compact_refine_result_codec() {
883 let enc_dec = |exp_res, exp_buf: &[u8]| {
884 let buf = CompactRefineResultRef(&exp_res).encode();
885 assert_eq!(buf, exp_buf);
886 let res = CompactRefineResult::decode(&mut &buf[..]).unwrap();
887 assert_eq!(res.0, exp_res);
888 };
889
890 enc_dec(Ok(vec![1, 2, 3].into()), &[0, 3, 1, 2, 3]);
891 enc_dec(Err(WorkError::OutOfGas), &[1]);
892 enc_dec(Err(WorkError::Panic), &[2]);
893 enc_dec(Err(WorkError::BadExports), &[3]);
894 enc_dec(Err(WorkError::OutputOversize), &[4]);
895 enc_dec(Err(WorkError::BadCode), &[5]);
896 enc_dec(Err(WorkError::CodeOversize), &[6]);
897 }
898
899 #[test]
900 fn service_info_encoded_len_is_correct() {
901 assert_eq!(ServiceInfo::max_encoded_len(), ServiceInfo::ENCODED_LEN);
902 }
903
904 #[test]
905 fn service_info_item_offset_works() {
906 let default = ServiceInfo {
907 code_hash: Default::default(),
908 balance: Default::default(),
909 threshold: Default::default(),
910 min_item_gas: Default::default(),
911 min_memo_gas: Default::default(),
912 bytes: Default::default(),
913 items: Default::default(),
914 deposit_offset: Default::default(),
915 creation_slot: Default::default(),
916 last_accumulation_slot: Default::default(),
917 parent_service: Default::default(),
918 };
919
920 macro_rules! sanity_check {
921 ($field:ident, $type:ident, $offset:expr, $value:expr) => {{
922 let type_size = core::mem::size_of::<$type>();
924 assert_eq!(type_size, $type::max_encoded_len());
925 let expected = $type::from($value);
926 let test_struct = ServiceInfo { $field: expected, ..default };
927 let buffer = test_struct.encode();
928 let actual: $type =
930 DecodeAll::decode_all(&mut &buffer[$offset..$offset + type_size]).unwrap();
931 assert_eq!(expected, actual);
932 }};
933 ($field:ident, $type:ident, $offset:expr) => {{
934 sanity_check!($field, $type, $offset, <$type>::MAX);
935 }};
936 }
937
938 sanity_check!(code_hash, CodeHash, ServiceInfo::CODE_HASH_OFFSET, [0xff_u8; 32]);
939 sanity_check!(balance, Balance, ServiceInfo::BALANCE_OFFSET);
940 sanity_check!(threshold, Balance, ServiceInfo::THRESHOLD_OFFSET);
941 sanity_check!(min_item_gas, UnsignedGas, ServiceInfo::MIN_ITEM_GAS_OFFSET);
942 sanity_check!(min_memo_gas, UnsignedGas, ServiceInfo::MIN_MEMO_GAS_OFFSET);
943 sanity_check!(bytes, u64, ServiceInfo::BYTES_OFFSET);
944 sanity_check!(items, u32, ServiceInfo::ITEMS_OFFSET);
945 sanity_check!(deposit_offset, Balance, ServiceInfo::DEPOSIT_OFFSET_OFFSET);
946 sanity_check!(creation_slot, Slot, ServiceInfo::CREATION_SLOT_OFFSET);
947 sanity_check!(last_accumulation_slot, Slot, ServiceInfo::LAST_ACCUMULATION_SLOT_OFFSET);
948 sanity_check!(parent_service, ServiceId, ServiceInfo::PARENT_SERVICE_OFFSET);
949 }
950
951 #[test]
952 fn context_max_encoded_len() {
953 assert_eq!(RefineContext::largest().encoded_size(), RefineContext::max_encoded_len());
954 }
955
956 #[test]
957 fn package_max_encoded_len() {
958 let portion = |total, i, num| (total / num) + if i == 0 { total % num } else { 0 };
959
960 let max_inputs = 2 + max_work_items();
961 let input = |i| vec![0; portion(max_input() as usize, i, max_inputs)];
962
963 let imports = |i| {
964 bounded_vec![
965 ImportSpec { root: RootIdentifier::Direct(Default::default()), index: u16::MAX };
966 portion(max_imports() as usize, i, max_work_items())
967 ]
968 };
969
970 let extrinsics = |i| {
971 bounded_vec![
972 ExtrinsicSpec { hash: Default::default(), len: u32::MAX };
973 portion(max_extrinsics() as usize, i, max_work_items())
974 ]
975 };
976
977 let largest_package = WorkPackage {
988 authorization: input(0).into(),
989 auth_code_host: ServiceId::MAX,
990 authorizer: Authorizer { code_hash: Default::default(), config: input(1).into() },
991 context: RefineContext::largest(),
992 items: (0..max_work_items())
993 .map(|i| WorkItem {
994 service: ServiceId::MAX,
995 code_hash: Default::default(),
996 payload: input(2 + i).into(),
997 refine_gas_limit: UnsignedGas::MAX,
998 accumulate_gas_limit: UnsignedGas::MAX,
999 import_segments: imports(i),
1000 extrinsics: extrinsics(i),
1001 export_count: u16::MAX,
1002 })
1003 .try_collect()
1004 .unwrap(),
1005 };
1006 assert_eq!(largest_package.import_count(), max_imports());
1007 assert_eq!(largest_package.extrinsic_count(), max_extrinsics());
1008 let largest_package_size = largest_package.encoded_size();
1009
1010 let max = WorkPackage::max_encoded_len();
1016 assert!(largest_package_size <= max);
1017 assert!((largest_package_size + max_inputs + (2 * max_work_items())) >= max);
1018 }
1019}