metadata_shortener/
cutter.rs

1//! Tools for metadata cutting.
2use crate::std::{borrow::ToOwned, string::String, vec::Vec};
3
4#[cfg(not(feature = "std"))]
5use core::any::TypeId;
6#[cfg(all(not(feature = "std"), any(feature = "merkle-lean", test)))]
7use core::marker::PhantomData;
8
9#[cfg(feature = "std")]
10use std::any::TypeId;
11#[cfg(all(feature = "std", any(feature = "merkle-lean", test)))]
12use std::marker::PhantomData;
13
14use external_memory_tools::{AddressableBuffer, ExternalMemory};
15
16#[cfg(any(feature = "proof-gen", test))]
17use merkle_cbt_lean::Hasher;
18
19#[cfg(any(feature = "merkle-lean", test))]
20use merkle_cbt_lean::Leaf;
21
22use parity_scale_codec::{Decode, Encode};
23
24use scale_info::{
25    form::PortableForm, interner::UntrackedSymbol, Field, Path, PortableType, Type, TypeDef,
26    TypeDefBitSequence, TypeDefPrimitive, TypeDefVariant, Variant,
27};
28
29#[cfg(any(feature = "proof-gen", test))]
30use substrate_parser::ShortSpecs;
31use substrate_parser::{
32    cards::Info,
33    compacts::get_compact,
34    decoding_sci::{decode_type_def_primitive, pick_variant, BitVecPositions, ResolvedTy, Ty},
35    error::{ParserError, RegistryError, SignableError},
36    propagated::{Checker, Propagated, SpecialtySet},
37    special_indicators::{Hint, SpecialtyTypeHinted, ENUM_INDEX_ENCODED_LEN},
38    traits::{AsMetadata, ResolveType, SignedExtensionMetadata, SpecNameVersion},
39    MarkedData,
40};
41
42use crate::error::{MetaCutError, RegistryCutError};
43
44#[cfg(any(feature = "proof-gen", test))]
45use crate::traits::{Blake3Hasher, HashableMetadata, HashableRegistry, MerkleProofMetadata};
46
47#[cfg(any(feature = "merkle-lean", test))]
48use crate::traits::LEN;
49
50/// Temporary registry.
51///
52/// Could be transformed both into [`ShortRegistry`] (for parsing) or into
53/// [`LeavesRegistry`] (for Merkle tree generation).
54///
55/// When type is added to `DraftRegistry`, it has all its docs removed (i.e.
56/// docs for type itself, for fields, and for enum variants).
57#[derive(Debug)]
58pub struct DraftRegistry {
59    pub types: Vec<DraftRegistryEntry>,
60}
61
62/// Temporary registry entry: id and entry details.
63#[derive(Debug)]
64pub struct DraftRegistryEntry {
65    pub id: u32,
66    pub entry_details: EntryDetails,
67}
68
69/// Temporary registry entry details. Enums and non-enums are treated
70/// separately.
71#[derive(Debug)]
72pub enum EntryDetails {
73    Regular {
74        ty: Type<PortableForm>,
75    },
76    ReduceableEnum {
77        path: Path<PortableForm>,
78        variants: Vec<Variant<PortableForm>>,
79    },
80}
81
82/// Shortened type registry, for use in `ShortMetadata`.
83///
84/// Note that although its inner structure is identical to that of
85/// [`PortableRegistry`](scale_info::PortableRegistry), `ShortRegistry` has a
86/// different implementation of type resolving with
87/// [`ResolveType`] trait implementation.
88///
89/// In `PortableRegistry` type is resolved by `id` with in-built tools of
90/// [`scale_info`] crate, effectively `id` being the index of corresponding
91/// `PortableType` in `types` vector.
92///
93/// In `ShortRegistry` resolved `id` must match that of the `PortableType`,
94/// regardless of the order.
95#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq)]
96pub struct ShortRegistry {
97    pub types: Vec<PortableType>,
98}
99
100/// Registry separated into elements that could be transformed into Merkle tree
101/// leaves.
102///
103/// Each element is a [`PortableType`] with either a single type entry
104/// (for non-enums) or with an enum entry with a single enum variant (for
105/// enums). Note that if multiple variants of a single enum are present, they
106/// are entered as separate [`PortableType`]s with the same id. `LeavesRegistry`
107/// is not intended to be used for decoding, only for Merkle tree construction.
108#[derive(Debug, PartialEq)]
109pub struct LeavesRegistry {
110    pub types: Vec<PortableType>,
111}
112
113impl DraftRegistry {
114    /// New empty `DraftRegistry`.
115    pub fn new() -> Self {
116        Self { types: Vec::new() }
117    }
118
119    /// Transform into [`ShortRegistry`], suitable for decoding.
120    ///
121    /// In `ShortRegistry`:
122    /// - Each type has an individual entry
123    /// - Entries are sorted by id
124    /// - Variants within single enum are sorted by variant index
125    pub fn finalize_to_short(&self) -> ShortRegistry {
126        let mut short_registry = ShortRegistry { types: Vec::new() };
127        for draft_entry in self.types.iter() {
128            let id = draft_entry.id;
129            let ty = match &draft_entry.entry_details {
130                EntryDetails::Regular { ty } => ty.to_owned(),
131                EntryDetails::ReduceableEnum { path, variants } => Type {
132                    path: path.to_owned(),
133                    type_params: Vec::new(),
134                    type_def: TypeDef::Variant(TypeDefVariant {
135                        variants: variants.to_owned(),
136                    }),
137                    docs: Vec::new(),
138                },
139            };
140            short_registry.types.push(PortableType { id, ty })
141        }
142        for entry in short_registry.types.iter_mut() {
143            if let TypeDef::Variant(ref mut variants_entry) = entry.ty.type_def {
144                variants_entry
145                    .variants
146                    .sort_by(|a, b| a.index.cmp(&b.index))
147            }
148        }
149        short_registry.types.sort_by(|a, b| a.id.cmp(&b.id));
150        short_registry
151    }
152
153    /// Transform into [`LeavesRegistry`], suitable for Merkle tree
154    /// construction.
155    ///
156    /// In `LeavesRegistry`:
157    /// - Each non-enum type has an individual entry
158    /// - Each enum variant is transformed into enum type with a single variant,
159    /// id is the same
160    /// - Entries are sorted by id
161    /// - Entries with identical id (enum variants) are sorted by variant index
162    pub fn into_leaves(&self) -> LeavesRegistry {
163        let mut leaves = LeavesRegistry { types: Vec::new() };
164        for draft_entry in self.types.iter() {
165            let id = draft_entry.id;
166            match &draft_entry.entry_details {
167                EntryDetails::Regular { ty } => leaves.types.push(PortableType {
168                    id,
169                    ty: ty.to_owned(),
170                }),
171                EntryDetails::ReduceableEnum { path, variants } => {
172                    for variant in variants.iter() {
173                        let ty = Type {
174                            path: path.to_owned(),
175                            type_params: Vec::new(),
176                            type_def: TypeDef::Variant(TypeDefVariant {
177                                variants: vec![variant.to_owned()],
178                            }),
179                            docs: Vec::new(),
180                        };
181                        leaves.types.push(PortableType { id, ty })
182                    }
183                }
184            };
185        }
186        leaves.types.sort_by(|a, b| {
187            if a.id == b.id {
188                if let TypeDef::Variant(variants_a) = &a.ty.type_def {
189                    if let TypeDef::Variant(variants_b) = &b.ty.type_def {
190                        variants_a.variants[0]
191                            .index
192                            .cmp(&variants_b.variants[0].index)
193                    } else {
194                        unreachable!("only variants have more than one entry")
195                    }
196                } else {
197                    unreachable!("only variants have more than one entry")
198                }
199            } else {
200                a.id.cmp(&b.id)
201            }
202        });
203        leaves
204    }
205}
206
207impl Default for DraftRegistry {
208    fn default() -> Self {
209        Self::new()
210    }
211}
212
213/// Add type into `DraftRegistry` as regular type, i.e. as non-enum.
214pub(crate) fn add_ty_as_regular(
215    draft_registry: &mut DraftRegistry,
216    mut ty: Type<PortableForm>,
217    id: u32,
218) -> Result<(), RegistryCutError> {
219    for draft_registry_entry in draft_registry.types.iter() {
220        if draft_registry_entry.id == id {
221            match draft_registry_entry.entry_details {
222                EntryDetails::Regular { ty: ref known_ty } => {
223                    if known_ty == &ty {
224                        return Ok(());
225                    } else {
226                        return Err(RegistryCutError::IndexTwice { id });
227                    }
228                }
229                EntryDetails::ReduceableEnum {
230                    path: _,
231                    variants: _,
232                } => return Err(RegistryCutError::IndexTwice { id }),
233            }
234        }
235    }
236
237    // Remove docs from each field in structs.
238    // Enums with non-empty set of variants do not get added as regular type,
239    // other types do not have internal field-related docs.
240    if let TypeDef::Composite(ref mut type_def_composite) = ty.type_def {
241        for field in type_def_composite.fields.iter_mut() {
242            field.docs.clear();
243        }
244    }
245
246    // Remove docs from type itself.
247    ty.docs.clear();
248    let entry_details = EntryDetails::Regular { ty };
249    let draft_registry_entry = DraftRegistryEntry { id, entry_details };
250    draft_registry.types.push(draft_registry_entry);
251    Ok(())
252}
253
254/// Add type into `DraftRegistry` as an enum.
255pub(crate) fn add_as_enum(
256    draft_registry: &mut DraftRegistry,
257    path: &Path<PortableForm>,
258    mut variant: Variant<PortableForm>,
259    id: u32,
260) -> Result<(), RegistryCutError> {
261    for draft_registry_entry in draft_registry.types.iter_mut() {
262        if draft_registry_entry.id == id {
263            match draft_registry_entry.entry_details {
264                EntryDetails::Regular { ty: _ } => {
265                    return Err(RegistryCutError::IndexTwice { id });
266                }
267                EntryDetails::ReduceableEnum {
268                    path: ref known_path,
269                    ref mut variants,
270                } => {
271                    if known_path == path {
272                        // Remove variant docs.
273                        variant.docs.clear();
274
275                        // Remove docs for each field.
276                        for field in variant.fields.iter_mut() {
277                            field.docs.clear();
278                        }
279
280                        if !variants.contains(&variant) {
281                            variants.push(variant)
282                        }
283                        return Ok(());
284                    } else {
285                        return Err(RegistryCutError::IndexTwice { id });
286                    }
287                }
288            }
289        }
290    }
291
292    // Remove variant docs.
293    variant.docs.clear();
294
295    // Remove docs for each field.
296    for field in variant.fields.iter_mut() {
297        field.docs.clear();
298    }
299
300    let variants = vec![variant];
301    let entry_details = EntryDetails::ReduceableEnum {
302        path: path.to_owned(),
303        variants,
304    };
305    let draft_registry_entry = DraftRegistryEntry { id, entry_details };
306    draft_registry.types.push(draft_registry_entry);
307    Ok(())
308}
309
310/// Update [`DraftRegistry`] with types needed to parse a call part of a marked
311/// transaction.
312pub fn pass_call<B, E, M>(
313    marked_data: &MarkedData<B, E, M>,
314    ext_memory: &mut E,
315    full_metadata: &M,
316    draft_registry: &mut DraftRegistry,
317) -> Result<(), MetaCutError<E, M>>
318where
319    B: AddressableBuffer<E>,
320    E: ExternalMemory,
321    M: AsMetadata<E>,
322{
323    let data = marked_data.data_no_extensions();
324    let mut position = marked_data.call_start();
325
326    pass_call_unmarked(
327        &data,
328        &mut position,
329        ext_memory,
330        full_metadata,
331        draft_registry,
332    )?;
333    if position != marked_data.extensions_start() {
334        Err(MetaCutError::Signable(SignableError::SomeDataNotUsedCall {
335            from: position,
336            to: marked_data.extensions_start(),
337        }))
338    } else {
339        Ok(())
340    }
341}
342
343/// Update [`DraftRegistry`] with types needed to parse a call part of an
344/// unmarked transaction.
345pub fn pass_call_unmarked<B, E, M>(
346    data: &B,
347    position: &mut usize,
348    ext_memory: &mut E,
349    full_metadata: &M,
350    draft_registry: &mut DraftRegistry,
351) -> Result<(), MetaCutError<E, M>>
352where
353    B: AddressableBuffer<E>,
354    E: ExternalMemory,
355    M: AsMetadata<E>,
356{
357    let call_ty = full_metadata
358        .call_ty()
359        .map_err(|e| MetaCutError::Signable(SignableError::MetaStructure(e)))?;
360
361    pass_type::<B, E, M>(
362        &Ty::Symbol(&call_ty),
363        data,
364        ext_memory,
365        position,
366        &full_metadata.types(),
367        Propagated::new(),
368        draft_registry,
369    )
370}
371
372/// Update [`DraftRegistry`] with types needed to parse extensions of a marked
373/// transaction.
374pub fn pass_extensions<B, E, M>(
375    marked_data: &MarkedData<B, E, M>,
376    ext_memory: &mut E,
377    full_metadata: &M,
378    draft_registry: &mut DraftRegistry,
379) -> Result<(), MetaCutError<E, M>>
380where
381    B: AddressableBuffer<E>,
382    E: ExternalMemory,
383    M: AsMetadata<E>,
384{
385    let mut position = marked_data.extensions_start();
386    let data = marked_data.data();
387
388    pass_extensions_unmarked(
389        data,
390        &mut position,
391        ext_memory,
392        full_metadata,
393        draft_registry,
394    )
395}
396
397/// Update [`DraftRegistry`] with types needed to parse extensions of an
398/// unmarked transaction.
399pub fn pass_extensions_unmarked<B, E, M>(
400    data: &B,
401    position: &mut usize,
402    ext_memory: &mut E,
403    full_metadata: &M,
404    draft_registry: &mut DraftRegistry,
405) -> Result<(), MetaCutError<E, M>>
406where
407    B: AddressableBuffer<E>,
408    E: ExternalMemory,
409    M: AsMetadata<E>,
410{
411    let full_metadata_types = full_metadata.types();
412    let signed_extensions = full_metadata
413        .signed_extensions()
414        .map_err(|e| MetaCutError::Signable(SignableError::MetaStructure(e)))?;
415    for signed_extensions_metadata in signed_extensions.iter() {
416        pass_type::<B, E, M>(
417            &Ty::Symbol(&signed_extensions_metadata.ty),
418            data,
419            ext_memory,
420            position,
421            &full_metadata_types,
422            Propagated::from_ext_meta(signed_extensions_metadata),
423            draft_registry,
424        )?;
425    }
426    for signed_extensions_metadata in signed_extensions.iter() {
427        pass_type::<B, E, M>(
428            &Ty::Symbol(&signed_extensions_metadata.additional_signed),
429            data,
430            ext_memory,
431            position,
432            &full_metadata_types,
433            Propagated::from_ext_meta(signed_extensions_metadata),
434            draft_registry,
435        )?;
436    }
437    // `position > data.total_len()` is ruled out elsewhere
438    if *position != data.total_len() {
439        Err(MetaCutError::Signable(
440            SignableError::SomeDataNotUsedExtensions { from: *position },
441        ))
442    } else {
443        Ok(())
444    }
445}
446
447/// Shortened metadata, custom-made for specific transaction.
448///
449/// Contains all the data necessary to parse a signable transaction and to
450/// generate a metadata digest.
451#[cfg(any(feature = "merkle-lean", test))]
452#[repr(C)]
453#[derive(Debug, Decode, Encode, Eq, PartialEq)]
454pub struct ShortMetadata<L, E>
455where
456    L: Leaf<LEN, E>,
457    E: ExternalMemory,
458{
459    pub short_registry: ShortRegistry,
460    pub indices: Vec<u32>,
461    pub lemmas: Vec<L>,
462    pub metadata_descriptor: MetadataDescriptor,
463    ext_memory_type: PhantomData<E>,
464}
465
466/// Versioned metadata descriptor with non-registry entities necessary for
467/// transaction parsing.
468#[repr(C)]
469#[non_exhaustive]
470#[derive(Debug, Decode, Encode, Eq, PartialEq)]
471pub enum MetadataDescriptor {
472    V0,
473    V1 {
474        call_ty: UntrackedSymbol<TypeId>,
475        signed_extensions: Vec<SignedExtensionMetadata>,
476        spec_name_version: SpecNameVersion,
477        base58prefix: u16,
478        decimals: u8,
479        unit: String,
480    },
481}
482
483/// Construct [`ShortMetadata`] for a regular transaction.
484///
485/// Transaction could be separated into call and extension as the call is
486/// prefixed with call length compact.
487#[cfg(any(feature = "proof-gen", test))]
488pub fn cut_metadata<B, E, L, M>(
489    data: &B,
490    ext_memory: &mut E,
491    full_metadata: &M,
492    short_specs: &ShortSpecs,
493) -> Result<ShortMetadata<L, E>, MetaCutError<E, M>>
494where
495    B: AddressableBuffer<E>,
496    E: ExternalMemory,
497    L: Leaf<LEN, E>,
498    M: HashableMetadata<E>,
499    <M as AsMetadata<E>>::TypeRegistry: HashableRegistry<E>,
500{
501    let mut draft_registry = DraftRegistry::new();
502
503    let marked_data =
504        MarkedData::<B, E, M>::mark(data, ext_memory).map_err(MetaCutError::Signable)?;
505    pass_call::<B, E, M>(&marked_data, ext_memory, full_metadata, &mut draft_registry)?;
506    pass_extensions::<B, E, M>(&marked_data, ext_memory, full_metadata, &mut draft_registry)?;
507
508    let short_registry = draft_registry.finalize_to_short();
509
510    let leaves_registry_short =
511        <ShortRegistry as HashableRegistry<E>>::merkle_leaves_source(&short_registry)
512            .map_err(MetaCutError::Registry)?;
513    let leaves_registry_long = full_metadata
514        .types()
515        .merkle_leaves_source()
516        .map_err(MetaCutError::Registry)?;
517
518    let leaves_short: Vec<[u8; LEN]> = leaves_registry_short
519        .types
520        .iter()
521        .map(|entry| Blake3Hasher::make(&entry.encode()))
522        .collect();
523    let leaves_long: Vec<[u8; LEN]> = leaves_registry_long
524        .types
525        .iter()
526        .map(|entry| Blake3Hasher::make(&entry.encode()))
527        .collect();
528
529    let proof = MerkleProofMetadata::for_leaves_subset(leaves_long, &leaves_short, ext_memory)
530        .map_err(MetaCutError::TreeCalculateProof)?;
531
532    let metadata_descriptor = MetadataDescriptor::V1 {
533        call_ty: full_metadata
534            .call_ty()
535            .map_err(|e| MetaCutError::Signable(SignableError::MetaStructure(e)))?,
536        signed_extensions: full_metadata
537            .signed_extensions()
538            .map_err(|e| MetaCutError::Signable(SignableError::MetaStructure(e)))?,
539        spec_name_version: full_metadata
540            .spec_name_version()
541            .map_err(|e| MetaCutError::Signable(SignableError::MetaStructure(e)))?,
542        base58prefix: short_specs.base58prefix,
543        decimals: short_specs.decimals,
544        unit: short_specs.unit.to_owned(),
545    };
546
547    Ok(ShortMetadata {
548        short_registry,
549        indices: proof.indices(),
550        lemmas: proof.lemmas,
551        metadata_descriptor,
552        ext_memory_type: PhantomData,
553    })
554}
555
556/// Construct [`ShortMetadata`] for an unmarked transaction.
557///
558/// Unmarked transaction is not prefixed with call length compact, and thus call
559/// and extensions could not be separated.
560#[cfg(any(feature = "proof-gen", test))]
561pub fn cut_metadata_transaction_unmarked<B, E, L, M>(
562    data: &B,
563    ext_memory: &mut E,
564    full_metadata: &M,
565    short_specs: &ShortSpecs,
566) -> Result<ShortMetadata<L, E>, MetaCutError<E, M>>
567where
568    B: AddressableBuffer<E>,
569    E: ExternalMemory,
570    L: Leaf<LEN, E>,
571    M: HashableMetadata<E>,
572    <M as AsMetadata<E>>::TypeRegistry: HashableRegistry<E>,
573{
574    let mut draft_registry = DraftRegistry::new();
575
576    let mut position = 0;
577    pass_call_unmarked::<B, E, M>(
578        data,
579        &mut position,
580        ext_memory,
581        full_metadata,
582        &mut draft_registry,
583    )?;
584    pass_extensions_unmarked::<B, E, M>(
585        data,
586        &mut position,
587        ext_memory,
588        full_metadata,
589        &mut draft_registry,
590    )?;
591
592    let short_registry = draft_registry.finalize_to_short();
593
594    let leaves_registry_short =
595        <ShortRegistry as HashableRegistry<E>>::merkle_leaves_source(&short_registry)
596            .map_err(MetaCutError::Registry)?;
597    let leaves_registry_long = full_metadata
598        .types()
599        .merkle_leaves_source()
600        .map_err(MetaCutError::Registry)?;
601
602    let leaves_short: Vec<[u8; LEN]> = leaves_registry_short
603        .types
604        .iter()
605        .map(|entry| Blake3Hasher::make(&entry.encode()))
606        .collect();
607    let leaves_long: Vec<[u8; LEN]> = leaves_registry_long
608        .types
609        .iter()
610        .map(|entry| Blake3Hasher::make(&entry.encode()))
611        .collect();
612
613    let proof = MerkleProofMetadata::for_leaves_subset(leaves_long, &leaves_short, ext_memory)
614        .map_err(MetaCutError::TreeCalculateProof)?;
615
616    let metadata_descriptor = MetadataDescriptor::V1 {
617        call_ty: full_metadata
618            .call_ty()
619            .map_err(|e| MetaCutError::Signable(SignableError::MetaStructure(e)))?,
620        signed_extensions: full_metadata
621            .signed_extensions()
622            .map_err(|e| MetaCutError::Signable(SignableError::MetaStructure(e)))?,
623        spec_name_version: full_metadata
624            .spec_name_version()
625            .map_err(|e| MetaCutError::Signable(SignableError::MetaStructure(e)))?,
626        base58prefix: short_specs.base58prefix,
627        decimals: short_specs.decimals,
628        unit: short_specs.unit.to_owned(),
629    };
630
631    Ok(ShortMetadata {
632        short_registry,
633        indices: proof.indices(),
634        lemmas: proof.lemmas,
635        metadata_descriptor,
636        ext_memory_type: PhantomData,
637    })
638}
639
640/// Update [`DraftRegistry`] for parsing data corresponding to a type.
641pub fn pass_type<B, E, M>(
642    ty_input: &Ty,
643    data: &B,
644    ext_memory: &mut E,
645    position: &mut usize,
646    registry: &M::TypeRegistry,
647    mut propagated: Propagated,
648    draft_registry: &mut DraftRegistry,
649) -> Result<(), MetaCutError<E, M>>
650where
651    B: AddressableBuffer<E>,
652    E: ExternalMemory,
653    M: AsMetadata<E>,
654{
655    let (ty, id) = match ty_input {
656        Ty::Resolved(resolved_ty) => (resolved_ty.ty.to_owned(), resolved_ty.id),
657        Ty::Symbol(ty_symbol) => (
658            registry.resolve_ty(ty_symbol.id, ext_memory).map_err(|e| {
659                MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
660            })?,
661            ty_symbol.id,
662        ),
663    };
664
665    let info_ty = Info::from_ty(&ty);
666    propagated.add_info(&info_ty);
667
668    match &ty.type_def {
669        TypeDef::Composite(x) => {
670            pass_fields::<B, E, M>(
671                &x.fields,
672                data,
673                ext_memory,
674                position,
675                registry,
676                propagated.checker,
677                draft_registry,
678            )?;
679            add_ty_as_regular(draft_registry, ty.to_owned(), id).map_err(MetaCutError::Registry)
680        }
681        TypeDef::Variant(x) => {
682            propagated.reject_compact().map_err(|e| {
683                MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
684            })?;
685            if !x.variants.is_empty() {
686                pass_variant::<B, E, M>(
687                    &x.variants,
688                    data,
689                    ext_memory,
690                    position,
691                    registry,
692                    draft_registry,
693                    &info_ty.path,
694                    id,
695                )
696            } else {
697                add_ty_as_regular(draft_registry, ty.to_owned(), id).map_err(MetaCutError::Registry)
698            }
699        }
700        TypeDef::Sequence(x) => {
701            let number_of_elements = get_compact::<u32, B, E>(data, ext_memory, position)
702                .map_err(|e| MetaCutError::Signable(SignableError::Parsing(e)))?;
703            propagated.checker.drop_cycle_check();
704            pass_elements_set::<B, E, M>(
705                &x.type_param,
706                number_of_elements,
707                data,
708                ext_memory,
709                position,
710                registry,
711                propagated,
712                draft_registry,
713            )?;
714            add_ty_as_regular(draft_registry, ty, id).map_err(MetaCutError::Registry)
715        }
716        TypeDef::Array(x) => {
717            pass_elements_set::<B, E, M>(
718                &x.type_param,
719                x.len,
720                data,
721                ext_memory,
722                position,
723                registry,
724                propagated,
725                draft_registry,
726            )?;
727            add_ty_as_regular(draft_registry, ty, id).map_err(MetaCutError::Registry)
728        }
729        TypeDef::Tuple(x) => {
730            if x.fields.len() > 1 {
731                propagated.reject_compact().map_err(|e| {
732                    MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
733                })?;
734                propagated.forget_hint();
735            }
736            for inner_ty_symbol in x.fields.iter() {
737                let id = inner_ty_symbol.id;
738                let ty = registry.resolve_ty(id, ext_memory).map_err(|e| {
739                    MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
740                })?;
741                pass_type::<B, E, M>(
742                    &Ty::Resolved(ResolvedTy {
743                        ty: ty.to_owned(),
744                        id,
745                    }),
746                    data,
747                    ext_memory,
748                    position,
749                    registry,
750                    Propagated::for_ty(&propagated.checker, &ty, id).map_err(|e| {
751                        MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
752                    })?,
753                    draft_registry,
754                )?;
755            }
756            add_ty_as_regular(draft_registry, ty, id).map_err(MetaCutError::Registry)
757        }
758        TypeDef::Primitive(x) => {
759            decode_type_def_primitive::<B, E>(
760                x,
761                data,
762                ext_memory,
763                position,
764                propagated.checker.specialty_set,
765            )
766            .map_err(|e| MetaCutError::Signable(SignableError::Parsing(e)))?;
767            add_ty_as_regular(draft_registry, ty, id).map_err(MetaCutError::Registry)
768        }
769        TypeDef::Compact(x) => {
770            propagated.reject_compact().map_err(|e| {
771                MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
772            })?;
773            propagated.checker.specialty_set.compact_at = Some(id);
774            propagated.checker.check_id(x.type_param.id).map_err(|e| {
775                MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
776            })?;
777            pass_type::<B, E, M>(
778                &Ty::Symbol(&x.type_param),
779                data,
780                ext_memory,
781                position,
782                registry,
783                propagated,
784                draft_registry,
785            )?;
786            add_ty_as_regular(draft_registry, ty, id).map_err(MetaCutError::Registry)
787        }
788        TypeDef::BitSequence(x) => {
789            propagated.reject_compact().map_err(|e| {
790                MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
791            })?;
792            pass_type_def_bit_sequence::<B, E, M>(
793                x,
794                id,
795                data,
796                ext_memory,
797                position,
798                registry,
799                draft_registry,
800            )?;
801            add_ty_as_regular(draft_registry, ty, id).map_err(MetaCutError::Registry)
802        }
803    }
804}
805
806/// Update [`DraftRegistry`] for parsing a [`Field`] set.
807fn pass_fields<B, E, M>(
808    fields: &[Field<PortableForm>],
809    data: &B,
810    ext_memory: &mut E,
811    position: &mut usize,
812    registry: &M::TypeRegistry,
813    mut checker: Checker,
814    draft_registry: &mut DraftRegistry,
815) -> Result<(), MetaCutError<E, M>>
816where
817    B: AddressableBuffer<E>,
818    E: ExternalMemory,
819    M: AsMetadata<E>,
820{
821    if fields.len() > 1 {
822        // Only single-field structs can be processed as a compact.
823        // Note: compact flag was already checked in enum processing at this
824        // point.
825        checker.reject_compact().map_err(|e| {
826            MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
827        })?;
828
829        // `Hint` remains relevant only if single-field struct is processed.
830        // Note: checker gets renewed when fields of enum are processed.
831        checker.forget_hint();
832    }
833    for field in fields.iter() {
834        pass_type::<B, E, M>(
835            &Ty::Symbol(&field.ty),
836            data,
837            ext_memory,
838            position,
839            registry,
840            Propagated::for_field(&checker, field).map_err(|e| {
841                MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
842            })?,
843            draft_registry,
844        )?;
845    }
846    Ok(())
847}
848
849/// Update [`DraftRegistry`] for parsing a set of identical elements (in a
850/// vector or array).
851#[allow(clippy::too_many_arguments)]
852fn pass_elements_set<B, E, M>(
853    element: &UntrackedSymbol<TypeId>,
854    number_of_elements: u32,
855    data: &B,
856    ext_memory: &mut E,
857    position: &mut usize,
858    registry: &M::TypeRegistry,
859    propagated: Propagated,
860    draft_registry: &mut DraftRegistry,
861) -> Result<(), MetaCutError<E, M>>
862where
863    B: AddressableBuffer<E>,
864    E: ExternalMemory,
865    M: AsMetadata<E>,
866{
867    propagated
868        .reject_compact()
869        .map_err(|e| MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e))))?;
870
871    let husked = husk_type_no_info::<E, M>(
872        element,
873        registry,
874        ext_memory,
875        propagated.checker,
876        draft_registry,
877    )?;
878
879    for _i in 0..number_of_elements {
880        pass_type::<B, E, M>(
881            &Ty::Resolved(ResolvedTy {
882                ty: husked.ty.to_owned(),
883                id: husked.id,
884            }),
885            data,
886            ext_memory,
887            position,
888            registry,
889            Propagated::with_checker(husked.checker.clone()),
890            draft_registry,
891        )?;
892    }
893    Ok(())
894}
895
896/// Update [`DraftRegistry`] for parsing a [`Variant`].
897#[allow(clippy::too_many_arguments)]
898fn pass_variant<B, E, M>(
899    variants: &[Variant<PortableForm>],
900    data: &B,
901    ext_memory: &mut E,
902    position: &mut usize,
903    registry: &M::TypeRegistry,
904    draft_registry: &mut DraftRegistry,
905    path: &Path<PortableForm>,
906    enum_ty_id: u32,
907) -> Result<(), MetaCutError<E, M>>
908where
909    B: AddressableBuffer<E>,
910    E: ExternalMemory,
911    M: AsMetadata<E>,
912{
913    let found_variant = pick_variant::<B, E>(variants, data, ext_memory, *position)
914        .map_err(|e| MetaCutError::Signable(SignableError::Parsing(e)))?;
915
916    *position += ENUM_INDEX_ENCODED_LEN;
917
918    pass_fields::<B, E, M>(
919        &found_variant.fields,
920        data,
921        ext_memory,
922        position,
923        registry,
924        Checker::new(),
925        draft_registry,
926    )?;
927
928    add_as_enum(draft_registry, path, found_variant.to_owned(), enum_ty_id)
929        .map_err(MetaCutError::Registry)
930}
931
932/// Update [`DraftRegistry`] for parsing a [`TypeDefBitSequence`].
933fn pass_type_def_bit_sequence<B, E, M>(
934    bit_ty: &TypeDefBitSequence<PortableForm>,
935    id: u32,
936    data: &B,
937    ext_memory: &mut E,
938    position: &mut usize,
939    registry: &M::TypeRegistry,
940    draft_registry: &mut DraftRegistry,
941) -> Result<(), MetaCutError<E, M>>
942where
943    B: AddressableBuffer<E>,
944    E: ExternalMemory,
945    M: AsMetadata<E>,
946{
947    // BitOrder
948    let bitorder_type = registry
949        .resolve_ty(bit_ty.bit_order_type.id, ext_memory)
950        .map_err(|e| MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e))))?;
951    add_ty_as_regular(draft_registry, bitorder_type, bit_ty.bit_order_type.id)
952        .map_err(MetaCutError::Registry)?;
953
954    // BitStore
955    let bitstore_type = registry
956        .resolve_ty(bit_ty.bit_store_type.id, ext_memory)
957        .map_err(|e| MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e))))?;
958
959    match bitstore_type.type_def {
960        TypeDef::Primitive(TypeDefPrimitive::U8) => {
961            pass_bitvec_decode::<u8, B, E>(data, ext_memory, position)
962        }
963        TypeDef::Primitive(TypeDefPrimitive::U16) => {
964            pass_bitvec_decode::<u16, B, E>(data, ext_memory, position)
965        }
966        TypeDef::Primitive(TypeDefPrimitive::U32) => {
967            pass_bitvec_decode::<u32, B, E>(data, ext_memory, position)
968        }
969        TypeDef::Primitive(TypeDefPrimitive::U64) => {
970            pass_bitvec_decode::<u64, B, E>(data, ext_memory, position)
971        }
972        _ => Err(ParserError::Registry(RegistryError::NotBitStoreType { id })),
973    }
974    .map_err(|e| MetaCutError::Signable(SignableError::Parsing(e)))?;
975
976    add_ty_as_regular(draft_registry, bitstore_type, bit_ty.bit_store_type.id)
977        .map_err(MetaCutError::Registry)
978}
979
980/// Move current position after encountering a [`TypeDefBitSequence`].
981fn pass_bitvec_decode<'a, T, B, E>(
982    data: &B,
983    ext_memory: &'a mut E,
984    position: &'a mut usize,
985) -> Result<(), ParserError<E>>
986where
987    B: AddressableBuffer<E>,
988    E: ExternalMemory,
989{
990    let bitvec_positions = BitVecPositions::new::<T, B, E>(data, ext_memory, *position)?;
991    *position = bitvec_positions.bitvec_end;
992    Ok(())
993}
994
995/// Type, resolved as much as possible.
996///
997/// `HuskedTypeNoInfo` is useful when decoding sets of identical elements (in
998/// vectors or arrays) and while searching for types with specified descriptors
999/// (such as extrinsic type with pre-known structure).
1000///
1001/// No identical [`Type`] `id`s are expected to be encountered, otherwise the
1002/// resolving would go indefinitely.
1003///
1004/// [`Type`] `id`s are collected and checked in [`Checker`].
1005struct HuskedTypeNoInfo {
1006    checker: Checker,
1007    ty: Type<PortableForm>,
1008    id: u32,
1009}
1010
1011/// Resolve compact and single-field structs into corresponding inner types.
1012///
1013/// Resolving stops when more complex structure or a specialty is encountered.
1014fn husk_type_no_info<E, M>(
1015    entry_symbol: &UntrackedSymbol<TypeId>,
1016    registry: &M::TypeRegistry,
1017    ext_memory: &mut E,
1018    mut checker: Checker,
1019    draft_registry: &mut DraftRegistry,
1020) -> Result<HuskedTypeNoInfo, MetaCutError<E, M>>
1021where
1022    E: ExternalMemory,
1023    M: AsMetadata<E>,
1024{
1025    let entry_symbol_id = entry_symbol.id;
1026    checker
1027        .check_id(entry_symbol_id)
1028        .map_err(|e| MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e))))?;
1029    checker.specialty_set = SpecialtySet {
1030        compact_at: None,
1031        hint: Hint::None,
1032    };
1033
1034    let mut ty = registry
1035        .resolve_ty(entry_symbol_id, ext_memory)
1036        .map_err(|e| MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e))))?;
1037    let mut id = entry_symbol_id;
1038
1039    while let SpecialtyTypeHinted::None = SpecialtyTypeHinted::from_type(&ty) {
1040        let type_def = ty.type_def.to_owned();
1041        match type_def {
1042            TypeDef::Composite(x) => {
1043                if x.fields.len() == 1 {
1044                    add_ty_as_regular(draft_registry, ty.to_owned(), id)
1045                        .map_err(MetaCutError::Registry)?;
1046                    id = x.fields[0].ty.id;
1047                    checker.check_id(id).map_err(|e| {
1048                        MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
1049                    })?;
1050                    ty = registry.resolve_ty(id, ext_memory).map_err(|e| {
1051                        MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
1052                    })?;
1053                    if let Hint::None = checker.specialty_set.hint {
1054                        checker.specialty_set.hint = Hint::from_field(&x.fields[0])
1055                    }
1056                } else {
1057                    break;
1058                }
1059            }
1060            TypeDef::Compact(x) => {
1061                add_ty_as_regular(draft_registry, ty.to_owned(), id)
1062                    .map_err(MetaCutError::Registry)?;
1063                checker.reject_compact().map_err(|e| {
1064                    MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
1065                })?;
1066                checker.specialty_set.compact_at = Some(id);
1067                id = x.type_param.id;
1068                checker.check_id(id).map_err(|e| {
1069                    MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
1070                })?;
1071                ty = registry.resolve_ty(id, ext_memory).map_err(|e| {
1072                    MetaCutError::Signable(SignableError::Parsing(ParserError::Registry(e)))
1073                })?;
1074            }
1075            _ => break,
1076        }
1077    }
1078
1079    Ok(HuskedTypeNoInfo { checker, ty, id })
1080}
1081
1082#[cfg(test)]
1083mod tests {
1084    use super::*;
1085    use scale_info::{Path, PortableRegistry};
1086
1087    use crate::std::string::ToString;
1088
1089    #[test]
1090    fn sort_draft_registry() {
1091        let mut draft_registry = DraftRegistry::new();
1092        add_as_enum(
1093            &mut draft_registry,
1094            &Path::<PortableForm> {
1095                segments: vec!["test".to_string(), "Path".to_string()],
1096            },
1097            Variant::<PortableForm> {
1098                name: "OtherVariant".to_string(),
1099                fields: Vec::new(),
1100                index: 3u8,
1101                docs: Vec::new(),
1102            },
1103            144,
1104        )
1105        .unwrap();
1106        add_as_enum(
1107            &mut draft_registry,
1108            &Path::<PortableForm> {
1109                segments: vec!["test".to_string(), "Path".to_string()],
1110            },
1111            Variant::<PortableForm> {
1112                name: "SomeVariant".to_string(),
1113                fields: Vec::new(),
1114                index: 1u8,
1115                docs: Vec::new(),
1116            },
1117            144,
1118        )
1119        .unwrap();
1120        add_as_enum(
1121            &mut draft_registry,
1122            &Path::<PortableForm> {
1123                segments: vec!["test".to_string(), "Path".to_string()],
1124            },
1125            Variant::<PortableForm> {
1126                name: "ThirdVariant".to_string(),
1127                fields: Vec::new(),
1128                index: 7u8,
1129                docs: Vec::new(),
1130            },
1131            144,
1132        )
1133        .unwrap();
1134
1135        let to_short = draft_registry.finalize_to_short();
1136        assert_eq!(
1137            to_short,
1138            ShortRegistry {
1139                types: vec![PortableType {
1140                    id: 144,
1141                    ty: Type {
1142                        path: Path {
1143                            segments: vec!["test".to_string(), "Path".to_string()]
1144                        },
1145                        type_params: Vec::new(),
1146                        type_def: TypeDef::Variant(TypeDefVariant {
1147                            variants: vec![
1148                                Variant {
1149                                    name: "SomeVariant".to_string(),
1150                                    fields: Vec::new(),
1151                                    index: 1,
1152                                    docs: Vec::new()
1153                                },
1154                                Variant {
1155                                    name: "OtherVariant".to_string(),
1156                                    fields: Vec::new(),
1157                                    index: 3,
1158                                    docs: Vec::new()
1159                                },
1160                                Variant {
1161                                    name: "ThirdVariant".to_string(),
1162                                    fields: Vec::new(),
1163                                    index: 7,
1164                                    docs: Vec::new()
1165                                }
1166                            ]
1167                        }),
1168                        docs: Vec::new()
1169                    }
1170                }]
1171            }
1172        );
1173
1174        let leaves = draft_registry.into_leaves();
1175        assert_eq!(
1176            leaves,
1177            LeavesRegistry {
1178                types: vec![
1179                    PortableType {
1180                        id: 144,
1181                        ty: Type {
1182                            path: Path {
1183                                segments: vec!["test".to_string(), "Path".to_string()]
1184                            },
1185                            type_params: Vec::new(),
1186                            type_def: TypeDef::Variant(TypeDefVariant {
1187                                variants: vec![Variant {
1188                                    name: "SomeVariant".to_string(),
1189                                    fields: Vec::new(),
1190                                    index: 1,
1191                                    docs: Vec::new()
1192                                }]
1193                            }),
1194                            docs: Vec::new()
1195                        }
1196                    },
1197                    PortableType {
1198                        id: 144,
1199                        ty: Type {
1200                            path: Path {
1201                                segments: vec!["test".to_string(), "Path".to_string()]
1202                            },
1203                            type_params: Vec::new(),
1204                            type_def: TypeDef::Variant(TypeDefVariant {
1205                                variants: vec![Variant {
1206                                    name: "OtherVariant".to_string(),
1207                                    fields: Vec::new(),
1208                                    index: 3,
1209                                    docs: Vec::new()
1210                                }]
1211                            }),
1212                            docs: Vec::new()
1213                        }
1214                    },
1215                    PortableType {
1216                        id: 144,
1217                        ty: Type {
1218                            path: Path {
1219                                segments: vec!["test".to_string(), "Path".to_string()]
1220                            },
1221                            type_params: Vec::new(),
1222                            type_def: TypeDef::Variant(TypeDefVariant {
1223                                variants: vec![Variant {
1224                                    name: "ThirdVariant".to_string(),
1225                                    fields: Vec::new(),
1226                                    index: 7,
1227                                    docs: Vec::new()
1228                                }]
1229                            }),
1230                            docs: Vec::new()
1231                        }
1232                    }
1233                ]
1234            }
1235        );
1236    }
1237
1238    #[test]
1239    fn keep_empty_enums_portable() {
1240        let portable_registry = PortableRegistry {
1241            types: vec![PortableType {
1242                id: 0,
1243                ty: Type {
1244                    path: Path {
1245                        segments: vec!["test".to_string(), "Path".to_string()],
1246                    },
1247                    type_params: Vec::new(),
1248                    type_def: TypeDef::Variant(TypeDefVariant { variants: vec![] }),
1249                    docs: vec![
1250                        "important information".to_string(),
1251                        "danger ahead".to_string(),
1252                    ],
1253                },
1254            }],
1255        };
1256        let leaves_registry =
1257            <PortableRegistry as HashableRegistry<()>>::merkle_leaves_source(&portable_registry)
1258                .unwrap();
1259        assert_eq!(leaves_registry.types.len(), 1,);
1260    }
1261
1262    #[test]
1263    fn keep_empty_enums_short() {
1264        let short_registry = ShortRegistry {
1265            types: vec![PortableType {
1266                id: 15,
1267                ty: Type {
1268                    path: Path {
1269                        segments: vec!["test".to_string(), "Path".to_string()],
1270                    },
1271                    type_params: Vec::new(),
1272                    type_def: TypeDef::Variant(TypeDefVariant { variants: vec![] }),
1273                    docs: vec![
1274                        "important information".to_string(),
1275                        "danger ahead".to_string(),
1276                    ],
1277                },
1278            }],
1279        };
1280        let leaves_registry =
1281            <ShortRegistry as HashableRegistry<()>>::merkle_leaves_source(&short_registry).unwrap();
1282        assert_eq!(leaves_registry.types.len(), 1,);
1283    }
1284}