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, DecodeWithMemTracking, 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, DecodeWithMemTracking)]
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        '_,
162        Number,
163        Hash,
164        HeaderNumberFor<DomainHeader>,
165        HeaderHashFor<DomainHeader>,
166        Balance,
167    > {
168        match self {
169            Bundle::V0(bundle) => match &bundle.sealed_header.header.receipt {
170                ExecutionReceipt::V0(er) => ExecutionReceiptRef::V0(er),
171            },
172        }
173    }
174
175    /// Return the bundle size (include header and body) in bytes
176    pub fn size(&self) -> u32 {
177        self.encoded_size() as u32
178    }
179
180    /// Return the bundle body size in bytes
181    pub fn body_size(&self) -> u32 {
182        let extrinsics = match self {
183            Bundle::V0(bundle) => &bundle.extrinsics,
184        };
185
186        extrinsics
187            .iter()
188            .map(|tx| tx.encoded_size() as u32)
189            .sum::<u32>()
190    }
191
192    /// Return the bundle body length. i.e number of extrinsics in the bundle.
193    pub fn body_length(&self) -> usize {
194        let extrinsics = match self {
195            Bundle::V0(bundle) => &bundle.extrinsics,
196        };
197        extrinsics.len()
198    }
199
200    /// Returns the estimated weight of the Bundle.
201    pub fn estimated_weight(&self) -> Weight {
202        match self {
203            Bundle::V0(bundle) => bundle.sealed_header.header.estimated_bundle_weight,
204        }
205    }
206
207    /// Returns the slot number at this bundle was constructed.
208    pub fn slot_number(&self) -> u64 {
209        match self {
210            Bundle::V0(bundle) => bundle.sealed_header.header.proof_of_election.slot_number,
211        }
212    }
213
214    /// Returns the reference to proof of election.
215    pub fn proof_of_election(&self) -> &ProofOfElection {
216        match self {
217            Bundle::V0(bundle) => &bundle.sealed_header.header.proof_of_election,
218        }
219    }
220
221    /// Returns the sealed header in the Bundle.
222    pub fn sealed_header(&self) -> SealedBundleHeaderRef<'_, Number, Hash, DomainHeader, Balance> {
223        match self {
224            Bundle::V0(bundle) => SealedBundleHeaderRef::V0(&bundle.sealed_header),
225        }
226    }
227
228    /// Returns the bundle body consuming the bundle. ie extrinsics.
229    pub fn into_extrinsics(self) -> Vec<Extrinsic> {
230        match self {
231            Bundle::V0(bundle) => bundle.extrinsics,
232        }
233    }
234
235    /// Returns the reference to bundle body. ie extrinsics.
236    pub fn extrinsics(&self) -> &[Extrinsic] {
237        match self {
238            Bundle::V0(bundle) => &bundle.extrinsics,
239        }
240    }
241
242    /// Sets extrinsics to the bundle.
243    pub fn set_extrinsics(&mut self, exts: Vec<Extrinsic>) {
244        match self {
245            Bundle::V0(bundle) => bundle.extrinsics = exts,
246        }
247    }
248
249    /// Sets bundle extrinsic root.
250    pub fn set_bundle_extrinsics_root(&mut self, root: HeaderHashFor<DomainHeader>) {
251        match self {
252            Bundle::V0(bundle) => bundle.sealed_header.header.bundle_extrinsics_root = root,
253        }
254    }
255
256    /// Returns a mutable reference to Execution receipt.
257    pub fn execution_receipt_as_mut(
258        &mut self,
259    ) -> ExecutionReceiptMutRef<
260        '_,
261        Number,
262        Hash,
263        HeaderNumberFor<DomainHeader>,
264        HeaderHashFor<DomainHeader>,
265        Balance,
266    > {
267        match self {
268            Bundle::V0(bundle) => match &mut bundle.sealed_header.header.receipt {
269                ExecutionReceipt::V0(er) => ExecutionReceiptMutRef::V0(er),
270            },
271        }
272    }
273
274    /// Set estimated bundle weight.
275    pub fn set_estimated_bundle_weight(&mut self, weight: Weight) {
276        match self {
277            Bundle::V0(bundle) => bundle.sealed_header.header.estimated_bundle_weight = weight,
278        }
279    }
280
281    /// Sets signature of the bundle.
282    pub fn set_signature(&mut self, signature: OperatorSignature) {
283        match self {
284            Bundle::V0(bundle) => bundle.sealed_header.signature = signature,
285        }
286    }
287
288    /// Sets proof of election for bundle.
289    pub fn set_proof_of_election(&mut self, poe: ProofOfElection) {
290        match self {
291            Bundle::V0(bundle) => bundle.sealed_header.header.proof_of_election = poe,
292        }
293    }
294}
295
296/// Bundle with opaque extrinsics.
297pub type OpaqueBundle<Number, Hash, DomainHeader, Balance> =
298    Bundle<OpaqueExtrinsic, Number, Hash, DomainHeader, Balance>;
299
300/// List of [`OpaqueBundle`].
301pub type OpaqueBundles<Block, DomainHeader, Balance> =
302    Vec<OpaqueBundle<NumberFor<Block>, BlockHashFor<Block>, DomainHeader, Balance>>;
303
304#[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
305pub fn dummy_opaque_bundle<
306    Number: Encode,
307    Hash: Default + Encode,
308    DomainHeader: HeaderT,
309    Balance: Encode,
310>(
311    domain_id: DomainId,
312    operator_id: OperatorId,
313    receipt: ExecutionReceipt<
314        Number,
315        Hash,
316        HeaderNumberFor<DomainHeader>,
317        HeaderHashFor<DomainHeader>,
318        Balance,
319    >,
320) -> OpaqueBundle<Number, Hash, DomainHeader, Balance> {
321    use sp_core::crypto::UncheckedFrom;
322
323    let header = BundleHeaderV0 {
324        proof_of_election: ProofOfElection::dummy(domain_id, operator_id),
325        receipt,
326        estimated_bundle_weight: Default::default(),
327        bundle_extrinsics_root: Default::default(),
328    };
329    let signature = OperatorSignature::unchecked_from([0u8; 64]);
330
331    OpaqueBundle::V0(BundleV0 {
332        sealed_header: SealedBundleHeaderV0::new(header, signature),
333        extrinsics: Vec::new(),
334    })
335}
336
337/// A digest of the bundle
338#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
339pub struct BundleDigest<Hash> {
340    /// The hash of the bundle header
341    pub header_hash: Hash,
342    /// The Merkle root of all new extrinsics included in this bundle.
343    pub extrinsics_root: Hash,
344    /// The size of the bundle body in bytes.
345    pub size: u32,
346}
347
348/// Bundle invalidity type
349///
350/// Each type contains the index of the first invalid extrinsic within the bundle
351#[derive(Debug, Decode, Encode, TypeInfo, Clone, PartialEq, Eq, DecodeWithMemTracking)]
352pub enum InvalidBundleType {
353    /// Failed to decode the opaque extrinsic.
354    #[codec(index = 0)]
355    UndecodableTx(u32),
356    /// Transaction is out of the tx range.
357    #[codec(index = 1)]
358    OutOfRangeTx(u32),
359    /// Transaction is illegal (unable to pay the fee, etc).
360    #[codec(index = 2)]
361    IllegalTx(u32),
362    /// Transaction is an invalid XDM.
363    #[codec(index = 3)]
364    InvalidXDM(u32),
365    /// Transaction is an inherent extrinsic.
366    #[codec(index = 4)]
367    InherentExtrinsic(u32),
368    /// The `estimated_bundle_weight` in the bundle header is invalid
369    #[codec(index = 5)]
370    InvalidBundleWeight,
371}
372
373impl InvalidBundleType {
374    // Return the checking order of the invalid type
375    pub fn checking_order(&self) -> u64 {
376        // A bundle can contains multiple invalid extrinsics thus consider the first invalid extrinsic
377        // as the invalid type
378        let extrinsic_order = match self {
379            Self::UndecodableTx(i) => *i,
380            Self::OutOfRangeTx(i) => *i,
381            Self::IllegalTx(i) => *i,
382            Self::InvalidXDM(i) => *i,
383            Self::InherentExtrinsic(i) => *i,
384            // NOTE: the `InvalidBundleWeight` is targeting the whole bundle not a specific
385            // single extrinsic, as `extrinsic_index` is used as the order to check the extrinsic
386            // in the bundle returning `u32::MAX` indicate `InvalidBundleWeight` is checked after
387            // all the extrinsic in the bundle is checked.
388            Self::InvalidBundleWeight => u32::MAX,
389        };
390
391        // The extrinsic can be considered as invalid due to multiple `invalid_type` (i.e. an extrinsic
392        // can be `OutOfRangeTx` and `IllegalTx` at the same time) thus use the following checking order
393        // and consider the first check as the invalid type
394        //
395        // NOTE: Use explicit number as the order instead of the enum discriminant
396        // to avoid changing the order accidentally
397        let rule_order = match self {
398            Self::UndecodableTx(_) => 1,
399            Self::OutOfRangeTx(_) => 2,
400            Self::InherentExtrinsic(_) => 3,
401            Self::InvalidXDM(_) => 4,
402            Self::IllegalTx(_) => 5,
403            Self::InvalidBundleWeight => 6,
404        };
405
406        // The checking order is a combination of the `extrinsic_order` and `rule_order`
407        // it presents as an `u64` where the first 32 bits is the `extrinsic_order` and
408        // last 32 bits is the `rule_order` meaning the `extrinsic_order` is checked first
409        // then the `rule_order`.
410        ((extrinsic_order as u64) << 32) | (rule_order as u64)
411    }
412
413    // Return the index of the extrinsic that the invalid type points to
414    //
415    // NOTE: `InvalidBundleWeight` will return `None` since it is targeting the whole bundle not a
416    // specific single extrinsic
417    pub fn extrinsic_index(&self) -> Option<u32> {
418        match self {
419            Self::UndecodableTx(i) => Some(*i),
420            Self::OutOfRangeTx(i) => Some(*i),
421            Self::IllegalTx(i) => Some(*i),
422            Self::InvalidXDM(i) => Some(*i),
423            Self::InherentExtrinsic(i) => Some(*i),
424            Self::InvalidBundleWeight => None,
425        }
426    }
427}
428
429#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, DecodeWithMemTracking)]
430pub enum BundleValidity<Hash> {
431    // The invalid bundle was originally included in the consensus block but subsequently
432    // excluded from execution as invalid and holds the `InvalidBundleType`
433    Invalid(InvalidBundleType),
434    // The valid bundle's hash of `Vec<(tx_signer, tx_hash)>` of all domain extrinsic being
435    // included in the bundle.
436    // TODO remove this and use host function to fetch above mentioned data
437    Valid(Hash),
438}
439
440/// [`InboxedBundle`] represents a bundle that was successfully submitted to the consensus chain
441#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone, DecodeWithMemTracking)]
442pub struct InboxedBundle<Hash> {
443    pub bundle: BundleValidity<Hash>,
444    // TODO remove this as the root is already present in the `ExecutionInbox` storage
445    pub extrinsics_root: Hash,
446}
447
448impl<Hash> InboxedBundle<Hash> {
449    pub fn valid(bundle_digest_hash: Hash, extrinsics_root: Hash) -> Self {
450        InboxedBundle {
451            bundle: BundleValidity::Valid(bundle_digest_hash),
452            extrinsics_root,
453        }
454    }
455
456    pub fn invalid(invalid_bundle_type: InvalidBundleType, extrinsics_root: Hash) -> Self {
457        InboxedBundle {
458            bundle: BundleValidity::Invalid(invalid_bundle_type),
459            extrinsics_root,
460        }
461    }
462
463    pub fn is_invalid(&self) -> bool {
464        matches!(self.bundle, BundleValidity::Invalid(_))
465    }
466
467    pub fn invalid_extrinsic_index(&self) -> Option<u32> {
468        match &self.bundle {
469            BundleValidity::Invalid(invalid_bundle_type) => invalid_bundle_type.extrinsic_index(),
470            BundleValidity::Valid(_) => None,
471        }
472    }
473
474    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
475    pub fn dummy(extrinsics_root: Hash) -> Self
476    where
477        Hash: Default,
478    {
479        InboxedBundle {
480            bundle: BundleValidity::Valid(Hash::default()),
481            extrinsics_root,
482        }
483    }
484}