sp_domains/
bundle.rs

1#[cfg(not(feature = "std"))]
2extern crate alloc;
3
4use crate::bundle::bundle_v0::{BundleHeaderV0, SealedBundleHeaderV0};
5use crate::execution_receipt::{ExecutionReceipt, ExecutionReceiptMutRef, ExecutionReceiptRef};
6use crate::{
7    DomainId, HeaderHashFor, HeaderNumberFor, OperatorId, OperatorSignature, ProofOfElection,
8};
9#[cfg(not(feature = "std"))]
10use alloc::collections::BTreeSet;
11#[cfg(not(feature = "std"))]
12use alloc::string::String;
13#[cfg(not(feature = "std"))]
14use alloc::vec::Vec;
15use bundle_v0::BundleV0;
16use parity_scale_codec::{Decode, Encode};
17use scale_info::TypeInfo;
18use sp_core::H256;
19use sp_runtime::OpaqueExtrinsic;
20use sp_runtime::traits::{BlakeTwo256, Hash, Header as HeaderT, NumberFor, Zero};
21use sp_weights::Weight;
22use subspace_runtime_primitives::BlockHashFor;
23
24pub mod bundle_v0;
25
26/// Header of bundle holding the references to various versions of SealedHeader.
27#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
28pub enum SealedBundleHeaderRef<'a, Number, Hash, DomainHeader: HeaderT, Balance> {
29    V0(&'a SealedBundleHeaderV0<Number, Hash, DomainHeader, Balance>),
30}
31
32impl<'a, Number: Encode, Hash: Encode, DomainHeader: HeaderT, Balance: Encode>
33    SealedBundleHeaderRef<'a, Number, Hash, DomainHeader, Balance>
34{
35    /// Returns the hash of the inner unsealed header.
36    pub fn pre_hash(&self) -> HeaderHashFor<DomainHeader> {
37        match self {
38            SealedBundleHeaderRef::V0(bundle) => bundle.header.hash(),
39        }
40    }
41
42    /// Returns the signature on the sealed bundle header.
43    pub fn signature(&self) -> &'a OperatorSignature {
44        match self {
45            SealedBundleHeaderRef::V0(bundle) => &bundle.signature,
46        }
47    }
48
49    /// Returns the proof of election on the bundle.
50    pub fn proof_of_election(&self) -> &'a ProofOfElection {
51        match self {
52            SealedBundleHeaderRef::V0(bundle) => &bundle.header.proof_of_election,
53        }
54    }
55
56    /// Returns the receipt on the bundle.
57    pub fn receipt(
58        &self,
59    ) -> ExecutionReceiptRef<
60        'a,
61        Number,
62        Hash,
63        HeaderNumberFor<DomainHeader>,
64        HeaderHashFor<DomainHeader>,
65        Balance,
66    > {
67        match self {
68            SealedBundleHeaderRef::V0(bundle) => match &bundle.header.receipt {
69                ExecutionReceipt::V0(receipt) => ExecutionReceiptRef::V0(receipt),
70            },
71        }
72    }
73
74    pub fn slot_number(&self) -> u64 {
75        match self {
76            SealedBundleHeaderRef::V0(bundle) => bundle.header.proof_of_election.slot_number,
77        }
78    }
79
80    pub fn bundle_extrinsics_root(&self) -> HeaderHashFor<DomainHeader> {
81        match self {
82            SealedBundleHeaderRef::V0(bundle) => bundle.header.bundle_extrinsics_root,
83        }
84    }
85}
86
87/// Bundle Versions.
88#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, Copy)]
89pub enum BundleVersion {
90    /// V0 bundle version
91    V0,
92}
93
94/// Versioned Domain Bundle.
95#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
96pub enum Bundle<Extrinsic, Number, Hash, DomainHeader: HeaderT, Balance> {
97    /// V0 version of the domain bundle.
98    V0(BundleV0<Extrinsic, Number, Hash, DomainHeader, Balance>),
99}
100
101impl<Extrinsic, Number, Hash, DomainHeader, Balance>
102    Bundle<Extrinsic, Number, Hash, DomainHeader, Balance>
103where
104    Extrinsic: Encode + Clone,
105    Number: Encode + Zero,
106    Hash: Encode + Default,
107    DomainHeader: HeaderT,
108    Balance: Encode + Zero + Default,
109{
110    /// Returns the hash of this bundle.
111    pub fn hash(&self) -> H256 {
112        BlakeTwo256::hash_of(self)
113    }
114
115    /// Returns the domain_id of this bundle.
116    pub fn domain_id(&self) -> DomainId {
117        match self {
118            Bundle::V0(bundle) => bundle.sealed_header.header.proof_of_election.domain_id,
119        }
120    }
121
122    /// Return the `bundle_extrinsics_root`
123    pub fn extrinsics_root(&self) -> HeaderHashFor<DomainHeader> {
124        match self {
125            Bundle::V0(bundle) => bundle.sealed_header.header.bundle_extrinsics_root,
126        }
127    }
128
129    /// Return the `operator_id`
130    pub fn operator_id(&self) -> OperatorId {
131        match self {
132            Bundle::V0(bundle) => bundle.sealed_header.header.proof_of_election.operator_id,
133        }
134    }
135
136    /// Return a reference of the execution receipt.
137    pub fn receipt_domain_block_number(&self) -> &HeaderNumberFor<DomainHeader> {
138        match self {
139            Bundle::V0(bundle) => bundle.sealed_header.header.receipt.domain_block_number(),
140        }
141    }
142
143    /// Consumes [`Bundle`] to extract the execution receipt.
144    pub fn into_receipt(
145        self,
146    ) -> ExecutionReceipt<
147        Number,
148        Hash,
149        HeaderNumberFor<DomainHeader>,
150        HeaderHashFor<DomainHeader>,
151        Balance,
152    > {
153        match self {
154            Bundle::V0(bundle) => bundle.sealed_header.header.receipt,
155        }
156    }
157
158    pub fn receipt(
159        &self,
160    ) -> ExecutionReceiptRef<
161        Number,
162        Hash,
163        HeaderNumberFor<DomainHeader>,
164        HeaderHashFor<DomainHeader>,
165        Balance,
166    > {
167        match self {
168            Bundle::V0(bundle) => match &bundle.sealed_header.header.receipt {
169                ExecutionReceipt::V0(er) => ExecutionReceiptRef::V0(er),
170            },
171        }
172    }
173
174    /// Return the bundle size (include header and body) in bytes
175    pub fn size(&self) -> u32 {
176        self.encoded_size() as u32
177    }
178
179    /// Return the bundle body size in bytes
180    pub fn body_size(&self) -> u32 {
181        let extrinsics = match self {
182            Bundle::V0(bundle) => &bundle.extrinsics,
183        };
184
185        extrinsics
186            .iter()
187            .map(|tx| tx.encoded_size() as u32)
188            .sum::<u32>()
189    }
190
191    /// Return the bundle body length. i.e number of extrinsics in the bundle.
192    pub fn body_length(&self) -> usize {
193        let extrinsics = match self {
194            Bundle::V0(bundle) => &bundle.extrinsics,
195        };
196        extrinsics.len()
197    }
198
199    /// Returns the estimated weight of the Bundle.
200    pub fn estimated_weight(&self) -> Weight {
201        match self {
202            Bundle::V0(bundle) => bundle.sealed_header.header.estimated_bundle_weight,
203        }
204    }
205
206    /// Returns the slot number at this bundle was constructed.
207    pub fn slot_number(&self) -> u64 {
208        match self {
209            Bundle::V0(bundle) => bundle.sealed_header.header.proof_of_election.slot_number,
210        }
211    }
212
213    /// Returns the reference to proof of election.
214    pub fn proof_of_election(&self) -> &ProofOfElection {
215        match self {
216            Bundle::V0(bundle) => &bundle.sealed_header.header.proof_of_election,
217        }
218    }
219
220    /// Returns the sealed header in the Bundle.
221    pub fn sealed_header(&self) -> SealedBundleHeaderRef<Number, Hash, DomainHeader, Balance> {
222        match self {
223            Bundle::V0(bundle) => SealedBundleHeaderRef::V0(&bundle.sealed_header),
224        }
225    }
226
227    /// Returns the bundle body consuming the bundle. ie extrinsics.
228    pub fn into_extrinsics(self) -> Vec<Extrinsic> {
229        match self {
230            Bundle::V0(bundle) => bundle.extrinsics,
231        }
232    }
233
234    /// Returns the reference to bundle body. ie extrinsics.
235    pub fn extrinsics(&self) -> &[Extrinsic] {
236        match self {
237            Bundle::V0(bundle) => &bundle.extrinsics,
238        }
239    }
240
241    /// Sets extrinsics to the bundle.
242    pub fn set_extrinsics(&mut self, exts: Vec<Extrinsic>) {
243        match self {
244            Bundle::V0(bundle) => bundle.extrinsics = exts,
245        }
246    }
247
248    /// Sets bundle extrinsic root.
249    pub fn set_bundle_extrinsics_root(&mut self, root: HeaderHashFor<DomainHeader>) {
250        match self {
251            Bundle::V0(bundle) => bundle.sealed_header.header.bundle_extrinsics_root = root,
252        }
253    }
254
255    /// Returns a mutable reference to Execution receipt.
256    pub fn execution_receipt_as_mut(
257        &mut self,
258    ) -> ExecutionReceiptMutRef<
259        Number,
260        Hash,
261        HeaderNumberFor<DomainHeader>,
262        HeaderHashFor<DomainHeader>,
263        Balance,
264    > {
265        match self {
266            Bundle::V0(bundle) => match &mut bundle.sealed_header.header.receipt {
267                ExecutionReceipt::V0(er) => ExecutionReceiptMutRef::V0(er),
268            },
269        }
270    }
271
272    /// Set estimated bundle weight.
273    pub fn set_estimated_bundle_weight(&mut self, weight: Weight) {
274        match self {
275            Bundle::V0(bundle) => bundle.sealed_header.header.estimated_bundle_weight = weight,
276        }
277    }
278
279    /// Sets signature of the bundle.
280    pub fn set_signature(&mut self, signature: OperatorSignature) {
281        match self {
282            Bundle::V0(bundle) => bundle.sealed_header.signature = signature,
283        }
284    }
285
286    /// Sets proof of election for bundle.
287    pub fn set_proof_of_election(&mut self, poe: ProofOfElection) {
288        match self {
289            Bundle::V0(bundle) => bundle.sealed_header.header.proof_of_election = poe,
290        }
291    }
292}
293
294/// Bundle with opaque extrinsics.
295pub type OpaqueBundle<Number, Hash, DomainHeader, Balance> =
296    Bundle<OpaqueExtrinsic, Number, Hash, DomainHeader, Balance>;
297
298/// List of [`OpaqueBundle`].
299pub type OpaqueBundles<Block, DomainHeader, Balance> =
300    Vec<OpaqueBundle<NumberFor<Block>, BlockHashFor<Block>, DomainHeader, Balance>>;
301
302#[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
303pub fn dummy_opaque_bundle<
304    Number: Encode,
305    Hash: Default + Encode,
306    DomainHeader: HeaderT,
307    Balance: Encode,
308>(
309    domain_id: DomainId,
310    operator_id: OperatorId,
311    receipt: ExecutionReceipt<
312        Number,
313        Hash,
314        HeaderNumberFor<DomainHeader>,
315        HeaderHashFor<DomainHeader>,
316        Balance,
317    >,
318) -> OpaqueBundle<Number, Hash, DomainHeader, Balance> {
319    use sp_core::crypto::UncheckedFrom;
320
321    let header = BundleHeaderV0 {
322        proof_of_election: ProofOfElection::dummy(domain_id, operator_id),
323        receipt,
324        estimated_bundle_weight: Default::default(),
325        bundle_extrinsics_root: Default::default(),
326    };
327    let signature = OperatorSignature::unchecked_from([0u8; 64]);
328
329    OpaqueBundle::V0(BundleV0 {
330        sealed_header: SealedBundleHeaderV0::new(header, signature),
331        extrinsics: Vec::new(),
332    })
333}
334
335/// A digest of the bundle
336#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
337pub struct BundleDigest<Hash> {
338    /// The hash of the bundle header
339    pub header_hash: Hash,
340    /// The Merkle root of all new extrinsics included in this bundle.
341    pub extrinsics_root: Hash,
342    /// The size of the bundle body in bytes.
343    pub size: u32,
344}
345
346/// Bundle invalidity type
347///
348/// Each type contains the index of the first invalid extrinsic within the bundle
349#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq)]
350pub enum InvalidBundleType {
351    /// Failed to decode the opaque extrinsic.
352    #[codec(index = 0)]
353    UndecodableTx(u32),
354    /// Transaction is out of the tx range.
355    #[codec(index = 1)]
356    OutOfRangeTx(u32),
357    /// Transaction is illegal (unable to pay the fee, etc).
358    #[codec(index = 2)]
359    IllegalTx(u32),
360    /// Transaction is an invalid XDM.
361    #[codec(index = 3)]
362    InvalidXDM(u32),
363    /// Transaction is an inherent extrinsic.
364    #[codec(index = 4)]
365    InherentExtrinsic(u32),
366    /// The `estimated_bundle_weight` in the bundle header is invalid
367    #[codec(index = 5)]
368    InvalidBundleWeight,
369}
370
371impl InvalidBundleType {
372    // Return the checking order of the invalid type
373    pub fn checking_order(&self) -> u64 {
374        // A bundle can contains multiple invalid extrinsics thus consider the first invalid extrinsic
375        // as the invalid type
376        let extrinsic_order = match self {
377            Self::UndecodableTx(i) => *i,
378            Self::OutOfRangeTx(i) => *i,
379            Self::IllegalTx(i) => *i,
380            Self::InvalidXDM(i) => *i,
381            Self::InherentExtrinsic(i) => *i,
382            // NOTE: the `InvalidBundleWeight` is targeting the whole bundle not a specific
383            // single extrinsic, as `extrinsic_index` is used as the order to check the extrinsic
384            // in the bundle returning `u32::MAX` indicate `InvalidBundleWeight` is checked after
385            // all the extrinsic in the bundle is checked.
386            Self::InvalidBundleWeight => u32::MAX,
387        };
388
389        // The extrinsic can be considered as invalid due to multiple `invalid_type` (i.e. an extrinsic
390        // can be `OutOfRangeTx` and `IllegalTx` at the same time) thus use the following checking order
391        // and consider the first check as the invalid type
392        //
393        // NOTE: Use explicit number as the order instead of the enum discriminant
394        // to avoid changing the order accidentally
395        let rule_order = match self {
396            Self::UndecodableTx(_) => 1,
397            Self::OutOfRangeTx(_) => 2,
398            Self::InherentExtrinsic(_) => 3,
399            Self::InvalidXDM(_) => 4,
400            Self::IllegalTx(_) => 5,
401            Self::InvalidBundleWeight => 6,
402        };
403
404        // The checking order is a combination of the `extrinsic_order` and `rule_order`
405        // it presents as an `u64` where the first 32 bits is the `extrinsic_order` and
406        // last 32 bits is the `rule_order` meaning the `extrinsic_order` is checked first
407        // then the `rule_order`.
408        ((extrinsic_order as u64) << 32) | (rule_order as u64)
409    }
410
411    // Return the index of the extrinsic that the invalid type points to
412    //
413    // NOTE: `InvalidBundleWeight` will return `None` since it is targeting the whole bundle not a
414    // specific single extrinsic
415    pub fn extrinsic_index(&self) -> Option<u32> {
416        match self {
417            Self::UndecodableTx(i) => Some(*i),
418            Self::OutOfRangeTx(i) => Some(*i),
419            Self::IllegalTx(i) => Some(*i),
420            Self::InvalidXDM(i) => Some(*i),
421            Self::InherentExtrinsic(i) => Some(*i),
422            Self::InvalidBundleWeight => None,
423        }
424    }
425}
426
427#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
428pub enum BundleValidity<Hash> {
429    // The invalid bundle was originally included in the consensus block but subsequently
430    // excluded from execution as invalid and holds the `InvalidBundleType`
431    Invalid(InvalidBundleType),
432    // The valid bundle's hash of `Vec<(tx_signer, tx_hash)>` of all domain extrinsic being
433    // included in the bundle.
434    // TODO remove this and use host function to fetch above mentioned data
435    Valid(Hash),
436}
437
438/// [`InboxedBundle`] represents a bundle that was successfully submitted to the consensus chain
439#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
440pub struct InboxedBundle<Hash> {
441    pub bundle: BundleValidity<Hash>,
442    // TODO remove this as the root is already present in the `ExecutionInbox` storage
443    pub extrinsics_root: Hash,
444}
445
446impl<Hash> InboxedBundle<Hash> {
447    pub fn valid(bundle_digest_hash: Hash, extrinsics_root: Hash) -> Self {
448        InboxedBundle {
449            bundle: BundleValidity::Valid(bundle_digest_hash),
450            extrinsics_root,
451        }
452    }
453
454    pub fn invalid(invalid_bundle_type: InvalidBundleType, extrinsics_root: Hash) -> Self {
455        InboxedBundle {
456            bundle: BundleValidity::Invalid(invalid_bundle_type),
457            extrinsics_root,
458        }
459    }
460
461    pub fn is_invalid(&self) -> bool {
462        matches!(self.bundle, BundleValidity::Invalid(_))
463    }
464
465    pub fn invalid_extrinsic_index(&self) -> Option<u32> {
466        match &self.bundle {
467            BundleValidity::Invalid(invalid_bundle_type) => invalid_bundle_type.extrinsic_index(),
468            BundleValidity::Valid(_) => None,
469        }
470    }
471
472    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
473    pub fn dummy(extrinsics_root: Hash) -> Self
474    where
475        Hash: Default,
476    {
477        InboxedBundle {
478            bundle: BundleValidity::Valid(Hash::default()),
479            extrinsics_root,
480        }
481    }
482}