sp_domains_fraud_proof/
fraud_proof.rs

1#[cfg(not(feature = "std"))]
2extern crate alloc;
3
4use crate::storage_proof::{self, *};
5#[cfg(not(feature = "std"))]
6use alloc::vec::Vec;
7use core::fmt;
8use frame_support::pallet_prelude::Zero;
9use parity_scale_codec::{Decode, Encode};
10use scale_info::TypeInfo;
11use sp_core::H256;
12use sp_domain_digests::AsPredigest;
13use sp_domains::bundle::{BundleValidity, InvalidBundleType};
14use sp_domains::execution_receipt::ExecutionReceiptFor;
15use sp_domains::proof_provider_and_verifier::StorageProofVerifier;
16use sp_domains::{DomainId, ExtrinsicDigest, HeaderHashFor, HeaderHashingFor};
17use sp_runtime::traits::{Block as BlockT, Hash, Header as HeaderT};
18use sp_runtime::{Digest, DigestItem};
19use sp_subspace_mmr::ConsensusChainMmrLeafProof;
20use sp_trie::StorageProof;
21use subspace_runtime_primitives::Balance;
22
23/// Mismatch type possible for ApplyExtrinsic execution phase
24#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
25pub enum ApplyExtrinsicMismatch {
26    StateRoot(u32),
27    Shorter,
28}
29
30/// Mismatch type possible for FinalizBlock execution phase
31#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
32pub enum FinalizeBlockMismatch {
33    StateRoot,
34    Longer(u32),
35}
36
37/// A phase of a block's execution, carrying necessary information needed for verifying the
38/// invalid state transition proof.
39#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
40pub enum ExecutionPhase {
41    /// Executes the `initialize_block` hook.
42    InitializeBlock,
43    /// Executes some extrinsic.
44    ApplyExtrinsic {
45        extrinsic_proof: StorageProof,
46        mismatch: ApplyExtrinsicMismatch,
47    },
48    /// Executes the `finalize_block` hook.
49    FinalizeBlock { mismatch: FinalizeBlockMismatch },
50}
51
52impl ExecutionPhase {
53    /// Returns the method for generating the proof.
54    pub fn execution_method(&self) -> &'static str {
55        match self {
56            // TODO: Replace `DomainCoreApi_initialize_block_with_post_state_root` with `Core_initalize_block`
57            // Should be a same issue with https://github.com/paritytech/substrate/pull/10922#issuecomment-1068997467
58            Self::InitializeBlock => "DomainCoreApi_initialize_block_with_post_state_root",
59            Self::ApplyExtrinsic { .. } => "DomainCoreApi_apply_extrinsic_with_post_state_root",
60            Self::FinalizeBlock { .. } => "BlockBuilder_finalize_block",
61        }
62    }
63
64    /// Returns true if execution phase refers to mismatch between state roots
65    /// false otherwise.
66    pub fn is_state_root_mismatch(&self) -> bool {
67        matches!(
68            self,
69            ExecutionPhase::InitializeBlock
70                | ExecutionPhase::ApplyExtrinsic {
71                    mismatch: ApplyExtrinsicMismatch::StateRoot(_),
72                    extrinsic_proof: _,
73                }
74                | ExecutionPhase::FinalizeBlock {
75                    mismatch: FinalizeBlockMismatch::StateRoot,
76                }
77        )
78    }
79    /// Returns the post state root for the given execution result.
80    pub fn decode_execution_result<Header: HeaderT>(
81        &self,
82        execution_result: Vec<u8>,
83    ) -> Result<Header::Hash, VerificationError<Header::Hash>> {
84        match self {
85            Self::InitializeBlock | Self::ApplyExtrinsic { .. } => {
86                let encoded_storage_root = Vec::<u8>::decode(&mut execution_result.as_slice())
87                    .map_err(VerificationError::InitializeBlockOrApplyExtrinsicDecode)?;
88                Header::Hash::decode(&mut encoded_storage_root.as_slice())
89                    .map_err(VerificationError::StorageRootDecode)
90            }
91            Self::FinalizeBlock { .. } => {
92                let new_header = Header::decode(&mut execution_result.as_slice())
93                    .map_err(VerificationError::HeaderDecode)?;
94                Ok(*new_header.state_root())
95            }
96        }
97    }
98
99    pub fn pre_post_state_root<CBlock, DomainHeader, Balance>(
100        &self,
101        bad_receipt: &ExecutionReceiptFor<DomainHeader, CBlock, Balance>,
102        bad_receipt_parent: &ExecutionReceiptFor<DomainHeader, CBlock, Balance>,
103    ) -> Result<(H256, H256), VerificationError<DomainHeader::Hash>>
104    where
105        CBlock: BlockT,
106        DomainHeader: HeaderT,
107        DomainHeader::Hash: Into<H256>,
108        Balance: Encode + Zero + Default,
109    {
110        if bad_receipt.execution_traces().len() < 2 {
111            return Err(VerificationError::InvalidExecutionTrace);
112        }
113        let (pre, post) = match self {
114            ExecutionPhase::InitializeBlock => (
115                *bad_receipt_parent.final_state_root(),
116                bad_receipt.execution_traces()[0],
117            ),
118            ExecutionPhase::ApplyExtrinsic {
119                mismatch: ApplyExtrinsicMismatch::StateRoot(mismatch_index),
120                ..
121            } => {
122                if *mismatch_index == 0
123                    || *mismatch_index >= bad_receipt.execution_traces().len() as u32 - 1
124                {
125                    return Err(VerificationError::InvalidApplyExtrinsicTraceIndex);
126                }
127                (
128                    bad_receipt.execution_traces()[*mismatch_index as usize - 1],
129                    bad_receipt.execution_traces()[*mismatch_index as usize],
130                )
131            }
132            ExecutionPhase::ApplyExtrinsic {
133                mismatch: ApplyExtrinsicMismatch::Shorter,
134                ..
135            } => {
136                let mismatch_index = bad_receipt.execution_traces().len() - 1;
137                (
138                    bad_receipt.execution_traces()[mismatch_index - 1],
139                    bad_receipt.execution_traces()[mismatch_index],
140                )
141            }
142            ExecutionPhase::FinalizeBlock {
143                mismatch: FinalizeBlockMismatch::StateRoot,
144            } => {
145                let mismatch_index = bad_receipt.execution_traces().len() - 1;
146                (
147                    bad_receipt.execution_traces()[mismatch_index - 1],
148                    bad_receipt.execution_traces()[mismatch_index],
149                )
150            }
151            ExecutionPhase::FinalizeBlock {
152                mismatch: FinalizeBlockMismatch::Longer(mismatch_index),
153            } => {
154                if *mismatch_index == 0
155                    || *mismatch_index >= bad_receipt.execution_traces().len() as u32 - 1
156                {
157                    return Err(VerificationError::InvalidLongerMismatchTraceIndex);
158                }
159                (
160                    bad_receipt.execution_traces()[(*mismatch_index - 1) as usize],
161                    bad_receipt.execution_traces()[*mismatch_index as usize],
162                )
163            }
164        };
165        Ok((pre.into(), post.into()))
166    }
167
168    pub fn call_data<CBlock, DomainHeader, Balance>(
169        &self,
170        bad_receipt: &ExecutionReceiptFor<DomainHeader, CBlock, Balance>,
171        bad_receipt_parent: &ExecutionReceiptFor<DomainHeader, CBlock, Balance>,
172    ) -> Result<Vec<u8>, VerificationError<DomainHeader::Hash>>
173    where
174        CBlock: BlockT,
175        DomainHeader: HeaderT,
176        Balance: Encode + Zero + Default,
177    {
178        Ok(match self {
179            ExecutionPhase::InitializeBlock => {
180                let inherent_digests = Digest {
181                    logs: sp_std::vec![DigestItem::consensus_block_info(
182                        bad_receipt.consensus_block_hash(),
183                    )],
184                };
185
186                let new_header = DomainHeader::new(
187                    *bad_receipt.domain_block_number(),
188                    Default::default(),
189                    Default::default(),
190                    *bad_receipt_parent.domain_block_hash(),
191                    inherent_digests,
192                );
193                new_header.encode()
194            }
195            ExecutionPhase::ApplyExtrinsic {
196                extrinsic_proof: proof_of_inclusion,
197                mismatch,
198            } => {
199                let mismatch_index = match mismatch {
200                    ApplyExtrinsicMismatch::StateRoot(mismatch_index) => *mismatch_index,
201                    ApplyExtrinsicMismatch::Shorter => {
202                        (bad_receipt.execution_traces().len() - 1) as u32
203                    }
204                };
205                // There is a trace root of the `initialize_block` in the head of the trace so we
206                // need to minus one to get the correct `extrinsic_index`
207                let extrinsic_index: u32 = mismatch_index - 1;
208
209                let storage_key =
210                    StorageProofVerifier::<DomainHeader::Hashing>::enumerated_storage_key(
211                        extrinsic_index,
212                    );
213
214                StorageProofVerifier::<DomainHeader::Hashing>::get_bare_value(
215                    bad_receipt.domain_block_extrinsics_root(),
216                    proof_of_inclusion.clone(),
217                    storage_key,
218                )
219                .map_err(|_| VerificationError::InvalidApplyExtrinsicCallData)?
220            }
221            ExecutionPhase::FinalizeBlock { .. } => Vec::new(),
222        })
223    }
224}
225
226/// Error type of fraud proof verification on consensus node.
227#[derive(Debug, thiserror::Error)]
228pub enum VerificationError<DomainHash> {
229    /// Failed to pass the execution proof check.
230    #[error("Failed to pass the execution proof check")]
231    BadExecutionProof,
232    /// The fraud proof prove nothing invalid
233    #[error("The fraud proof prove nothing invalid")]
234    InvalidProof,
235    /// Failed to decode the return value of `initialize_block` and `apply_extrinsic`.
236    #[error("Failed to decode the return value of `initialize_block` and `apply_extrinsic`: {0}")]
237    InitializeBlockOrApplyExtrinsicDecode(parity_scale_codec::Error),
238    /// Failed to decode the storage root produced by verifying `initialize_block` or `apply_extrinsic`.
239    #[error(
240        "Failed to decode the storage root from verifying `initialize_block` and `apply_extrinsic`: {0}"
241    )]
242    StorageRootDecode(parity_scale_codec::Error),
243    /// Failed to decode the header produced by `finalize_block`.
244    #[error("Failed to decode the header from verifying `finalize_block`: {0}")]
245    HeaderDecode(parity_scale_codec::Error),
246    #[error("The receipt's execution_traces have less than 2 traces")]
247    InvalidExecutionTrace,
248    #[error("Invalid ApplyExtrinsic trace index")]
249    InvalidApplyExtrinsicTraceIndex,
250    #[error("Invalid longer mismatch trace index")]
251    InvalidLongerMismatchTraceIndex,
252    #[error("Invalid ApplyExtrinsic call data")]
253    InvalidApplyExtrinsicCallData,
254    /// Invalid bundle digest
255    #[error("Invalid Bundle Digest")]
256    InvalidBundleDigest,
257    /// Bundle with requested index not found in execution receipt
258    #[error("Bundle with requested index not found in execution receipt")]
259    BundleNotFound,
260    /// Invalid bundle entry in bad receipt was expected to be valid but instead found invalid entry
261    #[error(
262        "Unexpected bundle entry at {bundle_index} in bad receipt found: \
263        {targeted_entry_bundle:?} with fraud proof's type of proof: \
264        {fraud_proof_invalid_type_of_proof:?}"
265    )]
266    UnexpectedTargetedBundleEntry {
267        bundle_index: u32,
268        fraud_proof_invalid_type_of_proof: InvalidBundleType,
269        targeted_entry_bundle: BundleValidity<DomainHash>,
270    },
271    /// Failed to derive bundle digest
272    #[error("Failed to derive bundle digest")]
273    FailedToDeriveBundleDigest,
274    /// The target valid bundle not found from the target bad receipt
275    #[error("The target valid bundle not found from the target bad receipt")]
276    TargetValidBundleNotFound,
277    /// Failed to check extrinsics in single context
278    #[error("Failed to check extrinsics in single context")]
279    FailedToCheckExtrinsicsInSingleContext,
280    #[error(
281        "Bad MMR proof, the proof is probably expired or is generated against a different fork"
282    )]
283    BadMmrProof,
284    #[error("Unexpected MMR proof")]
285    UnexpectedMmrProof,
286    #[error("Failed to verify storage proof")]
287    StorageProof(storage_proof::VerificationError),
288    /// Failed to derive domain inherent extrinsic
289    #[error("Failed to derive domain inherent extrinsic")]
290    FailedToDeriveDomainInherentExtrinsic,
291    /// Failed to derive domain storage key
292    #[error("Failed to derive domain storage key")]
293    FailedToGetDomainStorageKey,
294    /// Unexpected invalid bundle proof data
295    #[error("Unexpected invalid bundle proof data")]
296    UnexpectedInvalidBundleProofData,
297    /// Extrinsic with requested index not found in bundle
298    #[error("Extrinsic with requested index not found in bundle")]
299    ExtrinsicNotFound,
300    /// Failed to get domain runtime call response
301    #[error("Failed to get domain runtime call response")]
302    FailedToGetDomainRuntimeCallResponse,
303    /// Failed to get bundle weight
304    #[error("Failed to get bundle weight")]
305    FailedToGetBundleWeight,
306    #[error("Failed to extract xdm mmr proof")]
307    FailedToGetExtractXdmMmrProof,
308    #[error("Failed to decode xdm mmr proof")]
309    FailedToDecodeXdmMmrProof,
310}
311
312impl<DomainHash> From<storage_proof::VerificationError> for VerificationError<DomainHash> {
313    fn from(err: storage_proof::VerificationError) -> Self {
314        Self::StorageProof(err)
315    }
316}
317
318/// Fraud proof for domains.
319#[derive(Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
320pub struct FraudProof<Number, Hash, DomainHeader: HeaderT, MmrHash> {
321    pub domain_id: DomainId,
322    /// Hash of the bad receipt this fraud proof targeted
323    pub bad_receipt_hash: HeaderHashFor<DomainHeader>,
324    /// The MMR proof for the consensus state root that is used to verify the storage proof
325    ///
326    /// It is set `None` if the specific fraud proof variant doesn't contain a storage proof
327    pub maybe_mmr_proof: Option<ConsensusChainMmrLeafProof<Number, Hash, MmrHash>>,
328    /// The domain runtime code storage proof
329    ///
330    /// It is set `None` if the specific fraud proof variant doesn't require domain runtime code
331    /// or the required domain runtime code is available from the current runtime state.
332    pub maybe_domain_runtime_code_proof: Option<DomainRuntimeCodeAt<Number, Hash, MmrHash>>,
333    /// The specific fraud proof variant
334    pub proof: FraudProofVariant<Number, Hash, MmrHash, DomainHeader>,
335}
336
337#[allow(clippy::large_enum_variant)]
338#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
339pub enum FraudProofVariant<Number, Hash, MmrHash, DomainHeader: HeaderT> {
340    #[codec(index = 0)]
341    InvalidStateTransition(InvalidStateTransitionProof),
342    #[codec(index = 1)]
343    ValidBundle(ValidBundleProof<Number, Hash, DomainHeader>),
344    #[codec(index = 2)]
345    InvalidExtrinsicsRoot(InvalidExtrinsicsRootProof),
346    #[codec(index = 3)]
347    InvalidBundles(InvalidBundlesProof<Number, Hash, MmrHash, DomainHeader>),
348    #[codec(index = 4)]
349    InvalidDomainBlockHash(InvalidDomainBlockHashProof),
350    #[codec(index = 5)]
351    InvalidBlockFees(InvalidBlockFeesProof),
352    #[codec(index = 6)]
353    InvalidTransfers(InvalidTransfersProof),
354    /// Dummy fraud proof only used in tests and benchmarks
355    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
356    #[codec(index = 100)]
357    Dummy,
358}
359
360impl<Number, Hash, MmrHash, DomainHeader: HeaderT> FraudProof<Number, Hash, DomainHeader, MmrHash> {
361    pub fn domain_id(&self) -> DomainId {
362        self.domain_id
363    }
364
365    pub fn targeted_bad_receipt_hash(&self) -> HeaderHashFor<DomainHeader> {
366        self.bad_receipt_hash
367    }
368
369    pub fn is_unexpected_domain_runtime_code_proof(&self) -> bool {
370        // The invalid domain block hash fraud proof doesn't use the domain runtime code
371        // during its verification so it is unexpected to see `maybe_domain_runtime_code_proof`
372        // set to `Some`
373        self.maybe_domain_runtime_code_proof.is_some()
374            && matches!(self.proof, FraudProofVariant::InvalidDomainBlockHash(_))
375    }
376
377    pub fn is_unexpected_mmr_proof(&self) -> bool {
378        if self.maybe_mmr_proof.is_none() {
379            return false;
380        }
381        // Only the `InvalidExtrinsicsRoot`, `InvalidBundles` and `ValidBundle` fraud proof
382        // are using the MMR proof during verifiction, for other fraud proofs it is unexpected
383        // to see `maybe_mmr_proof` set to `Some`
384        !matches!(
385            self.proof,
386            FraudProofVariant::InvalidExtrinsicsRoot(_)
387                | FraudProofVariant::InvalidBundles(_)
388                | FraudProofVariant::ValidBundle(_)
389        )
390    }
391
392    #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
393    pub fn dummy_fraud_proof(
394        domain_id: DomainId,
395        bad_receipt_hash: HeaderHashFor<DomainHeader>,
396    ) -> FraudProof<Number, Hash, DomainHeader, MmrHash> {
397        Self {
398            domain_id,
399            bad_receipt_hash,
400            maybe_mmr_proof: None,
401            maybe_domain_runtime_code_proof: None,
402            proof: FraudProofVariant::Dummy,
403        }
404    }
405}
406
407impl<Number, Hash, MmrHash, DomainHeader: HeaderT> FraudProof<Number, Hash, DomainHeader, MmrHash>
408where
409    Number: Encode,
410    Hash: Encode,
411    MmrHash: Encode,
412{
413    pub fn hash(&self) -> HeaderHashFor<DomainHeader> {
414        HeaderHashingFor::<DomainHeader>::hash(&self.encode())
415    }
416}
417
418impl<Number, Hash, MmrHash, DomainHeader: HeaderT> fmt::Debug
419    for FraudProof<Number, Hash, DomainHeader, MmrHash>
420{
421    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
422        let fp_target =
423            scale_info::prelude::format!("{:?}#{:?}", self.domain_id, self.bad_receipt_hash);
424        match &self.proof {
425            FraudProofVariant::InvalidStateTransition(_) => {
426                write!(f, "InvalidStateTransitionFraudProof({fp_target})")
427            }
428            FraudProofVariant::InvalidExtrinsicsRoot(_) => {
429                write!(f, "InvalidExtrinsicsRootFraudProof({fp_target})")
430            }
431            FraudProofVariant::InvalidBlockFees(_) => {
432                write!(f, "InvalidBlockFeesFraudProof({fp_target})")
433            }
434            FraudProofVariant::ValidBundle(_) => {
435                write!(f, "ValidBundleFraudProof({fp_target})")
436            }
437            FraudProofVariant::InvalidBundles(proof) => {
438                write!(
439                    f,
440                    "InvalidBundlesFraudProof(type: {:?}, target: {fp_target})",
441                    proof.invalid_bundle_type()
442                )
443            }
444            FraudProofVariant::InvalidDomainBlockHash(_) => {
445                write!(f, "InvalidDomainBlockHashFraudProof({fp_target})")
446            }
447            FraudProofVariant::InvalidTransfers(_) => {
448                write!(f, "InvalidTransfersFraudProof({fp_target})")
449            }
450            #[cfg(any(feature = "std", feature = "runtime-benchmarks"))]
451            FraudProofVariant::Dummy => {
452                write!(f, "DummyFraudProof({fp_target})")
453            }
454        }
455    }
456}
457
458/// Represents a valid bundle index and all the extrinsics within that bundle.
459#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
460pub struct ValidBundleDigest {
461    /// Index of this bundle in the original list of bundles in the consensus block.
462    pub bundle_index: u32,
463    /// `Vec<(tx_signer, tx_hash)>` of all extrinsics
464    pub bundle_digest: Vec<(
465        Option<domain_runtime_primitives::opaque::AccountId>,
466        ExtrinsicDigest,
467    )>,
468}
469
470// Domain runtime code at a specific block
471#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
472pub struct DomainRuntimeCodeAt<Number, Hash, MmrHash> {
473    pub mmr_proof: ConsensusChainMmrLeafProof<Number, Hash, MmrHash>,
474    pub domain_runtime_code_proof: DomainRuntimeCodeProof,
475}
476
477/// Proves an invalid state transition by challenging the trace at specific index in a bad receipt.
478#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
479pub struct InvalidStateTransitionProof {
480    /// Proof recorded during the computation.
481    pub execution_proof: StorageProof,
482    /// Execution phase.
483    pub execution_phase: ExecutionPhase,
484}
485
486/// Fraud proof for the valid bundles in `ExecutionReceipt::inboxed_bundles`
487#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
488pub struct ValidBundleProof<Number, Hash, DomainHeader: HeaderT> {
489    /// The targeted bundle with proof
490    pub bundle_with_proof: OpaqueBundleWithProof<Number, Hash, DomainHeader, Balance>,
491}
492
493impl<Number, Hash, DomainHeader: HeaderT> ValidBundleProof<Number, Hash, DomainHeader> {
494    pub fn set_bundle_index(&mut self, index: u32) {
495        self.bundle_with_proof.bundle_index = index
496    }
497}
498
499#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
500pub struct InvalidExtrinsicsRootProof {
501    /// Valid Bundle digests
502    pub valid_bundle_digests: Vec<ValidBundleDigest>,
503
504    /// The combined storage proofs used during verification
505    pub invalid_inherent_extrinsic_proofs: InvalidInherentExtrinsicDataProof,
506
507    /// A single domain runtime code upgrade (or "not upgraded") storage proof
508    pub maybe_domain_runtime_upgraded_proof: MaybeDomainRuntimeUpgradedProof,
509
510    /// Storage proof for a change to the chains that are allowed to open a channel with each domain
511    pub domain_chain_allowlist_proof: DomainChainsAllowlistUpdateStorageProof,
512
513    /// Optional sudo extrinsic call storage proof
514    pub domain_sudo_call_proof: DomainSudoCallStorageProof,
515
516    /// Optional EVM domain "set contract creation allowed by" extrinsic call storage proof
517    pub evm_domain_contract_creation_allowed_by_call_proof:
518        EvmDomainContractCreationAllowedByCallStorageProof,
519}
520
521#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
522pub struct MmrRootProof<Number, Hash, MmrHash> {
523    pub mmr_proof: ConsensusChainMmrLeafProof<Number, Hash, MmrHash>,
524    pub mmr_root_storage_proof: MmrRootStorageProof<MmrHash>,
525}
526
527/// Invalid versioned bundle proof data.
528#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
529pub enum InvalidBundlesProofData<Number, Hash, MmrHash, DomainHeader: HeaderT> {
530    Extrinsic(StorageProof),
531    Bundle(OpaqueBundleWithProof<Number, Hash, DomainHeader, Balance>),
532    BundleAndExecution {
533        bundle_with_proof: OpaqueBundleWithProof<Number, Hash, DomainHeader, Balance>,
534        execution_proof: StorageProof,
535    },
536    InvalidXDMProofData {
537        extrinsic_proof: StorageProof,
538        mmr_root_proof: Option<MmrRootProof<Number, Hash, MmrHash>>,
539    },
540}
541
542/// A proof about a bundle that was marked invalid (but might or might not actually be invalid).
543#[derive(Debug, Decode, Encode, TypeInfo, PartialEq, Eq, Clone)]
544pub struct InvalidBundlesProof<Number, Hash, MmrHash, DomainHeader: HeaderT> {
545    pub bundle_index: u32,
546    /// The invalid bundle type that the bundle was marked with.
547    pub invalid_bundle_type: InvalidBundleType,
548    /// If `true`, the fraud proof must prove the bundle was correctly marked invalid.
549    /// If `false`, it must prove the bundle was marked invalid, but is actually valid.
550    pub is_good_invalid_fraud_proof: bool,
551    /// Proof data of the bundle which was marked invalid.
552    pub proof_data: InvalidBundlesProofData<Number, Hash, MmrHash, DomainHeader>,
553}
554
555impl<Number, Hash, MmrHash, DomainHeader: HeaderT>
556    InvalidBundlesProof<Number, Hash, MmrHash, DomainHeader>
557{
558    pub fn invalid_bundle_type(&self) -> InvalidBundleType {
559        self.invalid_bundle_type.clone()
560    }
561
562    pub fn is_good_invalid_fraud_proof(&self) -> bool {
563        self.is_good_invalid_fraud_proof
564    }
565}
566
567/// Represents an invalid block fees proof.
568#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
569pub struct InvalidBlockFeesProof {
570    /// Storage witness needed for verifying this proof.
571    pub storage_proof: StorageProof,
572}
573
574/// Represents an invalid transfers proof.
575#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
576pub struct InvalidTransfersProof {
577    /// Storage witness needed for verifying this proof.
578    pub storage_proof: StorageProof,
579}
580
581/// Represents an invalid domain block hash fraud proof.
582#[derive(Clone, Debug, Decode, Encode, Eq, PartialEq, TypeInfo)]
583pub struct InvalidDomainBlockHashProof {
584    /// Digests storage proof that is used to derive Domain block hash.
585    pub digest_storage_proof: StorageProof,
586}