1#[cfg(not(feature = "std"))]
2extern crate alloc;
3
4use crate::fraud_proof::{
5 InvalidBundlesProof, InvalidBundlesProofData, InvalidExtrinsicsRootProof,
6 InvalidStateTransitionProof, MmrRootProof, ValidBundleProof, VerificationError,
7};
8use crate::storage_proof::{self, *};
9use crate::{
10 DomainInherentExtrinsic, DomainInherentExtrinsicData, DomainStorageKeyRequest,
11 StatelessDomainRuntimeCall, fraud_proof_runtime_interface,
12};
13#[cfg(not(feature = "std"))]
14use alloc::vec::Vec;
15use domain_runtime_primitives::BlockNumber;
16use hash_db::Hasher;
17use parity_scale_codec::{Decode, Encode};
18use sp_core::H256;
19use sp_core::storage::StorageKey;
20use sp_domains::bundle::{BundleValidity, InboxedBundle, InvalidBundleType};
21use sp_domains::execution_receipt::{BlockFees, ExecutionReceipt, Transfers};
22use sp_domains::extrinsics::deduplicate_and_shuffle_extrinsics;
23use sp_domains::proof_provider_and_verifier::StorageProofVerifier;
24use sp_domains::valued_trie::valued_ordered_trie_root;
25use sp_domains::{
26 DomainId, ExtrinsicDigest, HeaderHashFor, HeaderHashingFor, HeaderNumberFor,
27 INITIAL_DOMAIN_TX_RANGE, RuntimeId,
28};
29use sp_runtime::generic::Digest;
30use sp_runtime::traits::{
31 Block as BlockT, Hash, Header as HeaderT, NumberFor, UniqueSaturatedInto, Zero,
32};
33use sp_runtime::{OpaqueExtrinsic, SaturatedConversion};
34use sp_subspace_mmr::{ConsensusChainMmrLeafProof, MmrProofVerifier};
35use sp_trie::{LayoutV1, StorageProof};
36use subspace_core_primitives::U256;
37use trie_db::node::Value;
38
39pub fn verify_invalid_domain_extrinsics_root_fraud_proof<
41 CBlock,
42 Balance,
43 DomainHeader,
44 Hashing,
45 SKP,
46>(
47 bad_receipt: ExecutionReceipt<
48 NumberFor<CBlock>,
49 CBlock::Hash,
50 HeaderNumberFor<DomainHeader>,
51 HeaderHashFor<DomainHeader>,
52 Balance,
53 >,
54 fraud_proof: &InvalidExtrinsicsRootProof,
55 domain_id: DomainId,
56 runtime_id: RuntimeId,
57 state_root: CBlock::Hash,
58 domain_runtime_code: Vec<u8>,
59) -> Result<(), VerificationError<DomainHeader::Hash>>
60where
61 CBlock: BlockT,
62 DomainHeader: HeaderT,
63 DomainHeader::Hash: Into<H256> + PartialEq + Copy,
64 Hashing: Hasher<Out = CBlock::Hash>,
65 SKP: FraudProofStorageKeyProvider<NumberFor<CBlock>>,
66 Balance: Encode + Zero + Default,
67{
68 let InvalidExtrinsicsRootProof {
69 valid_bundle_digests,
70 invalid_inherent_extrinsic_proofs,
71 maybe_domain_runtime_upgraded_proof,
72 domain_chain_allowlist_proof,
73 domain_sudo_call_proof,
74 evm_domain_contract_creation_allowed_by_call_proof,
75 } = fraud_proof;
76
77 let invalid_inherent_extrinsic_data =
78 <InvalidInherentExtrinsicDataProof as BasicStorageProof<CBlock>>::verify::<SKP>(
79 invalid_inherent_extrinsic_proofs.clone(),
80 (),
81 &state_root,
82 )?;
83
84 let maybe_domain_runtime_upgrade =
85 maybe_domain_runtime_upgraded_proof.verify::<CBlock, SKP>(runtime_id, &state_root)?;
86
87 let domain_chain_allowlist = <DomainChainsAllowlistUpdateStorageProof as BasicStorageProof<
88 CBlock,
89 >>::verify::<SKP>(
90 domain_chain_allowlist_proof.clone(), domain_id, &state_root
91 )?;
92
93 let domain_sudo_call = <DomainSudoCallStorageProof as BasicStorageProof<CBlock>>::verify::<SKP>(
94 domain_sudo_call_proof.clone(),
95 domain_id,
96 &state_root,
97 )?;
98
99 let evm_domain_contract_creation_allowed_by_call =
100 <EvmDomainContractCreationAllowedByCallStorageProof as BasicStorageProof<CBlock>>::verify::<
101 SKP,
102 >(
103 evm_domain_contract_creation_allowed_by_call_proof.clone(),
104 domain_id,
105 &state_root,
106 )?;
107
108 let shuffling_seed = invalid_inherent_extrinsic_data.extrinsics_shuffling_seed;
109
110 let domain_inherent_extrinsic_data = DomainInherentExtrinsicData {
111 timestamp: invalid_inherent_extrinsic_data.timestamp,
112 maybe_domain_runtime_upgrade,
113 consensus_transaction_byte_fee: invalid_inherent_extrinsic_data
114 .consensus_transaction_byte_fee,
115 domain_chain_allowlist,
116 maybe_sudo_runtime_call: domain_sudo_call.maybe_call,
117 maybe_evm_domain_contract_creation_allowed_by_call:
118 evm_domain_contract_creation_allowed_by_call.maybe_call,
119 };
120
121 let DomainInherentExtrinsic {
122 domain_timestamp_extrinsic,
123 maybe_domain_chain_allowlist_extrinsic,
124 consensus_chain_byte_fee_extrinsic,
125 maybe_domain_set_code_extrinsic,
126 maybe_domain_sudo_call_extrinsic,
127 maybe_evm_domain_contract_creation_allowed_by_call_extrinsic,
128 } = fraud_proof_runtime_interface::construct_domain_inherent_extrinsic(
129 domain_runtime_code,
130 domain_inherent_extrinsic_data,
131 )
132 .ok_or(VerificationError::FailedToDeriveDomainInherentExtrinsic)?;
133
134 let bad_receipt_valid_bundle_digests = bad_receipt.valid_bundle_digests();
135 if valid_bundle_digests.len() != bad_receipt_valid_bundle_digests.len() {
136 return Err(VerificationError::InvalidBundleDigest);
137 }
138
139 let mut bundle_extrinsics_digests = Vec::new();
140 for (bad_receipt_valid_bundle_digest, bundle_digest) in bad_receipt_valid_bundle_digests
141 .into_iter()
142 .zip(valid_bundle_digests)
143 {
144 let bundle_digest_hash =
145 HeaderHashingFor::<DomainHeader>::hash_of(&bundle_digest.bundle_digest);
146 if bundle_digest_hash != bad_receipt_valid_bundle_digest {
147 return Err(VerificationError::InvalidBundleDigest);
148 }
149
150 bundle_extrinsics_digests.extend(bundle_digest.bundle_digest.clone());
151 }
152
153 let mut ordered_extrinsics =
154 deduplicate_and_shuffle_extrinsics(bundle_extrinsics_digests, shuffling_seed);
155
156 if let Some(domain_sudo_call_extrinsic) = maybe_domain_sudo_call_extrinsic {
171 let domain_sudo_call_extrinsic = ExtrinsicDigest::new::<
172 LayoutV1<HeaderHashingFor<DomainHeader>>,
173 >(domain_sudo_call_extrinsic);
174 ordered_extrinsics.push_front(domain_sudo_call_extrinsic);
175 }
176
177 let transaction_byte_fee_extrinsic = ExtrinsicDigest::new::<
178 LayoutV1<HeaderHashingFor<DomainHeader>>,
179 >(consensus_chain_byte_fee_extrinsic);
180 ordered_extrinsics.push_front(transaction_byte_fee_extrinsic);
181
182 if let Some(evm_domain_contract_creation_allowed_by_call_extrinsic) =
183 maybe_evm_domain_contract_creation_allowed_by_call_extrinsic
184 {
185 let evm_domain_contract_creation_allowed_by_call_extrinsic =
186 ExtrinsicDigest::new::<LayoutV1<HeaderHashingFor<DomainHeader>>>(
187 evm_domain_contract_creation_allowed_by_call_extrinsic,
188 );
189 ordered_extrinsics.push_front(evm_domain_contract_creation_allowed_by_call_extrinsic);
190 }
191
192 if let Some(domain_chain_allowlist_extrinsic) = maybe_domain_chain_allowlist_extrinsic {
193 let domain_chain_allowlist_extrinsic = ExtrinsicDigest::new::<
194 LayoutV1<HeaderHashingFor<DomainHeader>>,
195 >(domain_chain_allowlist_extrinsic);
196 ordered_extrinsics.push_front(domain_chain_allowlist_extrinsic);
197 }
198
199 if let Some(domain_set_code_extrinsic) = maybe_domain_set_code_extrinsic {
200 let domain_set_code_extrinsic = ExtrinsicDigest::new::<
201 LayoutV1<HeaderHashingFor<DomainHeader>>,
202 >(domain_set_code_extrinsic);
203 ordered_extrinsics.push_front(domain_set_code_extrinsic);
204 }
205
206 let timestamp_extrinsic = ExtrinsicDigest::new::<LayoutV1<HeaderHashingFor<DomainHeader>>>(
207 domain_timestamp_extrinsic,
208 );
209 ordered_extrinsics.push_front(timestamp_extrinsic);
210
211 let ordered_trie_node_values = ordered_extrinsics
212 .iter()
213 .map(|ext_digest| match ext_digest {
214 ExtrinsicDigest::Data(data) => Value::Inline(data),
215 ExtrinsicDigest::Hash(hash) => Value::Node(hash.0.as_slice()),
216 })
217 .collect();
218
219 let extrinsics_root = valued_ordered_trie_root::<LayoutV1<HeaderHashingFor<DomainHeader>>>(
220 ordered_trie_node_values,
221 );
222 if *bad_receipt.domain_block_extrinsics_root() == extrinsics_root {
223 return Err(VerificationError::InvalidProof);
224 }
225
226 Ok(())
227}
228
229pub fn verify_valid_bundle_fraud_proof<CBlock, DomainHeader, Balance, SKP>(
231 bad_receipt: ExecutionReceipt<
232 NumberFor<CBlock>,
233 CBlock::Hash,
234 HeaderNumberFor<DomainHeader>,
235 HeaderHashFor<DomainHeader>,
236 Balance,
237 >,
238 fraud_proof: &ValidBundleProof<NumberFor<CBlock>, CBlock::Hash, DomainHeader>,
239 domain_id: DomainId,
240 state_root: CBlock::Hash,
241 domain_runtime_code: Vec<u8>,
242) -> Result<(), VerificationError<DomainHeader::Hash>>
243where
244 CBlock: BlockT,
245 CBlock::Hash: Into<H256>,
246 DomainHeader: HeaderT,
247 DomainHeader::Hash: Into<H256> + PartialEq + Copy,
248 SKP: FraudProofStorageKeyProvider<NumberFor<CBlock>>,
249 Balance: Encode + Zero + Default,
250{
251 let ValidBundleProof { bundle_with_proof } = fraud_proof;
252 bundle_with_proof.verify::<CBlock, SKP>(domain_id, &state_root)?;
253 let (extrinsics, bundle_index) = (
254 bundle_with_proof.bundle.extrinsics().to_vec(),
255 bundle_with_proof.bundle_index,
256 );
257
258 let valid_bundle_digest =
259 fraud_proof_runtime_interface::derive_bundle_digest(domain_runtime_code, extrinsics)
260 .ok_or(VerificationError::FailedToDeriveBundleDigest)?;
261
262 let bad_valid_bundle_digest = bad_receipt
263 .valid_bundle_digest_at(bundle_index as usize)
264 .ok_or(VerificationError::TargetValidBundleNotFound)?;
265
266 if bad_valid_bundle_digest.into() == valid_bundle_digest {
267 Err(VerificationError::InvalidProof)
268 } else {
269 Ok(())
270 }
271}
272
273pub fn verify_invalid_state_transition_fraud_proof<CBlock, DomainHeader, Balance>(
275 bad_receipt: ExecutionReceipt<
276 NumberFor<CBlock>,
277 CBlock::Hash,
278 DomainHeader::Number,
279 DomainHeader::Hash,
280 Balance,
281 >,
282 bad_receipt_parent: ExecutionReceipt<
283 NumberFor<CBlock>,
284 CBlock::Hash,
285 DomainHeader::Number,
286 DomainHeader::Hash,
287 Balance,
288 >,
289 fraud_proof: &InvalidStateTransitionProof,
290 domain_runtime_code: Vec<u8>,
291) -> Result<(), VerificationError<DomainHeader::Hash>>
292where
293 CBlock: BlockT,
294 CBlock::Hash: Into<H256>,
295 DomainHeader: HeaderT,
296 DomainHeader::Hash: Into<H256> + From<H256>,
297 DomainHeader::Number: UniqueSaturatedInto<BlockNumber> + From<BlockNumber>,
298 Balance: Encode + Zero + Default,
299{
300 let InvalidStateTransitionProof {
301 execution_proof,
302 execution_phase,
303 ..
304 } = fraud_proof;
305
306 let (pre_state_root, post_state_root) = execution_phase
307 .pre_post_state_root::<CBlock, DomainHeader, Balance>(&bad_receipt, &bad_receipt_parent)?;
308
309 let call_data = execution_phase
310 .call_data::<CBlock, DomainHeader, Balance>(&bad_receipt, &bad_receipt_parent)?;
311
312 let execution_result = fraud_proof_runtime_interface::execution_proof_check(
313 (
314 (*bad_receipt_parent.domain_block_number()).saturated_into(),
315 (*bad_receipt_parent.domain_block_hash()).into(),
316 ),
317 pre_state_root,
318 execution_proof.encode(),
319 execution_phase.execution_method(),
320 call_data.as_ref(),
321 domain_runtime_code,
322 )
323 .ok_or(VerificationError::BadExecutionProof)?;
324
325 let valid_post_state_root = execution_phase
326 .decode_execution_result::<DomainHeader>(execution_result)?
327 .into();
328
329 let is_mismatch = valid_post_state_root != post_state_root;
330
331 let is_valid = is_mismatch == execution_phase.is_state_root_mismatch();
335
336 if is_valid {
337 Ok(())
338 } else {
339 Err(VerificationError::InvalidProof)
340 }
341}
342
343pub fn verify_invalid_domain_block_hash_fraud_proof<CBlock, Balance, DomainHeader>(
345 bad_receipt: ExecutionReceipt<
346 NumberFor<CBlock>,
347 CBlock::Hash,
348 DomainHeader::Number,
349 DomainHeader::Hash,
350 Balance,
351 >,
352 digest_storage_proof: StorageProof,
353 parent_domain_block_hash: DomainHeader::Hash,
354) -> Result<(), VerificationError<DomainHeader::Hash>>
355where
356 CBlock: BlockT,
357 Balance: PartialEq + Decode + Encode + Zero + Default,
358 DomainHeader: HeaderT,
359{
360 let state_root = *bad_receipt.final_state_root();
361 let digest_storage_key = StorageKey(sp_domains::system_digest_final_key());
362
363 let digest = StorageProofVerifier::<DomainHeader::Hashing>::get_decoded_value::<Digest>(
364 &state_root,
365 digest_storage_proof,
366 digest_storage_key,
367 )
368 .map_err(|err| {
369 VerificationError::StorageProof(storage_proof::VerificationError::DigestStorageProof(err))
370 })?;
371
372 let derived_domain_block_hash = sp_domains::derive_domain_block_hash::<DomainHeader>(
373 *bad_receipt.domain_block_number(),
374 *bad_receipt.domain_block_extrinsics_root(),
375 state_root,
376 parent_domain_block_hash,
377 digest,
378 );
379
380 if *bad_receipt.domain_block_hash() == derived_domain_block_hash {
381 return Err(VerificationError::InvalidProof);
382 }
383
384 Ok(())
385}
386
387pub fn verify_invalid_block_fees_fraud_proof<
389 CBlock,
390 DomainNumber,
391 DomainHash,
392 Balance,
393 DomainHashing,
394>(
395 bad_receipt: ExecutionReceipt<
396 NumberFor<CBlock>,
397 CBlock::Hash,
398 DomainNumber,
399 DomainHash,
400 Balance,
401 >,
402 storage_proof: &StorageProof,
403 domain_runtime_code: Vec<u8>,
404) -> Result<(), VerificationError<DomainHash>>
405where
406 CBlock: BlockT,
407 Balance: PartialEq + Decode + Encode + Zero + Default,
408 DomainHashing: Hasher<Out = DomainHash>,
409 DomainNumber: Encode + Zero,
410 DomainHash: Clone + Encode + Default + Copy,
411{
412 let storage_key = fraud_proof_runtime_interface::domain_storage_key(
413 domain_runtime_code,
414 DomainStorageKeyRequest::BlockFees,
415 )
416 .ok_or(VerificationError::FailedToGetDomainStorageKey)?;
417
418 let block_fees =
419 StorageProofVerifier::<DomainHashing>::get_decoded_value::<BlockFees<Balance>>(
420 bad_receipt.final_state_root(),
421 storage_proof.clone(),
422 StorageKey(storage_key),
423 )
424 .map_err(|err| {
425 VerificationError::StorageProof(
426 storage_proof::VerificationError::BlockFeesStorageProof(err),
427 )
428 })?;
429
430 if bad_receipt.block_fees() == &block_fees {
432 return Err(VerificationError::InvalidProof);
433 }
434
435 Ok(())
436}
437
438pub fn verify_invalid_transfers_fraud_proof<
440 CBlock,
441 DomainNumber,
442 DomainHash,
443 Balance,
444 DomainHashing,
445>(
446 bad_receipt: ExecutionReceipt<
447 NumberFor<CBlock>,
448 CBlock::Hash,
449 DomainNumber,
450 DomainHash,
451 Balance,
452 >,
453 storage_proof: &StorageProof,
454 domain_runtime_code: Vec<u8>,
455) -> Result<(), VerificationError<DomainHash>>
456where
457 CBlock: BlockT,
458 Balance: PartialEq + Decode + Encode + Zero + Default,
459 DomainNumber: Encode + Zero,
460 DomainHash: Clone + Encode + Default + Copy,
461 DomainHashing: Hasher<Out = DomainHash>,
462{
463 let storage_key = fraud_proof_runtime_interface::domain_storage_key(
464 domain_runtime_code,
465 DomainStorageKeyRequest::Transfers,
466 )
467 .ok_or(VerificationError::FailedToGetDomainStorageKey)?;
468
469 let transfers = StorageProofVerifier::<DomainHashing>::get_decoded_value::<Transfers<Balance>>(
470 bad_receipt.final_state_root(),
471 storage_proof.clone(),
472 StorageKey(storage_key),
473 )
474 .map_err(|err| {
475 VerificationError::StorageProof(storage_proof::VerificationError::TransfersStorageProof(
476 err,
477 ))
478 })?;
479
480 if bad_receipt.transfers() == &transfers {
482 return Err(VerificationError::InvalidProof);
483 }
484
485 Ok(())
486}
487
488fn check_expected_bundle_entry<CBlock, DomainHeader, Balance>(
492 bad_receipt: &ExecutionReceipt<
493 NumberFor<CBlock>,
494 CBlock::Hash,
495 HeaderNumberFor<DomainHeader>,
496 HeaderHashFor<DomainHeader>,
497 Balance,
498 >,
499 bundle_index: u32,
500 invalid_bundle_type: InvalidBundleType,
501 is_good_invalid_fraud_proof: bool,
502) -> Result<InboxedBundle<HeaderHashFor<DomainHeader>>, VerificationError<DomainHeader::Hash>>
503where
504 CBlock: BlockT,
505 DomainHeader: HeaderT,
506 Balance: Encode + Zero + Default,
507{
508 let targeted_invalid_bundle_entry = bad_receipt
509 .inboxed_bundles()
510 .get(bundle_index as usize)
511 .ok_or(VerificationError::BundleNotFound)?;
512
513 let is_expected = if !is_good_invalid_fraud_proof {
514 targeted_invalid_bundle_entry.bundle == BundleValidity::Invalid(invalid_bundle_type.clone())
518 } else {
519 match &targeted_invalid_bundle_entry.bundle {
521 BundleValidity::Valid(_) => true,
524 BundleValidity::Invalid(invalid_type) => {
525 invalid_bundle_type.checking_order() < invalid_type.checking_order()
528 }
529 }
530 };
531
532 if !is_expected {
533 return Err(VerificationError::UnexpectedTargetedBundleEntry {
534 bundle_index,
535 fraud_proof_invalid_type_of_proof: invalid_bundle_type,
536 targeted_entry_bundle: targeted_invalid_bundle_entry.bundle.clone(),
537 });
538 }
539
540 Ok(targeted_invalid_bundle_entry.clone())
541}
542
543fn get_extrinsic_from_proof<DomainHeader: HeaderT>(
544 extrinsic_index: u32,
545 extrinsics_root: <HeaderHashingFor<DomainHeader> as Hasher>::Out,
546 proof_data: StorageProof,
547) -> Result<OpaqueExtrinsic, VerificationError<DomainHeader::Hash>> {
548 let storage_key =
549 StorageProofVerifier::<HeaderHashingFor<DomainHeader>>::enumerated_storage_key(
550 extrinsic_index,
551 );
552 StorageProofVerifier::<HeaderHashingFor<DomainHeader>>::get_decoded_value(
553 &extrinsics_root,
554 proof_data,
555 storage_key,
556 )
557 .map_err(|err| {
558 VerificationError::StorageProof(storage_proof::VerificationError::ExtrinsicStorageProof(
559 err,
560 ))
561 })
562}
563
564pub fn verify_invalid_bundles_fraud_proof<CBlock, DomainHeader, MmrHash, Balance, SKP, MPV>(
565 bad_receipt: ExecutionReceipt<
566 NumberFor<CBlock>,
567 CBlock::Hash,
568 HeaderNumberFor<DomainHeader>,
569 HeaderHashFor<DomainHeader>,
570 Balance,
571 >,
572 bad_receipt_parent: ExecutionReceipt<
573 NumberFor<CBlock>,
574 CBlock::Hash,
575 HeaderNumberFor<DomainHeader>,
576 HeaderHashFor<DomainHeader>,
577 Balance,
578 >,
579 invalid_bundles_fraud_proof: &InvalidBundlesProof<
580 NumberFor<CBlock>,
581 CBlock::Hash,
582 MmrHash,
583 DomainHeader,
584 >,
585 domain_id: DomainId,
586 state_root: CBlock::Hash,
587 domain_runtime_code: Vec<u8>,
588) -> Result<(), VerificationError<DomainHeader::Hash>>
589where
590 CBlock: BlockT,
591 DomainHeader: HeaderT,
592 CBlock::Hash: Into<H256>,
593 DomainHeader::Hash: Into<H256>,
594 MmrHash: Decode + Clone,
595 SKP: FraudProofStorageKeyProvider<NumberFor<CBlock>>,
596 MPV: MmrProofVerifier<MmrHash, NumberFor<CBlock>, CBlock::Hash>,
597 Balance: Encode + Zero + Default,
598{
599 let InvalidBundlesProof {
600 bundle_index,
601 invalid_bundle_type,
602 is_good_invalid_fraud_proof,
603 proof_data,
604 } = invalid_bundles_fraud_proof;
605
606 let (bundle_index, is_good_invalid_fraud_proof) = (*bundle_index, *is_good_invalid_fraud_proof);
607 let targeted_invalid_bundle_entry = check_expected_bundle_entry::<CBlock, DomainHeader, Balance>(
608 &bad_receipt,
609 bundle_index,
610 invalid_bundle_type.clone(),
611 is_good_invalid_fraud_proof,
612 )?;
613 let bundle_extrinsic_root = targeted_invalid_bundle_entry.extrinsics_root;
614
615 match proof_data {
617 InvalidBundlesProofData::Bundle(bundle_with_proof)
618 | InvalidBundlesProofData::BundleAndExecution {
619 bundle_with_proof, ..
620 } => {
621 if bundle_with_proof.bundle_index != bundle_index {
622 return Err(VerificationError::UnexpectedInvalidBundleProofData);
623 }
624 bundle_with_proof.verify::<CBlock, SKP>(domain_id, &state_root)?;
625 }
626 InvalidBundlesProofData::Extrinsic(_) => {}
627 InvalidBundlesProofData::InvalidXDMProofData { .. } => {}
628 }
629
630 if let Some(invalid_extrinsic_index) = targeted_invalid_bundle_entry.invalid_extrinsic_index()
633 && let InvalidBundlesProofData::Bundle(bundle_with_proof) = proof_data
634 && bundle_with_proof.bundle.body_length() as u32 <= invalid_extrinsic_index
635 {
636 return Ok(());
637 }
638
639 match &invalid_bundle_type {
640 InvalidBundleType::OutOfRangeTx(extrinsic_index) => {
641 let bundle = match proof_data {
642 InvalidBundlesProofData::Bundle(bundle_with_proof) => {
643 bundle_with_proof.bundle.clone()
644 }
645 _ => return Err(VerificationError::UnexpectedInvalidBundleProofData),
646 };
647
648 let opaque_extrinsic = bundle
649 .extrinsics()
650 .get(*extrinsic_index as usize)
651 .cloned()
652 .ok_or(VerificationError::ExtrinsicNotFound)?;
653
654 let domain_tx_range = U256::MAX / INITIAL_DOMAIN_TX_RANGE;
655 let bundle_vrf_hash = U256::from_be_bytes(*bundle.proof_of_election().vrf_hash());
656
657 let is_tx_in_range = fraud_proof_runtime_interface::domain_runtime_call(
658 domain_runtime_code,
659 StatelessDomainRuntimeCall::IsTxInRange {
660 opaque_extrinsic,
661 domain_tx_range,
662 bundle_vrf_hash,
663 },
664 )
665 .ok_or(VerificationError::FailedToGetDomainRuntimeCallResponse)?;
666
667 if is_tx_in_range == is_good_invalid_fraud_proof {
671 return Err(VerificationError::InvalidProof);
672 }
673 Ok(())
674 }
675 InvalidBundleType::InherentExtrinsic(extrinsic_index) => {
676 let opaque_extrinsic = {
677 let extrinsic_storage_proof = match proof_data {
678 InvalidBundlesProofData::Extrinsic(p) => p.clone(),
679 _ => return Err(VerificationError::UnexpectedInvalidBundleProofData),
680 };
681 get_extrinsic_from_proof::<DomainHeader>(
682 *extrinsic_index,
683 bundle_extrinsic_root,
684 extrinsic_storage_proof,
685 )?
686 };
687 let is_inherent = fraud_proof_runtime_interface::domain_runtime_call(
688 domain_runtime_code,
689 StatelessDomainRuntimeCall::IsInherentExtrinsic(opaque_extrinsic),
690 )
691 .ok_or(VerificationError::FailedToGetDomainRuntimeCallResponse)?;
692
693 if is_inherent == is_good_invalid_fraud_proof {
697 Ok(())
698 } else {
699 Err(VerificationError::InvalidProof)
700 }
701 }
702 InvalidBundleType::IllegalTx(extrinsic_index) => {
703 let (bundle, execution_proof) = match proof_data {
704 InvalidBundlesProofData::BundleAndExecution {
705 bundle_with_proof,
706 execution_proof,
707 } => (bundle_with_proof.bundle.clone(), execution_proof.clone()),
708 _ => return Err(VerificationError::UnexpectedInvalidBundleProofData),
709 };
710
711 let extrinsics = bundle
712 .into_extrinsics()
713 .drain(..)
714 .take((*extrinsic_index + 1) as usize)
715 .collect();
716
717 let check_extrinsic_result =
719 fraud_proof_runtime_interface::check_extrinsics_in_single_context(
720 domain_runtime_code,
721 (
722 (*bad_receipt_parent.domain_block_number()).saturated_into(),
723 (*bad_receipt_parent.domain_block_hash()).into(),
724 ),
725 (*bad_receipt_parent.final_state_root()).into(),
726 extrinsics,
727 execution_proof.encode(),
728 )
729 .ok_or(VerificationError::FailedToCheckExtrinsicsInSingleContext)?;
730
731 let is_extrinsic_invalid = check_extrinsic_result == Some(*extrinsic_index);
732
733 if is_extrinsic_invalid == is_good_invalid_fraud_proof {
737 Ok(())
738 } else {
739 Err(VerificationError::InvalidProof)
740 }
741 }
742 InvalidBundleType::UndecodableTx(extrinsic_index) => {
743 let opaque_extrinsic = {
744 let extrinsic_storage_proof = match proof_data {
745 InvalidBundlesProofData::Extrinsic(p) => p.clone(),
746 _ => return Err(VerificationError::UnexpectedInvalidBundleProofData),
747 };
748 get_extrinsic_from_proof::<DomainHeader>(
749 *extrinsic_index,
750 bundle_extrinsic_root,
751 extrinsic_storage_proof,
752 )?
753 };
754 let is_decodable = fraud_proof_runtime_interface::domain_runtime_call(
755 domain_runtime_code,
756 StatelessDomainRuntimeCall::IsDecodableExtrinsic(opaque_extrinsic),
757 )
758 .ok_or(VerificationError::FailedToGetDomainRuntimeCallResponse)?;
759
760 if is_decodable == is_good_invalid_fraud_proof {
761 return Err(VerificationError::InvalidProof);
762 }
763 Ok(())
764 }
765 InvalidBundleType::InvalidBundleWeight => {
766 let bundle = match proof_data {
767 InvalidBundlesProofData::Bundle(bundle_with_proof) => {
768 bundle_with_proof.bundle.clone()
769 }
770 _ => return Err(VerificationError::UnexpectedInvalidBundleProofData),
771 };
772 let bundle_header_weight = bundle.estimated_weight();
773 let estimated_bundle_weight = fraud_proof_runtime_interface::bundle_weight(
774 domain_runtime_code,
775 bundle.into_extrinsics(),
776 )
777 .ok_or(VerificationError::FailedToGetBundleWeight)?;
778
779 let is_bundle_weight_correct = estimated_bundle_weight == bundle_header_weight;
780
781 if is_bundle_weight_correct == is_good_invalid_fraud_proof {
782 return Err(VerificationError::InvalidProof);
783 }
784 Ok(())
785 }
786 InvalidBundleType::InvalidXDM(extrinsic_index) => {
787 let (extrinsic_proof, maybe_mmr_root_proof) = match proof_data {
788 InvalidBundlesProofData::InvalidXDMProofData {
789 extrinsic_proof,
790 mmr_root_proof,
791 } => (extrinsic_proof.clone(), mmr_root_proof.clone()),
792 _ => return Err(VerificationError::UnexpectedInvalidBundleProofData),
793 };
794
795 let opaque_extrinsic = get_extrinsic_from_proof::<DomainHeader>(
796 *extrinsic_index,
797 bundle_extrinsic_root,
798 extrinsic_proof,
799 )?;
800
801 let maybe_xdm_mmr_proof = fraud_proof_runtime_interface::extract_xdm_mmr_proof(
802 domain_runtime_code,
803 opaque_extrinsic.encode(),
804 )
805 .ok_or(VerificationError::FailedToGetExtractXdmMmrProof)?;
806
807 let (mmr_root_proof, consensus_chain_mmr_leaf_proof) = match maybe_xdm_mmr_proof {
808 Some(encoded_xdm_mmr_proof) => {
809 let consensus_chain_mmr_leaf_proof: ConsensusChainMmrLeafProof<
810 NumberFor<CBlock>,
811 CBlock::Hash,
812 MmrHash,
813 > = Decode::decode(&mut encoded_xdm_mmr_proof.as_ref())
814 .map_err(|_| VerificationError::FailedToDecodeXdmMmrProof)?;
815 let mmr_root_proof = maybe_mmr_root_proof
816 .ok_or(VerificationError::UnexpectedInvalidBundleProofData)?;
817 (mmr_root_proof, consensus_chain_mmr_leaf_proof)
818 }
819 None => {
820 return if is_good_invalid_fraud_proof || maybe_mmr_root_proof.is_some() {
824 Err(VerificationError::InvalidProof)
825 } else {
826 Ok(())
827 };
828 }
829 };
830
831 let mmr_root = {
832 let MmrRootProof {
833 mmr_proof,
834 mmr_root_storage_proof,
835 } = mmr_root_proof;
836
837 let leaf_data = MPV::verify_proof_and_extract_leaf(mmr_proof)
838 .ok_or(VerificationError::BadMmrProof)?;
839
840 <MmrRootStorageProof<MmrHash> as BasicStorageProof<CBlock>>::verify::<SKP>(
841 mmr_root_storage_proof,
842 consensus_chain_mmr_leaf_proof.consensus_block_number,
843 &leaf_data.state_root(),
844 )?
845 };
846
847 let is_valid_mmr_proof =
849 MPV::verify_proof_stateless(mmr_root, consensus_chain_mmr_leaf_proof).is_some();
850
851 if is_valid_mmr_proof == is_good_invalid_fraud_proof {
852 return Err(VerificationError::InvalidProof);
853 }
854 Ok(())
855 }
856 }
857}