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)]
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 = FixedVec<OpaqueValKeyset, ValCount>;
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 state_root: StateRootHash,
197 pub beefy_root: MmrPeakHash,
199 pub lookup_anchor: HeaderHash,
203 pub lookup_anchor_slot: Slot,
207 pub prerequisites: VecSet<WorkPackageHash>,
210}
211
212impl MaxEncodedLen for RefineContext {
213 fn max_encoded_len() -> usize {
214 HeaderHash::max_encoded_len() + StateRootHash::max_encoded_len() + MmrPeakHash::max_encoded_len() + HeaderHash::max_encoded_len() + Slot::max_encoded_len() + BoundedVec::<WorkPackageHash, MaxDependencies>::max_encoded_len() }
221}
222
223impl RefineContext {
224 #[doc(hidden)]
225 pub fn largest() -> Self {
226 Self {
227 anchor: Default::default(),
228 state_root: Default::default(),
229 beefy_root: Default::default(),
230 lookup_anchor: Default::default(),
231 lookup_anchor_slot: Slot::MAX,
232 prerequisites: (0..max_dependencies()).map(|i| [i as u8; 32].into()).collect(),
233 }
234 }
235}
236
237#[derive(Clone, Debug)]
244pub struct WrappedWorkPackage<W: Wrap> {
245 pub authorization: Authorization,
247 pub auth_code_host: ServiceId,
249 pub authorizer: Authorizer,
251 pub context: W::Wrap<RefineContext>,
253 pub items: W::Wrap<WrappedWorkItems<W>>,
255}
256
257impl<W: Wrap> WrappedWorkPackage<W> {
258 pub fn extrinsic_count(&self) -> u32 {
260 self.items
261 .borrow()
262 .iter()
263 .map(|item| item.borrow().extrinsics.len() as u32)
264 .sum()
265 }
266
267 pub fn saturated_extrinsic_size(&self) -> u32 {
271 self.items
272 .borrow()
273 .iter()
274 .map(|item| item.borrow().extrinsic_size())
275 .fold(0u32, |sum, size| sum.saturating_add(size))
276 }
277
278 pub fn import_specs(&self) -> impl Iterator<Item = &ImportSpec> {
280 self.items.borrow().iter().flat_map(|item| item.borrow().import_segments.iter())
281 }
282
283 pub fn import_count(&self) -> u32 {
285 self.items
286 .borrow()
287 .iter()
288 .map(|item| item.borrow().import_segments.len() as u32)
289 .sum()
290 }
291
292 pub fn export_count(&self) -> u32 {
294 self.items.borrow().iter().map(|item| item.borrow().export_count as u32).sum()
295 }
296
297 pub fn dependency_count(&self) -> u32 {
299 let mut indirect = VecSet::new();
300 for spec in self.import_specs() {
301 if let RootIdentifier::Indirect(wph) = &spec.root {
302 indirect.insert(wph);
303 }
304 }
305 (self.context.borrow().prerequisites.len() + indirect.len()) as u32
306 }
307}
308
309impl<W: Wrap> Encode for WrappedWorkPackage<W>
310where
311 W::Wrap<RefineContext>: Encode,
312 W::Wrap<WrappedWorkItems<W>>: Encode,
313{
314 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
315 self.auth_code_host.encode_to(dest);
316 self.authorizer.code_hash.encode_to(dest);
317 self.context.encode_to(dest);
318 self.authorization.encode_to(dest);
319 self.authorizer.config.encode_to(dest);
320 self.items.encode_to(dest);
321 }
322}
323
324impl<W: Wrap> Decode for WrappedWorkPackage<W>
325where
326 W::Wrap<RefineContext>: Decode,
327 W::Wrap<WrappedWorkItems<W>>: Decode,
328{
329 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
330 let auth_code_host = ServiceId::decode(input)?;
331 let auth_code_hash = CodeHash::decode(input)?;
332 let context = W::Wrap::<RefineContext>::decode(input)?;
333 let authorization = Authorization::decode(input)?;
334 let auth_config = AuthConfig::decode(input)?;
335 let items = W::Wrap::<WrappedWorkItems<W>>::decode(input)?;
336 Ok(Self {
337 authorization,
338 auth_code_host,
339 authorizer: Authorizer { code_hash: auth_code_hash, config: auth_config },
340 context,
341 items,
342 })
343 }
344}
345
346impl<W: Wrap> MaxEncodedLen for WrappedWorkPackage<W>
347where
348 Self: Encode,
349{
350 fn max_encoded_len() -> usize {
351 let mut max = Authorization::max_encoded_len() +
357 ServiceId::max_encoded_len() + Authorizer::max_encoded_len() +
359 RefineContext::max_encoded_len() +
360 WorkItems::max_encoded_len();
361
362 let max_inputs = 2 + max_work_items();
367 max -= (max_inputs - 1) * (max_input() as usize);
368
369 max -= (max_work_items() - 1) * (max_imports() as usize) * ImportSpec::max_encoded_len();
372
373 max -=
375 (max_work_items() - 1) * (max_extrinsics() as usize) * ExtrinsicSpec::max_encoded_len();
376
377 max
378 }
379}
380
381pub type WorkPackage = WrappedWorkPackage<NoWrap>;
385
386#[derive(Clone, Encode, Decode, MaxEncodedLen, Debug)]
389pub struct Authorizer {
390 pub code_hash: CodeHash,
392 pub config: AuthConfig,
394}
395
396impl Authorizer {
397 pub fn any() -> Self {
398 Self { code_hash: CodeHash::zero(), config: Default::default() }
399 }
400
401 pub fn with_concat<R>(&self, f: impl Fn(&[u8]) -> R) -> R {
402 f(&[&self.code_hash.0[..], &self.config[..]].concat()[..])
403 }
404
405 pub fn hash(&self, hasher: impl Fn(&[u8]) -> Hash) -> AuthorizerHash {
406 self.with_concat(hasher).into()
407 }
408}
409
410#[derive(Clone, Encode, Decode, MaxEncodedLen, Debug, Eq, PartialEq)]
417#[doc(hidden)]
418pub enum WorkError {
419 OutOfGas = 1,
421 Panic = 2,
423 BadExports = 3,
425 OutputOversize = 4,
427 BadCode = 5,
431 CodeOversize = 6,
433}
434
435#[derive(Copy, Clone, Encode, Decode, MaxEncodedLen, Debug, Eq, PartialEq, Default)]
438#[doc(hidden)]
439pub struct RefineLoad {
440 #[codec(compact)]
442 pub gas_used: UnsignedGas,
443 #[codec(compact)]
445 pub imports: u16,
446 #[codec(compact)]
448 pub extrinsic_count: u16,
449 #[codec(compact)]
451 pub extrinsic_size: u32,
452 #[codec(compact)]
454 pub exports: u16,
455}
456
457impl Add for RefineLoad {
458 type Output = Self;
459 fn add(self, rhs: Self) -> Self {
460 Self {
461 gas_used: self.gas_used + rhs.gas_used,
462 imports: self.imports + rhs.imports,
463 extrinsic_count: self.extrinsic_count + rhs.extrinsic_count,
464 extrinsic_size: self.extrinsic_size + rhs.extrinsic_size,
465 exports: self.exports + rhs.exports,
466 }
467 }
468}
469
470impl AddAssign for RefineLoad {
471 fn add_assign(&mut self, rhs: Self) {
472 self.gas_used += rhs.gas_used;
473 self.imports += rhs.imports;
474 self.extrinsic_count += rhs.extrinsic_count;
475 self.extrinsic_size += rhs.extrinsic_size;
476 self.exports += rhs.exports;
477 }
478}
479
480#[derive(Clone, Encode, Decode, MaxEncodedLen, Debug, Eq, PartialEq)]
483#[doc(hidden)]
484pub struct WorkDigest {
485 pub service: ServiceId,
487 pub code_hash: CodeHash,
490 pub payload_hash: PayloadHash,
492 pub accumulate_gas: UnsignedGas,
494 #[codec(encoded_as = "CompactRefineResult")]
496 pub result: Result<WorkOutput, WorkError>,
497 pub refine_load: RefineLoad,
499}
500
501#[derive(Clone, Debug, Encode, Decode)]
504pub struct WorkItemRecord {
505 pub package: WorkPackageHash,
507 pub exports_root: SegmentTreeRoot,
510 pub authorizer_hash: AuthorizerHash,
513 pub payload: PayloadHash,
515 #[codec(compact)]
517 pub gas_limit: UnsignedGas,
518 #[codec(encoded_as = "CompactRefineResult")]
520 pub result: Result<WorkOutput, WorkError>,
521 pub auth_output: AuthTrace,
524}
525
526#[derive(Debug, Clone, Encode, Decode, Default)]
528pub struct TransferRecord {
529 pub source: ServiceId,
531 pub destination: ServiceId,
533 pub amount: Balance,
535 pub memo: Memo,
537 pub gas_limit: UnsignedGas,
540}
541
542#[derive(Debug, Encode, Decode)]
544pub enum AccumulateItem {
545 WorkItem(WorkItemRecord),
547 Transfer(TransferRecord),
549}
550
551impl From<WorkItemRecord> for AccumulateItem {
552 fn from(w: WorkItemRecord) -> Self {
553 AccumulateItem::WorkItem(w)
554 }
555}
556
557impl From<TransferRecord> for AccumulateItem {
558 fn from(t: TransferRecord) -> Self {
559 AccumulateItem::Transfer(t)
560 }
561}
562
563#[derive(Debug, Encode, Decode)]
565#[doc(hidden)]
566pub struct AccumulateParams {
567 #[codec(compact)]
569 pub slot: Slot,
570 #[codec(compact)]
572 pub service_id: ServiceId,
573 #[codec(compact)]
575 pub item_count: u32,
576}
577
578#[derive(Debug, Encode, Decode)]
580#[doc(hidden)]
581pub struct IsAuthorizedParams {
582 #[codec(compact)]
584 pub core: u16,
585}
586
587#[derive(Debug, Encode, Decode)]
589#[doc(hidden)]
590pub struct RefineParams {
591 #[codec(compact)]
593 pub core_index: CoreIndex,
594 #[codec(compact)]
596 pub item_index: u32, #[codec(compact)]
599 pub service_id: ServiceId,
600 pub payload: WorkPayload,
602 pub package_hash: WorkPackageHash,
604}
605
606#[derive(Debug, Encode)]
609#[doc(hidden)]
610pub struct RefineParamsRef<'a> {
611 #[codec(compact)]
613 pub core_index: CoreIndex,
614 #[codec(compact)]
616 pub item_index: u32,
617 #[codec(compact)]
619 pub service_id: ServiceId,
620 pub payload: &'a WorkPayload,
622 pub package_hash: &'a WorkPackageHash,
624}
625
626#[derive(Debug, Clone, Encode, Decode, MaxEncodedLen)]
630pub struct ServiceInfo {
631 pub code_hash: CodeHash,
633 pub balance: Balance,
635 pub threshold: Balance,
637 pub min_item_gas: UnsignedGas,
640 pub min_memo_gas: UnsignedGas,
643 pub bytes: u64,
645 pub items: u32,
647 pub deposit_offset: Balance,
649 pub creation_slot: Slot,
651 pub last_accumulation_slot: Slot,
653 pub parent_service: ServiceId,
655}
656
657impl ServiceInfo {
658 pub const CODE_HASH_OFFSET: usize = 0;
660 pub const BALANCE_OFFSET: usize = 32;
661 pub const THRESHOLD_OFFSET: usize = 40;
662 pub const MIN_ITEM_GAS_OFFSET: usize = 48;
663 pub const MIN_MEMO_GAS_OFFSET: usize = 56;
664 pub const BYTES_OFFSET: usize = 64;
665 pub const ITEMS_OFFSET: usize = 72;
666 pub const DEPOSIT_OFFSET_OFFSET: usize = 76;
667 pub const CREATION_SLOT_OFFSET: usize = 84;
668 pub const LAST_ACCUMULATION_SLOT_OFFSET: usize = 88;
669 pub const PARENT_SERVICE_OFFSET: usize = 92;
670 pub const ENCODED_LEN: usize = 96;
672}
673
674struct CompactRefineResult(Result<WorkOutput, WorkError>);
676struct CompactRefineResultRef<'a>(&'a Result<WorkOutput, WorkError>);
677
678impl From<CompactRefineResult> for Result<WorkOutput, WorkError> {
679 fn from(value: CompactRefineResult) -> Self {
680 value.0
681 }
682}
683
684impl<'a> From<&'a Result<WorkOutput, WorkError>> for CompactRefineResultRef<'a> {
685 fn from(value: &'a Result<WorkOutput, WorkError>) -> Self {
686 CompactRefineResultRef(value)
687 }
688}
689
690impl<'a> codec::EncodeAsRef<'a, Result<WorkOutput, WorkError>> for CompactRefineResult {
691 type RefType = CompactRefineResultRef<'a>;
692}
693
694impl Encode for CompactRefineResult {
695 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
696 CompactRefineResultRef(&self.0).encode_to(dest)
697 }
698}
699
700impl MaxEncodedLen for CompactRefineResult {
701 fn max_encoded_len() -> usize {
702 (1 + WorkOutput::max_encoded_len()).max(WorkError::max_encoded_len())
703 }
704}
705
706impl Encode for CompactRefineResultRef<'_> {
707 fn encode_to<T: codec::Output + ?Sized>(&self, dest: &mut T) {
708 match &self.0 {
709 Ok(o) => {
710 dest.push_byte(0);
711 o.encode_to(dest)
712 },
713 Err(e) => e.encode_to(dest),
714 }
715 }
716}
717
718impl Decode for CompactRefineResult {
719 fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> {
720 match input.read_byte()? {
721 0 => Ok(Self(Ok(WorkOutput::decode(input)?))),
722 e => Ok(Self(Err(WorkError::decode(&mut &[e][..])?))),
723 }
724 }
725}
726
727macro_rules! fetch_kind {
728 ($($(#[$attr:meta])* $variant:ident = $value:expr),* $(,)?) => {
729 #[derive(Copy, Clone, Debug)]
731 pub enum FetchKind {
732 $(
733 $(#[$attr])*
734 $variant = $value,
735 )*
736 }
737
738 impl TryFrom<u64> for FetchKind {
739 type Error = ();
740 fn try_from(value: u64) -> Result<Self, Self::Error> {
741 match value {
742 $(
743 $value => Ok(FetchKind::$variant),
744 )*
745 _ => Err(()),
746 }
747 }
748 }
749 };
750}
751
752fetch_kind! {
753 ProtocolParameters = 0,
755 Entropy = 1,
757 AuthTrace = 2,
759 AnyExtrinsic = 3,
761 OurExtrinsic = 4,
763 AnyImport = 5,
765 OurImport = 6,
767 WorkPackage = 7,
769 Authorizer = 8,
771 AuthToken = 9,
773 RefineContext = 10,
775 ItemsSummary = 11,
777 AnyItemSummary = 12,
779 AnyPayload = 13,
781 AccumulateItems = 14,
783 AnyAccumulateItem = 15,
785}
786
787#[derive(Clone, Encode, Decode, Debug)]
789pub struct WorkItemSummary {
790 pub service: ServiceId,
791 pub code_hash: CodeHash,
792 pub refine_gas_limit: UnsignedGas,
793 pub accumulate_gas_limit: UnsignedGas,
794 pub export_count: u16,
795 pub import_count: u16,
796 pub extrinsics_count: u16,
797 pub payload_len: u32,
798}
799
800impl From<&WorkItem> for WorkItemSummary {
801 fn from(w: &WorkItem) -> Self {
802 WorkItemSummary {
803 service: w.service,
804 code_hash: w.code_hash,
805 refine_gas_limit: w.refine_gas_limit,
806 accumulate_gas_limit: w.accumulate_gas_limit,
807 export_count: w.export_count,
808 import_count: w.import_segments.len() as u16,
809 extrinsics_count: w.extrinsics.len() as u16,
810 payload_len: w.payload.len() as u32,
811 }
812 }
813}
814
815#[derive(Copy, Clone, Debug)]
817pub enum PageMode {
818 ReadOnly = 0,
819 ReadWrite = 1,
820}
821
822#[derive(Copy, Clone, Debug)]
824pub enum PageOperation {
825 Free,
826 Alloc(PageMode),
827 SetMode(PageMode),
828}
829
830impl From<PageOperation> for u64 {
831 fn from(value: PageOperation) -> Self {
832 match value {
833 PageOperation::Free => 0,
834 PageOperation::Alloc(mode) => 1 + mode as u64,
835 PageOperation::SetMode(mode) => 3 + mode as u64,
836 }
837 }
838}
839
840impl TryFrom<u64> for PageOperation {
841 type Error = ();
842 fn try_from(operation: u64) -> Result<Self, Self::Error> {
843 match operation {
844 0 => Ok(Self::Free),
845 1 => Ok(Self::Alloc(PageMode::ReadOnly)),
846 2 => Ok(Self::Alloc(PageMode::ReadWrite)),
847 3 => Ok(Self::SetMode(PageMode::ReadOnly)),
848 4 => Ok(Self::SetMode(PageMode::ReadWrite)),
849 _ => Err(()),
850 }
851 }
852}
853
854#[cfg(test)]
855mod tests {
856 use super::*;
857 use bounded_collections::{bounded_vec, TryCollect};
858 use codec::DecodeAll;
859
860 #[test]
861 fn compact_refine_result_codec() {
862 let enc_dec = |exp_res, exp_buf: &[u8]| {
863 let buf = CompactRefineResultRef(&exp_res).encode();
864 assert_eq!(buf, exp_buf);
865 let res = CompactRefineResult::decode(&mut &buf[..]).unwrap();
866 assert_eq!(res.0, exp_res);
867 };
868
869 enc_dec(Ok(vec![1, 2, 3].into()), &[0, 3, 1, 2, 3]);
870 enc_dec(Err(WorkError::OutOfGas), &[1]);
871 enc_dec(Err(WorkError::Panic), &[2]);
872 enc_dec(Err(WorkError::BadExports), &[3]);
873 enc_dec(Err(WorkError::OutputOversize), &[4]);
874 enc_dec(Err(WorkError::BadCode), &[5]);
875 enc_dec(Err(WorkError::CodeOversize), &[6]);
876 }
877
878 #[test]
879 fn service_info_encoded_len_is_correct() {
880 assert_eq!(ServiceInfo::max_encoded_len(), ServiceInfo::ENCODED_LEN);
881 }
882
883 #[test]
884 fn service_info_item_offset_works() {
885 let default = ServiceInfo {
886 code_hash: Default::default(),
887 balance: Default::default(),
888 threshold: Default::default(),
889 min_item_gas: Default::default(),
890 min_memo_gas: Default::default(),
891 bytes: Default::default(),
892 items: Default::default(),
893 deposit_offset: Default::default(),
894 creation_slot: Default::default(),
895 last_accumulation_slot: Default::default(),
896 parent_service: Default::default(),
897 };
898
899 macro_rules! sanity_check {
900 ($field:ident, $type:ident, $offset:expr, $value:expr) => {{
901 let type_size = core::mem::size_of::<$type>();
903 assert_eq!(type_size, $type::max_encoded_len());
904 let expected = $type::from($value);
905 let test_struct = ServiceInfo { $field: expected, ..default };
906 let buffer = test_struct.encode();
907 let actual: $type =
909 DecodeAll::decode_all(&mut &buffer[$offset..$offset + type_size]).unwrap();
910 assert_eq!(expected, actual);
911 }};
912 ($field:ident, $type:ident, $offset:expr) => {{
913 sanity_check!($field, $type, $offset, <$type>::MAX);
914 }};
915 }
916
917 sanity_check!(code_hash, CodeHash, ServiceInfo::CODE_HASH_OFFSET, [0xff_u8; 32]);
918 sanity_check!(balance, Balance, ServiceInfo::BALANCE_OFFSET);
919 sanity_check!(threshold, Balance, ServiceInfo::THRESHOLD_OFFSET);
920 sanity_check!(min_item_gas, UnsignedGas, ServiceInfo::MIN_ITEM_GAS_OFFSET);
921 sanity_check!(min_memo_gas, UnsignedGas, ServiceInfo::MIN_MEMO_GAS_OFFSET);
922 sanity_check!(bytes, u64, ServiceInfo::BYTES_OFFSET);
923 sanity_check!(items, u32, ServiceInfo::ITEMS_OFFSET);
924 sanity_check!(deposit_offset, Balance, ServiceInfo::DEPOSIT_OFFSET_OFFSET);
925 sanity_check!(creation_slot, Slot, ServiceInfo::CREATION_SLOT_OFFSET);
926 sanity_check!(last_accumulation_slot, Slot, ServiceInfo::LAST_ACCUMULATION_SLOT_OFFSET);
927 sanity_check!(parent_service, ServiceId, ServiceInfo::PARENT_SERVICE_OFFSET);
928 }
929
930 #[test]
931 fn context_max_encoded_len() {
932 assert_eq!(RefineContext::largest().encoded_size(), RefineContext::max_encoded_len());
933 }
934
935 #[test]
936 fn package_max_encoded_len() {
937 let portion = |total, i, num| (total / num) + if i == 0 { total % num } else { 0 };
938
939 let max_inputs = 2 + max_work_items();
940 let input = |i| vec![0; portion(max_input() as usize, i, max_inputs)];
941
942 let imports = |i| {
943 bounded_vec![
944 ImportSpec { root: RootIdentifier::Direct(Default::default()), index: u16::MAX };
945 portion(max_imports() as usize, i, max_work_items())
946 ]
947 };
948
949 let extrinsics = |i| {
950 bounded_vec![
951 ExtrinsicSpec { hash: Default::default(), len: u32::MAX };
952 portion(max_extrinsics() as usize, i, max_work_items())
953 ]
954 };
955
956 let largest_package = WorkPackage {
967 authorization: input(0).into(),
968 auth_code_host: ServiceId::MAX,
969 authorizer: Authorizer { code_hash: Default::default(), config: input(1).into() },
970 context: RefineContext::largest(),
971 items: (0..max_work_items())
972 .map(|i| WorkItem {
973 service: ServiceId::MAX,
974 code_hash: Default::default(),
975 payload: input(2 + i).into(),
976 refine_gas_limit: UnsignedGas::MAX,
977 accumulate_gas_limit: UnsignedGas::MAX,
978 import_segments: imports(i),
979 extrinsics: extrinsics(i),
980 export_count: u16::MAX,
981 })
982 .try_collect()
983 .unwrap(),
984 };
985 assert_eq!(largest_package.import_count(), max_imports());
986 assert_eq!(largest_package.extrinsic_count(), max_extrinsics());
987 let largest_package_size = largest_package.encoded_size();
988
989 let max = WorkPackage::max_encoded_len();
995 assert!(largest_package_size <= max);
996 assert!((largest_package_size + max_inputs + (2 * max_work_items())) >= max);
997 }
998}